abi-compliance-checker.pl revision ba8922775121372b6e35f925f3938282ef03019e
1#!/usr/bin/perl
2###########################################################################
3# ABI Compliance Checker (ABICC) 1.99.17
4# A tool for checking backward compatibility of a C/C++ library API
5#
6# Copyright (C) 2009-2011 Institute for System Programming, RAS
7# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies)
8# Copyright (C) 2011-2012 ROSA Laboratory
9# Copyright (C) 2012-2016 Andrey Ponomarenko's ABI Laboratory
10#
11# Written by Andrey Ponomarenko
12#
13# PLATFORMS
14# =========
15#  Linux, FreeBSD, Mac OS X, Haiku, MS Windows, Symbian
16#
17# REQUIREMENTS
18# ============
19#  Linux
20#    - G++ (3.0-4.7, 4.8.3, 4.9 or newer)
21#    - GNU Binutils (readelf, c++filt, objdump)
22#    - Perl 5 (5.8 or newer)
23#    - Ctags (5.8 or newer)
24#    - ABI Dumper (0.99.15 or newer)
25#
26#  Mac OS X
27#    - Xcode (g++, c++filt, otool, nm)
28#    - Ctags (5.8 or newer)
29#
30#  MS Windows
31#    - MinGW (3.0-4.7, 4.8.3, 4.9 or newer)
32#    - MS Visual C++ (dumpbin, undname, cl)
33#    - Active Perl 5 (5.8 or newer)
34#    - Sigcheck v1.71 or newer
35#    - Info-ZIP 3.0 (zip, unzip)
36#    - Ctags (5.8 or newer)
37#    - Add tool locations to the PATH environment variable
38#    - Run vsvars32.bat (C:\Microsoft Visual Studio 9.0\Common7\Tools\)
39#
40# This program is free software: you can redistribute it and/or modify
41# it under the terms of the GNU General Public License or the GNU Lesser
42# General Public License as published by the Free Software Foundation.
43#
44# This program is distributed in the hope that it will be useful,
45# but WITHOUT ANY WARRANTY; without even the implied warranty of
46# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47# GNU General Public License for more details.
48#
49# You should have received a copy of the GNU General Public License
50# and the GNU Lesser General Public License along with this program.
51# If not, see <http://www.gnu.org/licenses/>.
52###########################################################################
53use Getopt::Long;
54Getopt::Long::Configure ("posix_default", "no_ignore_case");
55use File::Path qw(mkpath rmtree);
56use File::Temp qw(tempdir);
57use File::Copy qw(copy move);
58use Cwd qw(abs_path cwd realpath);
59use Storable qw(dclone);
60use Data::Dumper;
61use Config;
62
63my $TOOL_VERSION = "1.99.17";
64my $ABI_DUMP_VERSION = "3.2";
65my $XML_REPORT_VERSION = "1.2";
66my $XML_ABI_DUMP_VERSION = "1.2";
67my $OSgroup = get_OSgroup();
68my $ORIG_DIR = cwd();
69my $TMP_DIR = tempdir(CLEANUP=>1);
70my $LOCALE = "C.UTF-8";
71
72# Internal modules
73my $MODULES_DIR = get_Modules();
74push(@INC, get_dirname($MODULES_DIR));
75# Rules DB
76my %RULES_PATH = (
77    "Binary" => $MODULES_DIR."/RulesBin.xml",
78    "Source" => $MODULES_DIR."/RulesSrc.xml");
79
80my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName,
81$TestTool, $DumpAPI, $SymbolsListPath, $CheckHeadersOnly_Opt, $UseDumps,
82$AppPath, $StrictCompat, $DumpVersion, $ParamNamesPath,
83%RelativeDirectory, $TargetTitle, $TestDump, $LoggingPath,
84%TargetVersion, $InfoMsg, $CrossGcc, %OutputLogPath,
85$OutputReportPath, $OutputDumpPath, $ShowRetVal, $SystemRoot_Opt, $DumpSystem,
86$CmpSystems, $TargetLibsPath, $Debug, $CrossPrefix, $UseStaticLibs, $NoStdInc,
87$TargetComponent_Opt, $TargetSysInfo, $TargetHeader, $ExtendedCheck, $Quiet,
88$SkipHeadersPath, $CppCompat, $LogMode, $StdOut, $ListAffected, $ReportFormat,
89$UserLang, $TargetHeadersPath, $BinaryOnly, $SourceOnly, $BinaryReportPath,
90$SourceReportPath, $UseXML, $SortDump, $DumpFormat,
91$ExtraInfo, $ExtraDump, $Force, $Tolerance, $Tolerant, $SkipSymbolsListPath,
92$CheckInfo, $Quick, $AffectLimit, $AllAffected, $CppIncompat,
93$SkipInternalSymbols, $SkipInternalTypes, $TargetArch, $GccOptions,
94$TypesListPath, $SkipTypesListPath, $CheckPrivateABI);
95
96my $CmdName = get_filename($0);
97my %OS_LibExt = (
98    "dynamic" => {
99        "linux"=>"so",
100        "macos"=>"dylib",
101        "windows"=>"dll",
102        "symbian"=>"dso",
103        "default"=>"so"
104    },
105    "static" => {
106        "linux"=>"a",
107        "windows"=>"lib",
108        "symbian"=>"lib",
109        "default"=>"a"
110    }
111);
112
113my %OS_Archive = (
114    "windows"=>"zip",
115    "default"=>"tar.gz"
116);
117
118my %ERROR_CODE = (
119    # Compatible verdict
120    "Compatible"=>0,
121    "Success"=>0,
122    # Incompatible verdict
123    "Incompatible"=>1,
124    # Undifferentiated error code
125    "Error"=>2,
126    # System command is not found
127    "Not_Found"=>3,
128    # Cannot access input files
129    "Access_Error"=>4,
130    # Cannot compile header files
131    "Cannot_Compile"=>5,
132    # Header compiled with errors
133    "Compile_Error"=>6,
134    # Invalid input ABI dump
135    "Invalid_Dump"=>7,
136    # Incompatible version of ABI dump
137    "Dump_Version"=>8,
138    # Cannot find a module
139    "Module_Error"=>9,
140    # Empty intersection between
141    # headers and shared objects
142    "Empty_Intersection"=>10,
143    # Empty set of symbols in headers
144    "Empty_Set"=>11
145);
146
147my $HomePage = "http://lvc.github.io/abi-compliance-checker/";
148
149my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION
150A tool for checking backward compatibility of a C/C++ library API
151Copyright (C) 2015 Andrey Ponomarenko's ABI Laboratory
152License: GNU LGPL or GNU GPL
153
154Usage: $CmdName [options]
155Example: $CmdName -lib NAME -old OLD.xml -new NEW.xml
156
157OLD.xml and NEW.xml are XML-descriptors:
158
159    <version>
160        1.0
161    </version>
162
163    <headers>
164        /path/to/headers/
165    </headers>
166
167    <libs>
168        /path/to/libraries/
169    </libs>
170
171More info: $CmdName --help\n";
172
173if($#ARGV==-1)
174{
175    printMsg("INFO", $ShortUsage);
176    exit(0);
177}
178
179GetOptions("h|help!" => \$Help,
180  "i|info!" => \$InfoMsg,
181  "v|version!" => \$ShowVersion,
182  "dumpversion!" => \$DumpVersion,
183# general options
184  "l|lib|library=s" => \$TargetLibraryName,
185  "d1|old|o=s" => \$Descriptor{1}{"Path"},
186  "d2|new|n=s" => \$Descriptor{2}{"Path"},
187  "dump|dump-abi|dump_abi=s" => \$DumpAPI,
188# extra options
189  "app|application=s" => \$AppPath,
190  "static-libs!" => \$UseStaticLibs,
191  "gcc-path|cross-gcc=s" => \$CrossGcc,
192  "gcc-prefix|cross-prefix=s" => \$CrossPrefix,
193  "gcc-options=s" => \$GccOptions,
194  "sysroot=s" => \$SystemRoot_Opt,
195  "v1|vnum1|version1|vnum=s" => \$TargetVersion{1},
196  "v2|vnum2|version2=s" => \$TargetVersion{2},
197  "s|strict!" => \$StrictCompat,
198  "symbols-list=s" => \$SymbolsListPath,
199  "types-list=s" => \$TypesListPath,
200  "skip-symbols=s" => \$SkipSymbolsListPath,
201  "skip-types=s" => \$SkipTypesListPath,
202  "headers-list=s" => \$TargetHeadersPath,
203  "skip-headers=s" => \$SkipHeadersPath,
204  "header=s" => \$TargetHeader,
205  "headers-only|headers_only!" => \$CheckHeadersOnly_Opt,
206  "show-retval!" => \$ShowRetVal,
207  "use-dumps!" => \$UseDumps,
208  "nostdinc!" => \$NoStdInc,
209  "dump-system=s" => \$DumpSystem,
210  "sysinfo=s" => \$TargetSysInfo,
211  "cmp-systems!" => \$CmpSystems,
212  "libs-list=s" => \$TargetLibsPath,
213  "ext|extended!" => \$ExtendedCheck,
214  "q|quiet!" => \$Quiet,
215  "stdout!" => \$StdOut,
216  "report-format=s" => \$ReportFormat,
217  "dump-format=s" => \$DumpFormat,
218  "xml!" => \$UseXML,
219  "lang=s" => \$UserLang,
220  "arch=s" => \$TargetArch,
221  "binary|bin|abi!" => \$BinaryOnly,
222  "source|src|api!" => \$SourceOnly,
223  "limit-affected|affected-limit=s" => \$AffectLimit,
224# other options
225  "test!" => \$TestTool,
226  "test-dump!" => \$TestDump,
227  "debug!" => \$Debug,
228  "cpp-compatible!" => \$CppCompat,
229  "cpp-incompatible!" => \$CppIncompat,
230  "p|params=s" => \$ParamNamesPath,
231  "relpath1|relpath=s" => \$RelativeDirectory{1},
232  "relpath2=s" => \$RelativeDirectory{2},
233  "dump-path=s" => \$OutputDumpPath,
234  "sort!" => \$SortDump,
235  "report-path=s" => \$OutputReportPath,
236  "bin-report-path=s" => \$BinaryReportPath,
237  "src-report-path=s" => \$SourceReportPath,
238  "log-path=s" => \$LoggingPath,
239  "log1-path=s" => \$OutputLogPath{1},
240  "log2-path=s" => \$OutputLogPath{2},
241  "logging-mode=s" => \$LogMode,
242  "list-affected!" => \$ListAffected,
243  "title|l-full|lib-full=s" => \$TargetTitle,
244  "component=s" => \$TargetComponent_Opt,
245  "extra-info=s" => \$ExtraInfo,
246  "extra-dump!" => \$ExtraDump,
247  "force!" => \$Force,
248  "tolerance=s" => \$Tolerance,
249  "tolerant!" => \$Tolerant,
250  "check!" => \$CheckInfo,
251  "quick!" => \$Quick,
252  "all-affected!" => \$AllAffected,
253  "skip-internal-symbols|skip-internal=s" => \$SkipInternalSymbols,
254  "skip-internal-types=s" => \$SkipInternalTypes,
255  "check-private-abi!" => \$CheckPrivateABI
256) or ERR_MESSAGE();
257
258sub ERR_MESSAGE()
259{
260    printMsg("INFO", "\n".$ShortUsage);
261    exit($ERROR_CODE{"Error"});
262}
263
264my $LIB_TYPE = $UseStaticLibs?"static":"dynamic";
265my $SLIB_TYPE = $LIB_TYPE;
266if($OSgroup!~/macos|windows/ and $SLIB_TYPE eq "dynamic")
267{ # show as "shared" library
268    $SLIB_TYPE = "shared";
269}
270my $LIB_EXT = getLIB_EXT($OSgroup);
271my $AR_EXT = getAR_EXT($OSgroup);
272my $BYTE_SIZE = 8;
273my $COMMON_LOG_PATH = "logs/run.log";
274
275my $HelpMessage="
276NAME:
277  ABI Compliance Checker ($CmdName)
278  Check backward compatibility of a C/C++ library API
279
280DESCRIPTION:
281  ABI Compliance Checker (ABICC) is a tool for checking backward binary and
282  source-level compatibility of a $SLIB_TYPE C/C++ library. The tool checks
283  header files and $SLIB_TYPE libraries (*.$LIB_EXT) of old and new versions and
284  analyzes changes in API and ABI (ABI=API+compiler ABI) that may break binary
285  and/or source-level compatibility: changes in calling stack, v-table changes,
286  removed symbols, renamed fields, etc. Binary incompatibility may result in
287  crashing or incorrect behavior of applications built with an old version of
288  a library if they run on a new one. Source incompatibility may result in
289  recompilation errors with a new library version.
290
291  The tool is intended for developers of software libraries and maintainers
292  of operating systems who are interested in ensuring backward compatibility,
293  i.e. allow old applications to run or to be recompiled with newer library
294  versions.
295
296  Also the tool can be used by ISVs for checking applications portability to
297  new library versions. Found issues can be taken into account when adapting
298  the application to a new library version.
299
300  This tool is free software: you can redistribute it and/or modify it
301  under the terms of the GNU LGPL or GNU GPL.
302
303USAGE:
304  $CmdName [options]
305
306EXAMPLE:
307  $CmdName -lib NAME -old OLD.xml -new NEW.xml
308
309  OLD.xml and NEW.xml are XML-descriptors:
310
311    <version>
312        1.0
313    </version>
314
315    <headers>
316        /path1/to/header(s)/
317        /path2/to/header(s)/
318         ...
319    </headers>
320
321    <libs>
322        /path1/to/library(ies)/
323        /path2/to/library(ies)/
324         ...
325    </libs>
326
327INFORMATION OPTIONS:
328  -h|-help
329      Print this help.
330
331  -i|-info
332      Print complete info.
333
334  -v|-version
335      Print version information.
336
337  -dumpversion
338      Print the tool version ($TOOL_VERSION) and don't do anything else.
339
340GENERAL OPTIONS:
341  -l|-lib|-library NAME
342      Library name (without version).
343
344  -d1|-old|-o PATH
345      Descriptor of 1st (old) library version.
346      It may be one of the following:
347
348         1. XML-descriptor (VERSION.xml file):
349
350              <version>
351                  1.0
352              </version>
353
354              <headers>
355                  /path1/to/header(s)/
356                  /path2/to/header(s)/
357                   ...
358              </headers>
359
360              <libs>
361                  /path1/to/library(ies)/
362                  /path2/to/library(ies)/
363                   ...
364              </libs>
365
366                 ...
367
368         2. ABI dump generated by -dump option
369         3. Directory with headers and/or $SLIB_TYPE libraries
370         4. Single header file
371
372      If you are using an 2-4 descriptor types then you should
373      specify version numbers with -v1 and -v2 options too.
374
375      For more information, please see:
376        http://ispras.linuxbase.org/index.php/Library_Descriptor
377
378  -d2|-new|-n PATH
379      Descriptor of 2nd (new) library version.
380
381  -dump|-dump-abi PATH
382      Create library ABI dump for the input XML descriptor. You can
383      transfer it anywhere and pass instead of the descriptor. Also
384      it can be used for debugging the tool.
385
386      Supported versions of ABI dump: 2.0<=V<=$ABI_DUMP_VERSION\n";
387
388sub HELP_MESSAGE() {
389    printMsg("INFO", $HelpMessage."
390MORE INFO:
391     $CmdName --info\n");
392}
393
394sub INFO_MESSAGE()
395{
396    printMsg("INFO", "$HelpMessage
397EXTRA OPTIONS:
398  -app|-application PATH
399      This option allows to specify the application that should be checked
400      for portability to the new library version.
401
402  -static-libs
403      Check static libraries instead of the shared ones. The <libs> section
404      of the XML-descriptor should point to static libraries location.
405
406  -gcc-path PATH
407      Path to the cross GCC compiler to use instead of the usual (host) GCC.
408
409  -gcc-prefix PREFIX
410      GCC toolchain prefix.
411
412  -gcc-options OPTS
413      Additional compiler options.
414
415  -sysroot DIR
416      Specify the alternative root directory. The tool will search for include
417      paths in the DIR/usr/include and DIR/usr/lib directories.
418
419  -v1|-version1 NUM
420      Specify 1st library version outside the descriptor. This option is needed
421      if you have preferred an alternative descriptor type (see -d1 option).
422
423      In general case you should specify it in the XML-descriptor:
424          <version>
425              VERSION
426          </version>
427
428  -v2|-version2 NUM
429      Specify 2nd library version outside the descriptor.
430
431  -vnum NUM
432      Specify the library version in the generated ABI dump. The <version> section
433      of the input XML descriptor will be overwritten in this case.
434
435  -s|-strict
436      Treat all compatibility warnings as problems. Add a number of \"Low\"
437      severity problems to the return value of the tool.
438
439  -headers-only
440      Check header files without $SLIB_TYPE libraries. It is easy to run, but may
441      provide a low quality compatibility report with false positives and
442      without detecting of added/removed symbols.
443
444      Alternatively you can write \"none\" word to the <libs> section
445      in the XML-descriptor:
446          <libs>
447              none
448          </libs>
449
450  -show-retval
451      Show the symbol's return type in the report.
452
453  -symbols-list PATH
454      This option allows to specify a file with a list of symbols (mangled
455      names in C++) that should be checked. Other symbols will not be checked.
456
457  -types-list PATH
458      This option allows to specify a file with a list of types that should
459      be checked. Other types will not be checked.
460
461  -skip-symbols PATH
462      The list of symbols that should not be checked.
463
464  -skip-types PATH
465      The list of types that should not be checked.
466
467  -headers-list PATH
468      The file with a list of headers, that should be checked/dumped.
469
470  -skip-headers PATH
471      The file with the list of header files, that should not be checked.
472
473  -header NAME
474      Check/Dump ABI of this header only.
475
476  -use-dumps
477      Make dumps for two versions of a library and compare dumps. This should
478      increase the performance of the tool and decrease the system memory usage.
479
480  -nostdinc
481      Do not search in GCC standard system directories for header files.
482
483  -dump-system NAME -sysroot DIR
484      Find all the shared libraries and header files in DIR directory,
485      create XML descriptors and make ABI dumps for each library. The result
486      set of ABI dumps can be compared (--cmp-systems) with the other one
487      created for other version of operating system in order to check them for
488      compatibility. Do not forget to specify -cross-gcc option if your target
489      system requires some specific version of GCC compiler (different from
490      the host GCC). The system ABI dump will be generated to:
491          sys_dumps/NAME/ARCH
492
493  -dump-system DESCRIPTOR.xml
494      The same as the previous option but takes an XML descriptor of the target
495      system as input, where you should describe it:
496
497          /* Primary sections */
498
499          <name>
500              /* Name of the system */
501          </name>
502
503          <headers>
504              /* The list of paths to header files and/or
505                 directories with header files, one per line */
506          </headers>
507
508          <libs>
509              /* The list of paths to shared libraries and/or
510                 directories with shared libraries, one per line */
511          </libs>
512
513          /* Optional sections */
514
515          <search_headers>
516              /* List of directories to be searched
517                 for header files to automatically
518                 generate include paths, one per line */
519          </search_headers>
520
521          <search_libs>
522              /* List of directories to be searched
523                 for shared libraries to resolve
524                 dependencies, one per line */
525          </search_libs>
526
527          <tools>
528              /* List of directories with tools used
529                 for analysis (GCC toolchain), one per line */
530          </tools>
531
532          <cross_prefix>
533              /* GCC toolchain prefix.
534                 Examples:
535                     arm-linux-gnueabi
536                     arm-none-symbianelf */
537          </cross_prefix>
538
539          <gcc_options>
540              /* Additional GCC options, one per line */
541          </gcc_options>
542
543  -sysinfo DIR
544      This option should be used with -dump-system option to dump
545      ABI of operating systems and configure the dumping process.
546      You can find a sample in the package:
547          modules/Targets/{unix, symbian, windows}
548
549  -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH
550      Compare two system ABI dumps. Create compatibility reports for each
551      library and the common HTML report including the summary of test
552      results for all checked libraries. Report will be generated to:
553          sys_compat_reports/NAME1_to_NAME2/ARCH
554
555  -libs-list PATH
556      The file with a list of libraries, that should be dumped by
557      the -dump-system option or should be checked by the -cmp-systems option.
558
559  -ext|-extended
560      If your library A is supposed to be used by other library B and you
561      want to control the ABI of B, then you should enable this option. The
562      tool will check for changes in all data types, even if they are not
563      used by any function in the library A. Such data types are not part
564      of the A library ABI, but may be a part of the ABI of the B library.
565
566      The short scheme is:
567        app C (broken) -> lib B (broken ABI) -> lib A (stable ABI)
568
569  -q|-quiet
570      Print all messages to the file instead of stdout and stderr.
571      Default path (can be changed by -log-path option):
572          $COMMON_LOG_PATH
573
574  -stdout
575      Print analysis results (compatibility reports and ABI dumps) to stdout
576      instead of creating a file. This would allow piping data to other programs.
577
578  -report-format FMT
579      Change format of compatibility report.
580      Formats:
581        htm - HTML format (default)
582        xml - XML format
583
584  -dump-format FMT
585      Change format of ABI dump.
586      Formats:
587        perl - Data::Dumper format (default)
588        xml - XML format
589
590  -xml
591      Alias for: --report-format=xml or --dump-format=xml
592
593  -lang LANG
594      Set library language (C or C++). You can use this option if the tool
595      cannot auto-detect a language. This option may be useful for checking
596      C-library headers (--lang=C) in --headers-only or --extended modes.
597
598  -arch ARCH
599      Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390,
600      ect.). The option is useful if the tool cannot detect correct architecture
601      of the input objects.
602
603  -binary|-bin|-abi
604      Show \"Binary\" compatibility problems only.
605      Generate report to:
606        compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
607
608  -source|-src|-api
609      Show \"Source\" compatibility problems only.
610      Generate report to:
611        compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
612
613  -limit-affected LIMIT
614      The maximum number of affected symbols listed under the description
615      of the changed type in the report.
616
617OTHER OPTIONS:
618  -test
619      Run internal tests. Create two binary incompatible versions of a sample
620      library and run the tool to check them for compatibility. This option
621      allows to check if the tool works correctly in the current environment.
622
623  -test-dump
624      Test ability to create, read and compare ABI dumps.
625
626  -debug
627      Debugging mode. Print debug info on the screen. Save intermediate
628      analysis stages in the debug directory:
629          debug/LIB_NAME/VERSION/
630
631      Also consider using --dump option for debugging the tool.
632
633  -cpp-compatible
634      If your header files are written in C language and can be compiled
635      by the G++ compiler (i.e. don't use C++ keywords), then you can tell
636      the tool about this and speedup the analysis.
637
638  -cpp-incompatible
639      Set this option if input C header files use C++ keywords.
640
641  -p|-params PATH
642      Path to file with the function parameter names. It can be used
643      for improving report view if the library header files have no
644      parameter names. File format:
645
646            func1;param1;param2;param3 ...
647            func2;param1;param2;param3 ...
648             ...
649
650  -relpath PATH
651      Replace {RELPATH} macros to PATH in the XML-descriptor used
652      for dumping the library ABI (see -dump option).
653
654  -relpath1 PATH
655      Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1).
656
657  -relpath2 PATH
658      Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2).
659
660  -dump-path PATH
661      Specify a *.abi.$AR_EXT or *.abi file path where to generate an ABI dump.
662      Default:
663          abi_dumps/LIB_NAME/LIB_NAME_VERSION.abi.$AR_EXT
664
665  -sort
666      Enable sorting of data in ABI dumps.
667
668  -report-path PATH
669      Path to compatibility report.
670      Default:
671          compat_reports/LIB_NAME/V1_to_V2/compat_report.html
672
673  -bin-report-path PATH
674      Path to \"Binary\" compatibility report.
675      Default:
676          compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
677
678  -src-report-path PATH
679      Path to \"Source\" compatibility report.
680      Default:
681          compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
682
683  -log-path PATH
684      Log path for all messages.
685      Default:
686          logs/LIB_NAME/VERSION/log.txt
687
688  -log1-path PATH
689      Log path for 1st version of a library.
690      Default:
691          logs/LIB_NAME/V1/log.txt
692
693  -log2-path PATH
694      Log path for 2nd version of a library.
695      Default:
696          logs/LIB_NAME/V2/log.txt
697
698  -logging-mode MODE
699      Change logging mode.
700      Modes:
701        w - overwrite old logs (default)
702        a - append old logs
703        n - do not write any logs
704
705  -list-affected
706      Generate file with the list of incompatible
707      symbols beside the HTML compatibility report.
708      Use 'c++filt \@file' command from GNU binutils
709      to unmangle C++ symbols in the generated file.
710      Default names:
711          abi_affected.txt
712          src_affected.txt
713
714  -component NAME
715      The component name in the title and summary of the HTML report.
716      Default:
717          library
718
719  -title NAME
720      Change library name in the report title to NAME. By default
721      will be displayed a name specified by -l option.
722
723  -extra-info DIR
724      Dump extra info to DIR.
725
726  -extra-dump
727      Create extended ABI dump containing all symbols
728      from the translation unit.
729
730  -force
731      Try to use this option if the tool doesn't work.
732
733  -tolerance LEVEL
734      Apply a set of heuristics to successfully compile input
735      header files. You can enable several tolerance levels by
736      joining them into one string (e.g. 13, 124, etc.).
737      Levels:
738          1 - skip non-Linux headers (e.g. win32_*.h, etc.)
739          2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.)
740          3 - skip headers that iclude non-Linux headers
741          4 - skip headers included by others
742
743  -tolerant
744      Enable highest tolerance level [1234].
745
746  -check
747      Check completeness of the ABI dump.
748
749  -quick
750      Quick analysis. Disable check of some template instances.
751
752  -skip-internal-symbols PATTERN
753      Do not check symbols matched by the pattern.
754
755  -skip-internal-types PATTERN
756      Do not check types matched by the pattern.
757
758  -check-private-abi
759      Check data types from the private part of the ABI when
760      comparing ABI dumps created by the ABI Dumper tool with
761      use of the -public-headers option.
762
763      Requires ABI Dumper >= 0.99.14
764
765REPORT:
766    Compatibility report will be generated to:
767        compat_reports/LIB_NAME/V1_to_V2/compat_report.html
768
769    Log will be generated to:
770        logs/LIB_NAME/V1/log.txt
771        logs/LIB_NAME/V2/log.txt
772
773EXIT CODES:
774    0 - Compatible. The tool has run without any errors.
775    non-zero - Incompatible or the tool has run with errors.
776
777MORE INFORMATION:
778    ".$HomePage."\n");
779}
780
781my %Operator_Indication = (
782    "not" => "~",
783    "assign" => "=",
784    "andassign" => "&=",
785    "orassign" => "|=",
786    "xorassign" => "^=",
787    "or" => "|",
788    "xor" => "^",
789    "addr" => "&",
790    "and" => "&",
791    "lnot" => "!",
792    "eq" => "==",
793    "ne" => "!=",
794    "lt" => "<",
795    "lshift" => "<<",
796    "lshiftassign" => "<<=",
797    "rshiftassign" => ">>=",
798    "call" => "()",
799    "mod" => "%",
800    "modassign" => "%=",
801    "subs" => "[]",
802    "land" => "&&",
803    "lor" => "||",
804    "rshift" => ">>",
805    "ref" => "->",
806    "le" => "<=",
807    "deref" => "*",
808    "mult" => "*",
809    "preinc" => "++",
810    "delete" => " delete",
811    "vecnew" => " new[]",
812    "vecdelete" => " delete[]",
813    "predec" => "--",
814    "postinc" => "++",
815    "postdec" => "--",
816    "plusassign" => "+=",
817    "plus" => "+",
818    "minus" => "-",
819    "minusassign" => "-=",
820    "gt" => ">",
821    "ge" => ">=",
822    "new" => " new",
823    "multassign" => "*=",
824    "divassign" => "/=",
825    "div" => "/",
826    "neg" => "-",
827    "pos" => "+",
828    "memref" => "->*",
829    "compound" => "," );
830
831my %UnknownOperator;
832
833my %NodeType= (
834  "array_type" => "Array",
835  "binfo" => "Other",
836  "boolean_type" => "Intrinsic",
837  "complex_type" => "Intrinsic",
838  "const_decl" => "Other",
839  "enumeral_type" => "Enum",
840  "field_decl" => "Other",
841  "function_decl" => "Other",
842  "function_type" => "FunctionType",
843  "identifier_node" => "Other",
844  "integer_cst" => "Other",
845  "integer_type" => "Intrinsic",
846  "vector_type" => "Vector",
847  "method_type" => "MethodType",
848  "namespace_decl" => "Other",
849  "parm_decl" => "Other",
850  "pointer_type" => "Pointer",
851  "real_cst" => "Other",
852  "real_type" => "Intrinsic",
853  "record_type" => "Struct",
854  "reference_type" => "Ref",
855  "string_cst" => "Other",
856  "template_decl" => "Other",
857  "template_type_parm" => "TemplateParam",
858  "typename_type" => "TypeName",
859  "sizeof_expr" => "SizeOf",
860  "tree_list" => "Other",
861  "tree_vec" => "Other",
862  "type_decl" => "Other",
863  "union_type" => "Union",
864  "var_decl" => "Other",
865  "void_type" => "Intrinsic",
866  "nop_expr" => "Other", #
867  "addr_expr" => "Other", #
868  "offset_type" => "Other" );
869
870my %CppKeywords_C = map {$_=>1} (
871    # C++ 2003 keywords
872    "public",
873    "protected",
874    "private",
875    "default",
876    "template",
877    "new",
878    #"asm",
879    "dynamic_cast",
880    "auto",
881    "try",
882    "namespace",
883    "typename",
884    "using",
885    "reinterpret_cast",
886    "friend",
887    "class",
888    "virtual",
889    "const_cast",
890    "mutable",
891    "static_cast",
892    "export",
893    # C++0x keywords
894    "noexcept",
895    "nullptr",
896    "constexpr",
897    "static_assert",
898    "explicit",
899    # cannot be used as a macro name
900    # as it is an operator in C++
901    "and",
902    #"and_eq",
903    "not",
904    #"not_eq",
905    "or"
906    #"or_eq",
907    #"bitand",
908    #"bitor",
909    #"xor",
910    #"xor_eq",
911    #"compl"
912);
913
914my %CppKeywords_F = map {$_=>1} (
915    "delete",
916    "catch",
917    "alignof",
918    "thread_local",
919    "decltype",
920    "typeid"
921);
922
923my %CppKeywords_O = map {$_=>1} (
924    "bool",
925    "register",
926    "inline",
927    "operator"
928);
929
930my %CppKeywords_A = map {$_=>1} (
931    "this",
932    "throw",
933    "template"
934);
935
936foreach (keys(%CppKeywords_C),
937keys(%CppKeywords_F),
938keys(%CppKeywords_O)) {
939    $CppKeywords_A{$_}=1;
940}
941
942# Header file extensions as described by gcc
943my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+";
944
945my %IntrinsicMangling = (
946    "void" => "v",
947    "bool" => "b",
948    "wchar_t" => "w",
949    "char" => "c",
950    "signed char" => "a",
951    "unsigned char" => "h",
952    "short" => "s",
953    "unsigned short" => "t",
954    "int" => "i",
955    "unsigned int" => "j",
956    "long" => "l",
957    "unsigned long" => "m",
958    "long long" => "x",
959    "__int64" => "x",
960    "unsigned long long" => "y",
961    "__int128" => "n",
962    "unsigned __int128" => "o",
963    "float" => "f",
964    "double" => "d",
965    "long double" => "e",
966    "__float80" => "e",
967    "__float128" => "g",
968    "..." => "z"
969);
970
971my %IntrinsicNames = map {$_=>1} keys(%IntrinsicMangling);
972
973my %StdcxxMangling = (
974    "3std"=>"St",
975    "3std9allocator"=>"Sa",
976    "3std12basic_string"=>"Sb",
977    "3std12basic_stringIcE"=>"Ss",
978    "3std13basic_istreamIcE"=>"Si",
979    "3std13basic_ostreamIcE"=>"So",
980    "3std14basic_iostreamIcE"=>"Sd"
981);
982
983my $DEFAULT_STD_PARMS = "std::(allocator|less|char_traits|regex_traits|istreambuf_iterator|ostreambuf_iterator)";
984my %DEFAULT_STD_ARGS = map {$_=>1} ("_Alloc", "_Compare", "_Traits", "_Rx_traits", "_InIter", "_OutIter");
985
986my $ADD_TMPL_INSTANCES = 1;
987my $EMERGENCY_MODE_48 = 0;
988
989my %ConstantSuffix = (
990    "unsigned int"=>"u",
991    "long"=>"l",
992    "unsigned long"=>"ul",
993    "long long"=>"ll",
994    "unsigned long long"=>"ull"
995);
996
997my %ConstantSuffixR =
998reverse(%ConstantSuffix);
999
1000my %OperatorMangling = (
1001    "~" => "co",
1002    "=" => "aS",
1003    "|" => "or",
1004    "^" => "eo",
1005    "&" => "an",#ad (addr)
1006    "==" => "eq",
1007    "!" => "nt",
1008    "!=" => "ne",
1009    "<" => "lt",
1010    "<=" => "le",
1011    "<<" => "ls",
1012    "<<=" => "lS",
1013    ">" => "gt",
1014    ">=" => "ge",
1015    ">>" => "rs",
1016    ">>=" => "rS",
1017    "()" => "cl",
1018    "%" => "rm",
1019    "[]" => "ix",
1020    "&&" => "aa",
1021    "||" => "oo",
1022    "*" => "ml",#de (deref)
1023    "++" => "pp",#
1024    "--" => "mm",#
1025    "new" => "nw",
1026    "delete" => "dl",
1027    "new[]" => "na",
1028    "delete[]" => "da",
1029    "+=" => "pL",
1030    "+" => "pl",#ps (pos)
1031    "-" => "mi",#ng (neg)
1032    "-=" => "mI",
1033    "*=" => "mL",
1034    "/=" => "dV",
1035    "&=" => "aN",
1036    "|=" => "oR",
1037    "%=" => "rM",
1038    "^=" => "eO",
1039    "/" => "dv",
1040    "->*" => "pm",
1041    "->" => "pt",#rf (ref)
1042    "," => "cm",
1043    "?" => "qu",
1044    "." => "dt",
1045    "sizeof"=> "sz"#st
1046);
1047
1048my %Intrinsic_Keywords = map {$_=>1} (
1049    "true",
1050    "false",
1051    "_Bool",
1052    "_Complex",
1053    "const",
1054    "int",
1055    "long",
1056    "void",
1057    "short",
1058    "float",
1059    "volatile",
1060    "restrict",
1061    "unsigned",
1062    "signed",
1063    "char",
1064    "double",
1065    "class",
1066    "struct",
1067    "union",
1068    "enum"
1069);
1070
1071my %GlibcHeader = map {$_=>1} (
1072    "aliases.h",
1073    "argp.h",
1074    "argz.h",
1075    "assert.h",
1076    "cpio.h",
1077    "ctype.h",
1078    "dirent.h",
1079    "envz.h",
1080    "errno.h",
1081    "error.h",
1082    "execinfo.h",
1083    "fcntl.h",
1084    "fstab.h",
1085    "ftw.h",
1086    "glob.h",
1087    "grp.h",
1088    "iconv.h",
1089    "ifaddrs.h",
1090    "inttypes.h",
1091    "langinfo.h",
1092    "limits.h",
1093    "link.h",
1094    "locale.h",
1095    "malloc.h",
1096    "math.h",
1097    "mntent.h",
1098    "monetary.h",
1099    "nl_types.h",
1100    "obstack.h",
1101    "printf.h",
1102    "pwd.h",
1103    "regex.h",
1104    "sched.h",
1105    "search.h",
1106    "setjmp.h",
1107    "shadow.h",
1108    "signal.h",
1109    "spawn.h",
1110    "stdarg.h",
1111    "stdint.h",
1112    "stdio.h",
1113    "stdlib.h",
1114    "string.h",
1115    "strings.h",
1116    "tar.h",
1117    "termios.h",
1118    "time.h",
1119    "ulimit.h",
1120    "unistd.h",
1121    "utime.h",
1122    "wchar.h",
1123    "wctype.h",
1124    "wordexp.h" );
1125
1126my %GlibcDir = map {$_=>1} (
1127    "arpa",
1128    "bits",
1129    "gnu",
1130    "netinet",
1131    "net",
1132    "nfs",
1133    "rpc",
1134    "sys",
1135    "linux" );
1136
1137my %WinHeaders = map {$_=>1} (
1138    "dos.h",
1139    "process.h",
1140    "winsock.h",
1141    "config-win.h",
1142    "mem.h",
1143    "windows.h",
1144    "winsock2.h",
1145    "crtdbg.h",
1146    "ws2tcpip.h"
1147);
1148
1149my %ObsoleteHeaders = map {$_=>1} (
1150    "iostream.h",
1151    "fstream.h"
1152);
1153
1154my %AlienHeaders = map {$_=>1} (
1155 # Solaris
1156    "thread.h",
1157    "sys/atomic.h",
1158 # HPUX
1159    "sys/stream.h",
1160 # Symbian
1161    "AknDoc.h",
1162 # Atari ST
1163    "ext.h",
1164    "tos.h",
1165 # MS-DOS
1166    "alloc.h",
1167 # Sparc
1168    "sys/atomic.h"
1169);
1170
1171my %ConfHeaders = map {$_=>1} (
1172    "atomic",
1173    "conf.h",
1174    "config.h",
1175    "configure.h",
1176    "build.h",
1177    "setup.h"
1178);
1179
1180my %LocalIncludes = map {$_=>1} (
1181    "/usr/local/include",
1182    "/usr/local" );
1183
1184my %OS_AddPath=(
1185# These paths are needed if the tool cannot detect them automatically
1186    "macos"=>{
1187        "include"=>[
1188            "/Library",
1189            "/Developer/usr/include"
1190        ],
1191        "lib"=>[
1192            "/Library",
1193            "/Developer/usr/lib"
1194        ],
1195        "bin"=>[
1196            "/Developer/usr/bin"
1197        ]
1198    },
1199    "beos"=>{
1200    # Haiku has GCC 2.95.3 by default
1201    # try to find GCC>=3.0 in /boot/develop/abi
1202        "include"=>[
1203            "/boot/common",
1204            "/boot/develop"
1205        ],
1206        "lib"=>[
1207            "/boot/common/lib",
1208            "/boot/system/lib",
1209            "/boot/apps"
1210        ],
1211        "bin"=>[
1212            "/boot/common/bin",
1213            "/boot/system/bin",
1214            "/boot/develop/abi"
1215        ]
1216    }
1217);
1218
1219my %Slash_Type=(
1220    "default"=>"/",
1221    "windows"=>"\\"
1222);
1223
1224my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"};
1225
1226# Global Variables
1227my %COMMON_LANGUAGE=(
1228  1 => "C",
1229  2 => "C" );
1230
1231my $MAX_COMMAND_LINE_ARGUMENTS = 4096;
1232my $MAX_CPPFILT_FILE_SIZE = 50000;
1233my $CPPFILT_SUPPORT_FILE;
1234
1235my (%WORD_SIZE, %CPU_ARCH, %GCC_VERSION);
1236
1237my $STDCXX_TESTING = 0;
1238my $GLIBC_TESTING = 0;
1239my $CPP_HEADERS = 0;
1240
1241my $CheckHeadersOnly = $CheckHeadersOnly_Opt;
1242my $CheckUndefined = 0;
1243
1244my $TargetComponent = undef;
1245if($TargetComponent_Opt) {
1246    $TargetComponent = lc($TargetComponent_Opt);
1247}
1248else
1249{ # default: library
1250  # other components: header, system, ...
1251    $TargetComponent = "library";
1252}
1253
1254my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
1255
1256my $SystemRoot;
1257
1258my $MAIN_CPP_DIR;
1259my %RESULT;
1260my %LOG_PATH;
1261my %DEBUG_PATH;
1262my %Cache;
1263my %LibInfo;
1264my $COMPILE_ERRORS = 0;
1265my %CompilerOptions;
1266my %CheckedDyLib;
1267my $TargetLibraryShortName = parse_libname($TargetLibraryName, "shortest", $OSgroup);
1268
1269# Constants (#defines)
1270my %Constants;
1271my %SkipConstants;
1272my %EnumConstants;
1273
1274# Extra Info
1275my %SymbolHeader;
1276my %KnownLibs;
1277
1278# Templates
1279my %TemplateInstance;
1280my %BasicTemplate;
1281my %TemplateArg;
1282my %TemplateDecl;
1283my %TemplateMap;
1284
1285# Types
1286my %TypeInfo;
1287my %SkipTypes = (
1288  "1"=>{},
1289  "2"=>{} );
1290my %CheckedTypes;
1291my %TName_Tid;
1292my %EnumMembName_Id;
1293my %NestedNameSpaces = (
1294  "1"=>{},
1295  "2"=>{} );
1296my %VirtualTable;
1297my %VirtualTable_Model;
1298my %ClassVTable;
1299my %ClassVTable_Content;
1300my %VTableClass;
1301my %AllocableClass;
1302my %ClassMethods;
1303my %ClassNames;
1304my %Class_SubClasses;
1305my %OverriddenMethods;
1306my %TypedefToAnon;
1307my $MAX_ID = 0;
1308
1309my %CheckedTypeInfo;
1310
1311# Typedefs
1312my %Typedef_BaseName;
1313my %Typedef_Tr;
1314my %Typedef_Eq;
1315my %StdCxxTypedef;
1316my %MissedTypedef;
1317my %MissedBase;
1318my %MissedBase_R;
1319my %TypeTypedef;
1320
1321# Symbols
1322my %SymbolInfo;
1323my %tr_name;
1324my %mangled_name_gcc;
1325my %mangled_name;
1326my %SkipSymbols = (
1327  "1"=>{},
1328  "2"=>{} );
1329my %SkipNameSpaces = (
1330  "1"=>{},
1331  "2"=>{} );
1332my %AddNameSpaces = (
1333  "1"=>{},
1334  "2"=>{} );
1335my %SymbolsList;
1336my %TypesList;
1337my %SymbolsList_App;
1338my %CheckedSymbols;
1339my %Symbol_Library = (
1340  "1"=>{},
1341  "2"=>{} );
1342my %Library_Symbol = (
1343  "1"=>{},
1344  "2"=>{} );
1345my %DepSymbol_Library = (
1346  "1"=>{},
1347  "2"=>{} );
1348my %DepLibrary_Symbol = (
1349  "1"=>{},
1350  "2"=>{} );
1351my %MangledNames;
1352my %Func_ShortName;
1353my %AddIntParams;
1354my %GlobalDataObject;
1355my %WeakSymbols;
1356my %Library_Needed= (
1357  "1"=>{},
1358  "2"=>{} );
1359
1360# Extra Info
1361my %UndefinedSymbols;
1362my %PreprocessedHeaders;
1363
1364# Headers
1365my %Include_Preamble = (
1366    "1"=>[],
1367    "2"=>[] );
1368my %Registered_Headers;
1369my %Registered_Sources;
1370my %HeaderName_Paths;
1371my %Header_Dependency;
1372my %Include_Neighbors;
1373my %Include_Paths = (
1374    "1"=>[],
1375    "2"=>[] );
1376my %INC_PATH_AUTODETECT = (
1377  "1"=>1,
1378  "2"=>1 );
1379my %Add_Include_Paths = (
1380    "1"=>[],
1381    "2"=>[] );
1382my %Skip_Include_Paths;
1383my %RegisteredDirs;
1384my %Header_ErrorRedirect;
1385my %Header_Includes;
1386my %Header_Includes_R;
1387my %Header_ShouldNotBeUsed;
1388my %RecursiveIncludes;
1389my %Header_Include_Prefix;
1390my %SkipHeaders;
1391my %SkipHeadersList=(
1392  "1"=>{},
1393  "2"=>{} );
1394my %SkipLibs;
1395my %Include_Order;
1396my %TUnit_NameSpaces;
1397my %TUnit_Classes;
1398my %TUnit_Funcs;
1399my %TUnit_Vars;
1400
1401my %CppMode = (
1402  "1"=>0,
1403  "2"=>0 );
1404my %AutoPreambleMode = (
1405  "1"=>0,
1406  "2"=>0 );
1407my %MinGWMode = (
1408  "1"=>0,
1409  "2"=>0 );
1410my %Cpp0xMode = (
1411  "1"=>0,
1412  "2"=>0 );
1413
1414# Shared Objects
1415my %RegisteredObjects;
1416my %RegisteredObjects_Short;
1417my %RegisteredSONAMEs;
1418my %RegisteredObject_Dirs;
1419
1420my %CheckedArch;
1421
1422# System Objects
1423my %SystemObjects;
1424my @DefaultLibPaths;
1425my %DyLib_DefaultPath;
1426
1427# System Headers
1428my %SystemHeaders;
1429my @DefaultCppPaths;
1430my @DefaultGccPaths;
1431my @DefaultIncPaths;
1432my %DefaultCppHeader;
1433my %DefaultGccHeader;
1434my @UsersIncPath;
1435
1436# Merging
1437my %CompleteSignature;
1438my $Version;
1439my %AddedInt;
1440my %RemovedInt;
1441my %AddedInt_Virt;
1442my %RemovedInt_Virt;
1443my %VirtualReplacement;
1444my %ChangedTypedef;
1445my %CompatRules;
1446my %IncompleteRules;
1447my %UnknownRules;
1448my %VTableChanged_M;
1449my %ExtendedSymbols;
1450my %ReturnedClass;
1451my %ParamClass;
1452my %SourceAlternative;
1453my %SourceAlternative_B;
1454my %SourceReplacement;
1455my $CurrentSymbol; # for debugging
1456
1457# Calling Conventions
1458my %UseConv_Real = (
1459  1=>{ "R"=>0, "P"=>0 },
1460  2=>{ "R"=>0, "P"=>0 }
1461);
1462
1463# ABI Dump
1464my %UsedDump;
1465
1466# Filters
1467my %TargetLibs;
1468my %TargetHeaders;
1469
1470# Format of objects
1471my $OStarget = $OSgroup;
1472my %TargetTools;
1473
1474# Compliance Report
1475my %Type_MaxSeverity;
1476
1477# Recursion locks
1478my @RecurLib;
1479my @RecurTypes;
1480my @RecurTypes_Diff;
1481my @RecurInclude;
1482my @RecurConstant;
1483
1484# System
1485my %SystemPaths = (
1486    "include"=>[],
1487    "lib"=>[],
1488    "bin"=>[]
1489);
1490my @DefaultBinPaths;
1491my $GCC_PATH;
1492
1493# Symbols versioning
1494my %SymVer = (
1495  "1"=>{},
1496  "2"=>{} );
1497
1498# Problem descriptions
1499my %CompatProblems;
1500my %CompatProblems_Constants;
1501my %TotalAffected;
1502
1503# Reports
1504my $ContentID = 1;
1505my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1506my $ContentSpanStart_Affected = "<span class=\"section_affected\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1507my $ContentSpanStart_Info = "<span class=\"section_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1508my $ContentSpanEnd = "</span>\n";
1509my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
1510my $ContentDivEnd = "</div>\n";
1511my $Content_Counter = 0;
1512
1513# Modes
1514my $JoinReport = 1;
1515my $DoubleReport = 0;
1516
1517my %Severity_Val=(
1518    "High"=>3,
1519    "Medium"=>2,
1520    "Low"=>1,
1521    "Safe"=>-1
1522);
1523
1524sub get_Modules()
1525{
1526    my $TOOL_DIR = get_dirname($0);
1527    if(not $TOOL_DIR)
1528    { # patch for MS Windows
1529        $TOOL_DIR = ".";
1530    }
1531    my @SEARCH_DIRS = (
1532        # tool's directory
1533        abs_path($TOOL_DIR),
1534        # relative path to modules
1535        abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
1536        # install path
1537        'MODULES_INSTALL_PATH'
1538    );
1539    foreach my $DIR (@SEARCH_DIRS)
1540    {
1541        if(not is_abs($DIR))
1542        { # relative path
1543            $DIR = abs_path($TOOL_DIR)."/".$DIR;
1544        }
1545        if(-d $DIR."/modules") {
1546            return $DIR."/modules";
1547        }
1548    }
1549    exitStatus("Module_Error", "can't find modules");
1550}
1551
1552my %LoadedModules = ();
1553
1554sub loadModule($)
1555{
1556    my $Name = $_[0];
1557    if(defined $LoadedModules{$Name}) {
1558        return;
1559    }
1560    my $Path = $MODULES_DIR."/Internals/$Name.pm";
1561    if(not -f $Path) {
1562        exitStatus("Module_Error", "can't access \'$Path\'");
1563    }
1564    require $Path;
1565    $LoadedModules{$Name} = 1;
1566}
1567
1568sub readModule($$)
1569{
1570    my ($Module, $Name) = @_;
1571    my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
1572    if(not -f $Path) {
1573        exitStatus("Module_Error", "can't access \'$Path\'");
1574    }
1575    return readFile($Path);
1576}
1577
1578sub showPos($)
1579{
1580    my $Number = $_[0];
1581    if(not $Number) {
1582        $Number = 1;
1583    }
1584    else {
1585        $Number = int($Number)+1;
1586    }
1587    if($Number>3) {
1588        return $Number."th";
1589    }
1590    elsif($Number==1) {
1591        return "1st";
1592    }
1593    elsif($Number==2) {
1594        return "2nd";
1595    }
1596    elsif($Number==3) {
1597        return "3rd";
1598    }
1599    else {
1600        return $Number;
1601    }
1602}
1603
1604sub search_Tools($)
1605{
1606    my $Name = $_[0];
1607    return "" if(not $Name);
1608    if(my @Paths = keys(%TargetTools))
1609    {
1610        foreach my $Path (@Paths)
1611        {
1612            if(-f join_P($Path, $Name)) {
1613                return join_P($Path, $Name);
1614            }
1615            if($CrossPrefix)
1616            { # user-defined prefix (arm-none-symbianelf, ...)
1617                my $Candidate = join_P($Path, $CrossPrefix."-".$Name);
1618                if(-f $Candidate) {
1619                    return $Candidate;
1620                }
1621            }
1622        }
1623    }
1624    else {
1625        return "";
1626    }
1627}
1628
1629sub synch_Cmd($)
1630{
1631    my $Name = $_[0];
1632    if(not $GCC_PATH)
1633    { # GCC was not found yet
1634        return "";
1635    }
1636    my $Candidate = $GCC_PATH;
1637    if($Candidate=~s/\bgcc(|\.\w+)\Z/$Name$1/) {
1638        return $Candidate;
1639    }
1640    return "";
1641}
1642
1643sub get_CmdPath($)
1644{
1645    my $Name = $_[0];
1646    return "" if(not $Name);
1647    if(defined $Cache{"get_CmdPath"}{$Name}) {
1648        return $Cache{"get_CmdPath"}{$Name};
1649    }
1650    my %BinUtils = map {$_=>1} (
1651        "c++filt",
1652        "objdump",
1653        "readelf"
1654    );
1655    if($BinUtils{$Name} and $GCC_PATH)
1656    {
1657        if(my $Dir = get_dirname($GCC_PATH)) {
1658            $TargetTools{$Dir}=1;
1659        }
1660    }
1661    my $Path = search_Tools($Name);
1662    if(not $Path and $OSgroup eq "windows") {
1663        $Path = search_Tools($Name.".exe");
1664    }
1665    if(not $Path and $BinUtils{$Name})
1666    {
1667        if($CrossPrefix)
1668        { # user-defined prefix
1669            $Path = search_Cmd($CrossPrefix."-".$Name);
1670        }
1671    }
1672    if(not $Path and $BinUtils{$Name})
1673    {
1674        if(my $Candidate = synch_Cmd($Name))
1675        { # synch with GCC
1676            if($Candidate=~/[\/\\]/)
1677            { # command path
1678                if(-f $Candidate) {
1679                    $Path = $Candidate;
1680                }
1681            }
1682            elsif($Candidate = search_Cmd($Candidate))
1683            { # command name
1684                $Path = $Candidate;
1685            }
1686        }
1687    }
1688    if(not $Path) {
1689        $Path = search_Cmd($Name);
1690    }
1691    if(not $Path and $OSgroup eq "windows")
1692    { # search for *.exe file
1693        $Path=search_Cmd($Name.".exe");
1694    }
1695    if($Path=~/\s/) {
1696        $Path = "\"".$Path."\"";
1697    }
1698    return ($Cache{"get_CmdPath"}{$Name}=$Path);
1699}
1700
1701sub search_Cmd($)
1702{
1703    my $Name = $_[0];
1704    return "" if(not $Name);
1705    if(defined $Cache{"search_Cmd"}{$Name}) {
1706        return $Cache{"search_Cmd"}{$Name};
1707    }
1708    if(my $DefaultPath = get_CmdPath_Default($Name)) {
1709        return ($Cache{"search_Cmd"}{$Name} = $DefaultPath);
1710    }
1711    foreach my $Path (@{$SystemPaths{"bin"}})
1712    {
1713        my $CmdPath = join_P($Path,$Name);
1714        if(-f $CmdPath)
1715        {
1716            if($Name=~/gcc/) {
1717                next if(not check_gcc($CmdPath, "3"));
1718            }
1719            return ($Cache{"search_Cmd"}{$Name} = $CmdPath);
1720        }
1721    }
1722    return ($Cache{"search_Cmd"}{$Name} = "");
1723}
1724
1725sub get_CmdPath_Default($)
1726{ # search in PATH
1727    return "" if(not $_[0]);
1728    if(defined $Cache{"get_CmdPath_Default"}{$_[0]}) {
1729        return $Cache{"get_CmdPath_Default"}{$_[0]};
1730    }
1731    return ($Cache{"get_CmdPath_Default"}{$_[0]} = get_CmdPath_Default_I($_[0]));
1732}
1733
1734sub get_CmdPath_Default_I($)
1735{ # search in PATH
1736    my $Name = $_[0];
1737    if($Name=~/find/)
1738    { # special case: search for "find" utility
1739        if(`find \"$TMP_DIR\" -maxdepth 0 2>\"$TMP_DIR/null\"`) {
1740            return "find";
1741        }
1742    }
1743    elsif($Name=~/gcc/) {
1744        return check_gcc($Name, "3");
1745    }
1746    if(checkCmd($Name)) {
1747        return $Name;
1748    }
1749    if($OSgroup eq "windows")
1750    {
1751        if(`$Name /? 2>\"$TMP_DIR/null\"`) {
1752            return $Name;
1753        }
1754    }
1755    foreach my $Path (@DefaultBinPaths)
1756    {
1757        if(-f $Path."/".$Name) {
1758            return join_P($Path, $Name);
1759        }
1760    }
1761    return "";
1762}
1763
1764sub classifyPath($)
1765{
1766    my $Path = $_[0];
1767    if($Path=~/[\*\[]/)
1768    { # wildcard
1769        $Path=~s/\*/.*/g;
1770        $Path=~s/\\/\\\\/g;
1771        return ($Path, "Pattern");
1772    }
1773    elsif($Path=~/[\/\\]/)
1774    { # directory or relative path
1775        return (path_format($Path, $OSgroup), "Path");
1776    }
1777    else {
1778        return ($Path, "Name");
1779    }
1780}
1781
1782sub readDescriptor($$)
1783{
1784    my ($LibVersion, $Content) = @_;
1785    return if(not $LibVersion);
1786    my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
1787    if(not $Content) {
1788        exitStatus("Error", "$DName is empty");
1789    }
1790    if($Content!~/\</) {
1791        exitStatus("Error", "incorrect descriptor (see -d1 option)");
1792    }
1793    $Content=~s/\/\*(.|\n)+?\*\///g;
1794    $Content=~s/<\!--(.|\n)+?-->//g;
1795
1796    $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version");
1797    if($TargetVersion{$LibVersion}) {
1798        $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion};
1799    }
1800    if(not $Descriptor{$LibVersion}{"Version"}) {
1801        exitStatus("Error", "version in the $DName is not specified (<version> section)");
1802    }
1803    if($Content=~/{RELPATH}/)
1804    {
1805        if(my $RelDir = $RelativeDirectory{$LibVersion}) {
1806            $Content =~ s/{RELPATH}/$RelDir/g;
1807        }
1808        else
1809        {
1810            my $NeedRelpath = $DumpAPI?"-relpath":"-relpath$LibVersion";
1811            exitStatus("Error", "you have not specified $NeedRelpath option, but the $DName contains {RELPATH} macro");
1812        }
1813    }
1814
1815    my $DHeaders = parseTag(\$Content, "headers");
1816    if(not $DHeaders) {
1817        exitStatus("Error", "header files in the $DName are not specified (<headers> section)");
1818    }
1819    elsif(lc($DHeaders) ne "none")
1820    { # append the descriptor headers list
1821        if($Descriptor{$LibVersion}{"Headers"})
1822        { # multiple descriptors
1823            $Descriptor{$LibVersion}{"Headers"} .= "\n".$DHeaders;
1824        }
1825        else {
1826            $Descriptor{$LibVersion}{"Headers"} = $DHeaders;
1827        }
1828        foreach my $Path (split(/\s*\n\s*/, $DHeaders))
1829        {
1830            if(not -e $Path) {
1831                exitStatus("Access_Error", "can't access \'$Path\'");
1832            }
1833        }
1834    }
1835
1836    if(not $CheckHeadersOnly_Opt)
1837    {
1838        my $DObjects = parseTag(\$Content, "libs");
1839        if(not $DObjects) {
1840            exitStatus("Error", "$SLIB_TYPE libraries in the $DName are not specified (<libs> section)");
1841        }
1842        elsif(lc($DObjects) ne "none")
1843        { # append the descriptor libraries list
1844            if($Descriptor{$LibVersion}{"Libs"})
1845            { # multiple descriptors
1846                $Descriptor{$LibVersion}{"Libs"} .= "\n".$DObjects;
1847            }
1848            else {
1849                $Descriptor{$LibVersion}{"Libs"} .= $DObjects;
1850            }
1851            foreach my $Path (split(/\s*\n\s*/, $DObjects))
1852            {
1853                if(not -e $Path) {
1854                    exitStatus("Access_Error", "can't access \'$Path\'");
1855                }
1856            }
1857        }
1858    }
1859    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers")))
1860    {
1861        if(not -d $Path) {
1862            exitStatus("Access_Error", "can't access directory \'$Path\'");
1863        }
1864        $Path = get_abs_path($Path);
1865        $Path = path_format($Path, $OSgroup);
1866        push_U($SystemPaths{"include"}, $Path);
1867    }
1868    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs")))
1869    {
1870        if(not -d $Path) {
1871            exitStatus("Access_Error", "can't access directory \'$Path\'");
1872        }
1873        $Path = get_abs_path($Path);
1874        $Path = path_format($Path, $OSgroup);
1875        push_U($SystemPaths{"lib"}, $Path);
1876    }
1877    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools")))
1878    {
1879        if(not -d $Path) {
1880            exitStatus("Access_Error", "can't access directory \'$Path\'");
1881        }
1882        $Path = get_abs_path($Path);
1883        $Path = path_format($Path, $OSgroup);
1884        push_U($SystemPaths{"bin"}, $Path);
1885        $TargetTools{$Path}=1;
1886    }
1887    if(my $Prefix = parseTag(\$Content, "cross_prefix")) {
1888        $CrossPrefix = $Prefix;
1889    }
1890    $Descriptor{$LibVersion}{"IncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"IncludePaths"}); # perl 5.8 doesn't support //=
1891    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths")))
1892    {
1893        if(not -d $Path) {
1894            exitStatus("Access_Error", "can't access directory \'$Path\'");
1895        }
1896        $Path = get_abs_path($Path);
1897        $Path = path_format($Path, $OSgroup);
1898        push(@{$Descriptor{$LibVersion}{"IncludePaths"}}, $Path);
1899    }
1900    $Descriptor{$LibVersion}{"AddIncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"AddIncludePaths"});
1901    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths")))
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(@{$Descriptor{$LibVersion}{"AddIncludePaths"}}, $Path);
1909    }
1910    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths")))
1911    { # skip some auto-generated include paths
1912        if(not is_abs($Path))
1913        {
1914            if(my $P = abs_path($Path)) {
1915                $Path = $P;
1916            }
1917        }
1918        $Skip_Include_Paths{$LibVersion}{path_format($Path)} = 1;
1919    }
1920    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including")))
1921    { # skip direct including of some headers
1922        my ($CPath, $Type) = classifyPath($Path);
1923        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 2;
1924    }
1925    $Descriptor{$LibVersion}{"GccOptions"} = parseTag(\$Content, "gcc_options");
1926    foreach my $Option (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"GccOptions"}))
1927    {
1928        if($Option!~/\A\-(Wl|l|L)/)
1929        { # skip linker options
1930            $CompilerOptions{$LibVersion} .= " ".$Option;
1931        }
1932    }
1933    $Descriptor{$LibVersion}{"SkipHeaders"} = parseTag(\$Content, "skip_headers");
1934    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipHeaders"}))
1935    {
1936        $SkipHeadersList{$LibVersion}{$Path} = 1;
1937        my ($CPath, $Type) = classifyPath($Path);
1938        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 1;
1939    }
1940    $Descriptor{$LibVersion}{"SkipLibs"} = parseTag(\$Content, "skip_libs");
1941    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipLibs"}))
1942    {
1943        my ($CPath, $Type) = classifyPath($Path);
1944        $SkipLibs{$LibVersion}{$Type}{$CPath} = 1;
1945    }
1946    if(my $DDefines = parseTag(\$Content, "defines"))
1947    {
1948        if($Descriptor{$LibVersion}{"Defines"})
1949        { # multiple descriptors
1950            $Descriptor{$LibVersion}{"Defines"} .= "\n".$DDefines;
1951        }
1952        else {
1953            $Descriptor{$LibVersion}{"Defines"} = $DDefines;
1954        }
1955    }
1956    foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order")))
1957    {
1958        if($Order=~/\A(.+):(.+)\Z/) {
1959            $Include_Order{$LibVersion}{$1} = $2;
1960        }
1961    }
1962    foreach my $Type_Name (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")),
1963    split(/\s*\n\s*/, parseTag(\$Content, "skip_types")))
1964    { # opaque_types renamed to skip_types (1.23.4)
1965        $SkipTypes{$LibVersion}{$Type_Name} = 1;
1966    }
1967    foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")),
1968    split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols")))
1969    { # skip_interfaces renamed to skip_symbols (1.22.1)
1970        $SkipSymbols{$LibVersion}{$Symbol} = 1;
1971    }
1972    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) {
1973        $SkipNameSpaces{$LibVersion}{$NameSpace} = 1;
1974    }
1975    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) {
1976        $AddNameSpaces{$LibVersion}{$NameSpace} = 1;
1977    }
1978    foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) {
1979        $SkipConstants{$LibVersion}{$Constant} = 1;
1980    }
1981    if(my $DIncPreamble = parseTag(\$Content, "include_preamble"))
1982    {
1983        if($Descriptor{$LibVersion}{"IncludePreamble"})
1984        { # multiple descriptors
1985            $Descriptor{$LibVersion}{"IncludePreamble"} .= "\n".$DIncPreamble;
1986        }
1987        else {
1988            $Descriptor{$LibVersion}{"IncludePreamble"} = $DIncPreamble;
1989        }
1990    }
1991}
1992
1993sub parseTag(@)
1994{
1995    my $CodeRef = shift(@_);
1996    my $Tag = shift(@_);
1997    if(not $Tag or not $CodeRef) {
1998        return undef;
1999    }
2000    my $Sp = 0;
2001    if(@_) {
2002        $Sp = shift(@_);
2003    }
2004    my $Start = index(${$CodeRef}, "<$Tag>");
2005    if($Start!=-1)
2006    {
2007        my $End = index(${$CodeRef}, "</$Tag>");
2008        if($End!=-1)
2009        {
2010            my $TS = length($Tag)+3;
2011            my $Content = substr(${$CodeRef}, $Start, $End-$Start+$TS, "");
2012            substr($Content, 0, $TS-1, ""); # cut start tag
2013            substr($Content, -$TS, $TS, ""); # cut end tag
2014            if(not $Sp)
2015            {
2016                $Content=~s/\A\s+//g;
2017                $Content=~s/\s+\Z//g;
2018            }
2019            if(substr($Content, 0, 1) ne "<") {
2020                $Content = xmlSpecChars_R($Content);
2021            }
2022            return $Content;
2023        }
2024    }
2025    return undef;
2026}
2027
2028sub getInfo($)
2029{
2030    my $DumpPath = $_[0];
2031    return if(not $DumpPath or not -f $DumpPath);
2032
2033    readTUDump($DumpPath);
2034
2035    # processing info
2036    setTemplateParams_All();
2037
2038    if($ExtraDump) {
2039        setAnonTypedef_All();
2040    }
2041
2042    getTypeInfo_All();
2043    simplifyNames();
2044    simplifyConstants();
2045    getVarInfo_All();
2046    getSymbolInfo_All();
2047
2048    # clean memory
2049    %LibInfo = ();
2050    %TemplateInstance = ();
2051    %BasicTemplate = ();
2052    %MangledNames = ();
2053    %TemplateDecl = ();
2054    %StdCxxTypedef = ();
2055    %MissedTypedef = ();
2056    %Typedef_Tr = ();
2057    %Typedef_Eq = ();
2058    %TypedefToAnon = ();
2059
2060    # clean cache
2061    delete($Cache{"getTypeAttr"});
2062    delete($Cache{"getTypeDeclId"});
2063
2064    if($ExtraDump)
2065    {
2066        remove_Unused($Version, "Extra");
2067    }
2068    else
2069    { # remove unused types
2070        if($BinaryOnly and not $ExtendedCheck)
2071        { # --binary
2072            remove_Unused($Version, "All");
2073        }
2074        else {
2075            remove_Unused($Version, "Extended");
2076        }
2077    }
2078
2079    if($CheckInfo)
2080    {
2081        foreach my $Tid (keys(%{$TypeInfo{$Version}})) {
2082            check_Completeness($TypeInfo{$Version}{$Tid}, $Version);
2083        }
2084
2085        foreach my $Sid (keys(%{$SymbolInfo{$Version}})) {
2086            check_Completeness($SymbolInfo{$Version}{$Sid}, $Version);
2087        }
2088    }
2089
2090    if($Debug) {
2091        # debugMangling($Version);
2092    }
2093}
2094
2095sub readTUDump($)
2096{
2097    my $DumpPath = $_[0];
2098
2099    open(TU_DUMP, $DumpPath);
2100    local $/ = undef;
2101    my $Content = <TU_DUMP>;
2102    close(TU_DUMP);
2103
2104    unlink($DumpPath);
2105
2106    $Content=~s/\n[ ]+/ /g;
2107    my @Lines = split(/\n/, $Content);
2108
2109    # clean memory
2110    undef $Content;
2111
2112    $MAX_ID = $#Lines+1; # number of lines == number of nodes
2113
2114    foreach (0 .. $#Lines)
2115    {
2116        if($Lines[$_]=~/\A\@(\d+)[ ]+([a-z_]+)[ ]+(.+)\Z/i)
2117        { # get a number and attributes of a node
2118            next if(not $NodeType{$2});
2119            $LibInfo{$Version}{"info_type"}{$1}=$2;
2120            $LibInfo{$Version}{"info"}{$1}=$3;
2121        }
2122
2123        # clean memory
2124        delete($Lines[$_]);
2125    }
2126
2127    # clean memory
2128    undef @Lines;
2129}
2130
2131sub simplifyConstants()
2132{
2133    foreach my $Constant (keys(%{$Constants{$Version}}))
2134    {
2135        if(defined $Constants{$Version}{$Constant}{"Header"})
2136        {
2137            my $Value = $Constants{$Version}{$Constant}{"Value"};
2138            if(defined $EnumConstants{$Version}{$Value}) {
2139                $Constants{$Version}{$Constant}{"Value"} = $EnumConstants{$Version}{$Value}{"Value"};
2140            }
2141        }
2142    }
2143}
2144
2145sub simplifyNames()
2146{
2147    foreach my $Base (keys(%{$Typedef_Tr{$Version}}))
2148    {
2149        if($Typedef_Eq{$Version}{$Base}) {
2150            next;
2151        }
2152        my @Translations = sort keys(%{$Typedef_Tr{$Version}{$Base}});
2153        if($#Translations==0)
2154        {
2155            if(length($Translations[0])<=length($Base)) {
2156                $Typedef_Eq{$Version}{$Base} = $Translations[0];
2157            }
2158        }
2159        else
2160        { # select most appropriate
2161            foreach my $Tr (@Translations)
2162            {
2163                if($Base=~/\A\Q$Tr\E/)
2164                {
2165                    $Typedef_Eq{$Version}{$Base} = $Tr;
2166                    last;
2167                }
2168            }
2169        }
2170    }
2171    foreach my $TypeId (keys(%{$TypeInfo{$Version}}))
2172    {
2173        my $TypeName = $TypeInfo{$Version}{$TypeId}{"Name"};
2174        if(not $TypeName) {
2175            next;
2176        }
2177        next if(index($TypeName,"<")==-1);# template instances only
2178        if($TypeName=~/>(::\w+)+\Z/)
2179        { # skip unused types
2180            next;
2181        }
2182        foreach my $Base (sort {length($b)<=>length($a)}
2183        sort {$b cmp $a} keys(%{$Typedef_Eq{$Version}}))
2184        {
2185            next if(not $Base);
2186            next if(index($TypeName,$Base)==-1);
2187            next if(length($TypeName) - length($Base) <= 3);
2188            if(my $Typedef = $Typedef_Eq{$Version}{$Base})
2189            {
2190                $TypeName=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g;
2191                $TypeName=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g;
2192                if(defined $TypeInfo{$Version}{$TypeId}{"TParam"})
2193                {
2194                    foreach my $TPos (keys(%{$TypeInfo{$Version}{$TypeId}{"TParam"}}))
2195                    {
2196                        if(my $TPName = $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"})
2197                        {
2198                            $TPName=~s/\A\Q$Base\E(\W|\Z)/$Typedef$1/g;
2199                            $TPName=~s/\A\Q$Base\E(\w|\Z)/$Typedef $1/g;
2200                            $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"} = formatName($TPName, "T");
2201                        }
2202                    }
2203                }
2204            }
2205        }
2206        $TypeName = formatName($TypeName, "T");
2207        $TypeInfo{$Version}{$TypeId}{"Name"} = $TypeName;
2208        $TName_Tid{$Version}{$TypeName} = $TypeId;
2209    }
2210}
2211
2212sub setAnonTypedef_All()
2213{
2214    foreach my $InfoId (keys(%{$LibInfo{$Version}{"info"}}))
2215    {
2216        if($LibInfo{$Version}{"info_type"}{$InfoId} eq "type_decl")
2217        {
2218            if(isAnon(getNameByInfo($InfoId))) {
2219                $TypedefToAnon{getTypeId($InfoId)} = 1;
2220            }
2221        }
2222    }
2223}
2224
2225sub setTemplateParams_All()
2226{
2227    foreach (keys(%{$LibInfo{$Version}{"info"}}))
2228    {
2229        if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") {
2230            setTemplateParams($_);
2231        }
2232    }
2233}
2234
2235sub setTemplateParams($)
2236{
2237    my $Tid = getTypeId($_[0]);
2238    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2239    {
2240        if($Info=~/(inst|spcs)[ ]*:[ ]*@(\d+) /)
2241        {
2242            my $TmplInst_Id = $2;
2243            setTemplateInstParams($_[0], $TmplInst_Id);
2244            while($TmplInst_Id = getNextElem($TmplInst_Id)) {
2245                setTemplateInstParams($_[0], $TmplInst_Id);
2246            }
2247        }
2248
2249        $BasicTemplate{$Version}{$Tid} = $_[0];
2250
2251        if(my $Prms = getTreeAttr_Prms($_[0]))
2252        {
2253            if(my $Valu = getTreeAttr_Valu($Prms))
2254            {
2255                my $Vector = getTreeVec($Valu);
2256                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Vector}))
2257                {
2258                    if(my $Val = getTreeAttr_Valu($Vector->{$Pos}))
2259                    {
2260                        if(my $Name = getNameByInfo($Val))
2261                        {
2262                            $TemplateArg{$Version}{$_[0]}{$Pos} = $Name;
2263                            if($LibInfo{$Version}{"info_type"}{$Val} eq "parm_decl") {
2264                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = $Val;
2265                            }
2266                            else {
2267                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = getTreeAttr_Type($Val);
2268                            }
2269                        }
2270                    }
2271                }
2272            }
2273        }
2274    }
2275    if(my $TypeId = getTreeAttr_Type($_[0]))
2276    {
2277        if(my $IType = $LibInfo{$Version}{"info_type"}{$TypeId})
2278        {
2279            if($IType eq "record_type") {
2280                $TemplateDecl{$Version}{$TypeId} = 1;
2281            }
2282        }
2283    }
2284}
2285
2286sub setTemplateInstParams($$)
2287{
2288    my ($Tmpl, $Inst) = @_;
2289
2290    if(my $Info = $LibInfo{$Version}{"info"}{$Inst})
2291    {
2292        my ($Params_InfoId, $ElemId) = ();
2293        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
2294            $Params_InfoId = $1;
2295        }
2296        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
2297            $ElemId = $1;
2298        }
2299        if($Params_InfoId and $ElemId)
2300        {
2301            my $Params_Info = $LibInfo{$Version}{"info"}{$Params_InfoId};
2302            while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
2303            {
2304                my ($PPos, $PTypeId) = ($1, $2);
2305                if(my $PType = $LibInfo{$Version}{"info_type"}{$PTypeId})
2306                {
2307                    if($PType eq "template_type_parm") {
2308                        $TemplateDecl{$Version}{$ElemId} = 1;
2309                    }
2310                }
2311                if($LibInfo{$Version}{"info_type"}{$ElemId} eq "function_decl")
2312                { # functions
2313                    $TemplateInstance{$Version}{"Func"}{$ElemId}{$PPos} = $PTypeId;
2314                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2315                }
2316                else
2317                { # types
2318                    $TemplateInstance{$Version}{"Type"}{$ElemId}{$PPos} = $PTypeId;
2319                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2320                }
2321            }
2322        }
2323    }
2324}
2325
2326sub getTypeDeclId($)
2327{
2328    if($_[0])
2329    {
2330        if(defined $Cache{"getTypeDeclId"}{$Version}{$_[0]}) {
2331            return $Cache{"getTypeDeclId"}{$Version}{$_[0]};
2332        }
2333        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2334        {
2335            if($Info=~/name[ ]*:[ ]*@(\d+)/) {
2336                return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = $1);
2337            }
2338        }
2339    }
2340    return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = 0);
2341}
2342
2343sub getTypeInfo_All()
2344{
2345    if(not check_gcc($GCC_PATH, "4.5"))
2346    { # support for GCC < 4.5
2347      # missed typedefs: QStyle::State is typedef to QFlags<QStyle::StateFlag>
2348      # but QStyleOption.state is of type QFlags<QStyle::StateFlag> in the TU dump
2349      # FIXME: check GCC versions
2350        addMissedTypes_Pre();
2351    }
2352
2353    foreach (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2354    { # forward order only
2355        my $IType = $LibInfo{$Version}{"info_type"}{$_};
2356        if($IType=~/_type\Z/ and $IType ne "function_type"
2357        and $IType ne "method_type") {
2358            getTypeInfo("$_");
2359        }
2360    }
2361
2362    # add "..." type
2363    $TypeInfo{$Version}{"-1"} = {
2364        "Name" => "...",
2365        "Type" => "Intrinsic",
2366        "Tid" => "-1"
2367    };
2368    $TName_Tid{$Version}{"..."} = "-1";
2369
2370    if(not check_gcc($GCC_PATH, "4.5"))
2371    { # support for GCC < 4.5
2372        addMissedTypes_Post();
2373    }
2374
2375    if($ADD_TMPL_INSTANCES)
2376    {
2377        # templates
2378        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}}))
2379        {
2380            if(defined $TemplateMap{$Version}{$Tid}
2381            and not defined $TypeInfo{$Version}{$Tid}{"Template"})
2382            {
2383                if(defined $TypeInfo{$Version}{$Tid}{"Memb"})
2384                {
2385                    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Memb"}}))
2386                    {
2387                        if(my $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"})
2388                        {
2389                            if(my %MAttr = getTypeAttr($MembTypeId))
2390                            {
2391                                $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"algn"} = $MAttr{"Algn"};
2392                                $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"} = instType($TemplateMap{$Version}{$Tid}, $MembTypeId, $Version);
2393                            }
2394                        }
2395                    }
2396                }
2397                if(defined $TypeInfo{$Version}{$Tid}{"Base"})
2398                {
2399                    foreach my $Bid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Base"}}))
2400                    {
2401                        my $NBid = instType($TemplateMap{$Version}{$Tid}, $Bid, $Version);
2402
2403                        if($NBid ne $Bid
2404                        and $NBid ne $Tid)
2405                        {
2406                            %{$TypeInfo{$Version}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$Version}{$Tid}{"Base"}{$Bid}};
2407                            delete($TypeInfo{$Version}{$Tid}{"Base"}{$Bid});
2408                        }
2409                    }
2410                }
2411            }
2412        }
2413    }
2414}
2415
2416sub createType($$)
2417{
2418    my ($Attr, $LibVersion) = @_;
2419    my $NewId = ++$MAX_ID;
2420
2421    $Attr->{"Tid"} = $NewId;
2422    $TypeInfo{$Version}{$NewId} = $Attr;
2423    $TName_Tid{$Version}{formatName($Attr->{"Name"}, "T")} = $NewId;
2424
2425    return "$NewId";
2426}
2427
2428sub instType($$$)
2429{ # create template instances
2430    my ($Map, $Tid, $LibVersion) = @_;
2431
2432    if(not $TypeInfo{$LibVersion}{$Tid}) {
2433        return undef;
2434    }
2435    my $Attr = dclone($TypeInfo{$LibVersion}{$Tid});
2436
2437    foreach my $Key (sort keys(%{$Map}))
2438    {
2439        if(my $Val = $Map->{$Key})
2440        {
2441            $Attr->{"Name"}=~s/\b$Key\b/$Val/g;
2442
2443            if(defined $Attr->{"NameSpace"}) {
2444                $Attr->{"NameSpace"}=~s/\b$Key\b/$Val/g;
2445            }
2446            foreach (keys(%{$Attr->{"TParam"}})) {
2447                $Attr->{"TParam"}{$_}{"name"}=~s/\b$Key\b/$Val/g;
2448            }
2449        }
2450        else
2451        { # remove absent
2452          # _Traits, etc.
2453            $Attr->{"Name"}=~s/,\s*\b$Key(,|>)/$1/g;
2454            if(defined $Attr->{"NameSpace"}) {
2455                $Attr->{"NameSpace"}=~s/,\s*\b$Key(,|>)/$1/g;
2456            }
2457            foreach (keys(%{$Attr->{"TParam"}}))
2458            {
2459                if($Attr->{"TParam"}{$_}{"name"} eq $Key) {
2460                    delete($Attr->{"TParam"}{$_});
2461                }
2462                else {
2463                    $Attr->{"TParam"}{$_}{"name"}=~s/,\s*\b$Key(,|>)/$1/g;
2464                }
2465            }
2466        }
2467    }
2468
2469    my $Tmpl = 0;
2470
2471    if(defined $Attr->{"TParam"})
2472    {
2473        foreach (sort {int($a)<=>int($b)} keys(%{$Attr->{"TParam"}}))
2474        {
2475            my $PName = $Attr->{"TParam"}{$_}{"name"};
2476
2477            if(my $PTid = $TName_Tid{$LibVersion}{$PName})
2478            {
2479                my %Base = get_BaseType($PTid, $LibVersion);
2480
2481                if($Base{"Type"} eq "TemplateParam"
2482                or defined $Base{"Template"})
2483                {
2484                    $Tmpl = 1;
2485                    last
2486                }
2487            }
2488        }
2489    }
2490
2491    if(my $Id = getTypeIdByName($Attr->{"Name"}, $LibVersion)) {
2492        return "$Id";
2493    }
2494    else
2495    {
2496        if(not $Tmpl) {
2497            delete($Attr->{"Template"});
2498        }
2499
2500        my $New = createType($Attr, $LibVersion);
2501
2502        my %EMap = ();
2503        if(defined $TemplateMap{$LibVersion}{$Tid}) {
2504            %EMap = %{$TemplateMap{$LibVersion}{$Tid}};
2505        }
2506        foreach (keys(%{$Map})) {
2507            $EMap{$_} = $Map->{$_};
2508        }
2509
2510        if(defined $TypeInfo{$LibVersion}{$New}{"BaseType"}) {
2511            $TypeInfo{$LibVersion}{$New}{"BaseType"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"BaseType"}, $LibVersion);
2512        }
2513        if(defined $TypeInfo{$LibVersion}{$New}{"Base"})
2514        {
2515            foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$New}{"Base"}}))
2516            {
2517                my $NBid = instType(\%EMap, $Bid, $LibVersion);
2518
2519                if($NBid ne $Bid
2520                and $NBid ne $New)
2521                {
2522                    %{$TypeInfo{$LibVersion}{$New}{"Base"}{$NBid}} = %{$TypeInfo{$LibVersion}{$New}{"Base"}{$Bid}};
2523                    delete($TypeInfo{$LibVersion}{$New}{"Base"}{$Bid});
2524                }
2525            }
2526        }
2527
2528        if(defined $TypeInfo{$LibVersion}{$New}{"Memb"})
2529        {
2530            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Memb"}}))
2531            {
2532                if(defined $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}) {
2533                    $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}, $LibVersion);
2534                }
2535            }
2536        }
2537
2538        if(defined $TypeInfo{$LibVersion}{$New}{"Param"})
2539        {
2540            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Param"}})) {
2541                $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"}, $LibVersion);
2542            }
2543        }
2544
2545        if(defined $TypeInfo{$LibVersion}{$New}{"Return"}) {
2546            $TypeInfo{$LibVersion}{$New}{"Return"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Return"}, $LibVersion);
2547        }
2548
2549        return $New;
2550    }
2551}
2552
2553sub addMissedTypes_Pre()
2554{
2555    my %MissedTypes = ();
2556    foreach my $MissedTDid (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2557    { # detecting missed typedefs
2558        if($LibInfo{$Version}{"info_type"}{$MissedTDid} eq "type_decl")
2559        {
2560            my $TypeId = getTreeAttr_Type($MissedTDid);
2561            next if(not $TypeId);
2562            my $TypeType = getTypeType($TypeId);
2563            if($TypeType eq "Unknown")
2564            { # template_type_parm
2565                next;
2566            }
2567            my $TypeDeclId = getTypeDeclId($TypeId);
2568            next if($TypeDeclId eq $MissedTDid);#or not $TypeDeclId
2569            my $TypedefName = getNameByInfo($MissedTDid);
2570            next if(not $TypedefName);
2571            next if($TypedefName eq "__float80");
2572            next if(isAnon($TypedefName));
2573            if(not $TypeDeclId
2574            or getNameByInfo($TypeDeclId) ne $TypedefName) {
2575                $MissedTypes{$Version}{$TypeId}{$MissedTDid} = 1;
2576            }
2577        }
2578    }
2579    my %AddTypes = ();
2580    foreach my $Tid (keys(%{$MissedTypes{$Version}}))
2581    { # add missed typedefs
2582        my @Missed = keys(%{$MissedTypes{$Version}{$Tid}});
2583        if(not @Missed or $#Missed>=1) {
2584            next;
2585        }
2586        my $MissedTDid = $Missed[0];
2587        my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid);
2588        if(not $TypedefName) {
2589            next;
2590        }
2591        my $NewId = ++$MAX_ID;
2592        my %MissedInfo = ( # typedef info
2593            "Name" => $TypedefName,
2594            "NameSpace" => $TypedefNS,
2595            "BaseType" => $Tid,
2596            "Type" => "Typedef",
2597            "Tid" => "$NewId" );
2598        my ($H, $L) = getLocation($MissedTDid);
2599        $MissedInfo{"Header"} = $H;
2600        $MissedInfo{"Line"} = $L;
2601        if($TypedefName=~/\*|\&|\s/)
2602        { # other types
2603            next;
2604        }
2605        if($TypedefName=~/>(::\w+)+\Z/)
2606        { # QFlags<Qt::DropAction>::enum_type
2607            next;
2608        }
2609        if(getTypeType($Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/)
2610        { # double-check for the name of typedef
2611            my ($TName, $TNS) = getTrivialName(getTypeDeclId($Tid), $Tid); # base type info
2612            next if(not $TName);
2613            if(length($TypedefName)>=length($TName))
2614            { # too long typedef
2615                next;
2616            }
2617            if($TName=~/\A\Q$TypedefName\E</) {
2618                next;
2619            }
2620            if($TypedefName=~/\A\Q$TName\E/)
2621            { # QDateTimeEdit::Section and QDateTimeEdit::Sections::enum_type
2622                next;
2623            }
2624            if(get_depth($TypedefName)==0 and get_depth($TName)!=0)
2625            { # std::_Vector_base and std::vector::_Base
2626                next;
2627            }
2628        }
2629
2630        $AddTypes{$MissedInfo{"Tid"}} = \%MissedInfo;
2631
2632        # register typedef
2633        $MissedTypedef{$Version}{$Tid}{"Tid"} = $MissedInfo{"Tid"};
2634        $MissedTypedef{$Version}{$Tid}{"TDid"} = $MissedTDid;
2635        $TName_Tid{$Version}{$TypedefName} = $MissedInfo{"Tid"};
2636    }
2637
2638    # add missed & remove other
2639    $TypeInfo{$Version} = \%AddTypes;
2640    delete($Cache{"getTypeAttr"}{$Version});
2641}
2642
2643sub addMissedTypes_Post()
2644{
2645    foreach my $BaseId (keys(%{$MissedTypedef{$Version}}))
2646    {
2647        if(my $Tid = $MissedTypedef{$Version}{$BaseId}{"Tid"})
2648        {
2649            $TypeInfo{$Version}{$Tid}{"Size"} = $TypeInfo{$Version}{$BaseId}{"Size"};
2650            if(my $TName = $TypeInfo{$Version}{$Tid}{"Name"}) {
2651                $Typedef_BaseName{$Version}{$TName} = $TypeInfo{$Version}{$BaseId}{"Name"};
2652            }
2653        }
2654    }
2655}
2656
2657sub getTypeInfo($)
2658{
2659    my $TypeId = $_[0];
2660    %{$TypeInfo{$Version}{$TypeId}} = getTypeAttr($TypeId);
2661    my $TName = $TypeInfo{$Version}{$TypeId}{"Name"};
2662    if(not $TName) {
2663        delete($TypeInfo{$Version}{$TypeId});
2664    }
2665}
2666
2667sub getArraySize($$)
2668{
2669    my ($TypeId, $BaseName) = @_;
2670    if(my $Size = getSize($TypeId))
2671    {
2672        my $Elems = $Size/$BYTE_SIZE;
2673        while($BaseName=~s/\s*\[(\d+)\]//) {
2674            $Elems/=$1;
2675        }
2676        if(my $BasicId = $TName_Tid{$Version}{$BaseName})
2677        {
2678            if(my $BasicSize = $TypeInfo{$Version}{$BasicId}{"Size"}) {
2679                $Elems/=$BasicSize;
2680            }
2681        }
2682        return $Elems;
2683    }
2684    return 0;
2685}
2686
2687sub getTParams($$)
2688{
2689    my ($TypeId, $Kind) = @_;
2690    my @TmplParams = ();
2691    my @Positions = sort {int($a)<=>int($b)} keys(%{$TemplateInstance{$Version}{$Kind}{$TypeId}});
2692    foreach my $Pos (@Positions)
2693    {
2694        my $Param_TypeId = $TemplateInstance{$Version}{$Kind}{$TypeId}{$Pos};
2695        my $NodeType = $LibInfo{$Version}{"info_type"}{$Param_TypeId};
2696        if(not $NodeType)
2697        { # typename_type
2698            return ();
2699        }
2700        if($NodeType eq "tree_vec")
2701        {
2702            if($Pos!=$#Positions)
2703            { # select last vector of parameters ( ns<P1>::type<P2> )
2704                next;
2705            }
2706        }
2707        my @Params = get_TemplateParam($Pos, $Param_TypeId);
2708        foreach my $P (@Params)
2709        {
2710            if($P eq "") {
2711                return ();
2712            }
2713            elsif($P ne "\@skip\@") {
2714                @TmplParams = (@TmplParams, $P);
2715            }
2716        }
2717    }
2718    return @TmplParams;
2719}
2720
2721sub getTypeAttr($)
2722{
2723    my $TypeId = $_[0];
2724    my %TypeAttr = ();
2725    if(defined $TypeInfo{$Version}{$TypeId}
2726    and $TypeInfo{$Version}{$TypeId}{"Name"})
2727    { # already created
2728        return %{$TypeInfo{$Version}{$TypeId}};
2729    }
2730    elsif($Cache{"getTypeAttr"}{$Version}{$TypeId})
2731    { # incomplete type
2732        return ();
2733    }
2734    $Cache{"getTypeAttr"}{$Version}{$TypeId} = 1;
2735
2736    my $TypeDeclId = getTypeDeclId($TypeId);
2737    $TypeAttr{"Tid"} = $TypeId;
2738
2739    if(not $MissedBase{$Version}{$TypeId} and isTypedef($TypeId))
2740    {
2741        if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
2742        {
2743            if($Info=~/qual[ ]*:/)
2744            {
2745                my $NewId = ++$MAX_ID;
2746
2747                $MissedBase{$Version}{$TypeId} = "$NewId";
2748                $MissedBase_R{$Version}{$NewId} = $TypeId;
2749                $LibInfo{$Version}{"info"}{$NewId} = $LibInfo{$Version}{"info"}{$TypeId};
2750                $LibInfo{$Version}{"info_type"}{$NewId} = $LibInfo{$Version}{"info_type"}{$TypeId};
2751            }
2752        }
2753        $TypeAttr{"Type"} = "Typedef";
2754    }
2755    else {
2756        $TypeAttr{"Type"} = getTypeType($TypeId);
2757    }
2758
2759    if(my $ScopeId = getTreeAttr_Scpe($TypeDeclId))
2760    {
2761        if($LibInfo{$Version}{"info_type"}{$ScopeId} eq "function_decl")
2762        { # local code
2763            return ();
2764        }
2765    }
2766
2767    if($TypeAttr{"Type"} eq "Unknown") {
2768        return ();
2769    }
2770    elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/)
2771    {
2772        %TypeAttr = getMemPtrAttr(pointTo($TypeId), $TypeId, $TypeAttr{"Type"});
2773        if(my $TName = $TypeAttr{"Name"})
2774        {
2775            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2776            $TName_Tid{$Version}{$TName} = $TypeId;
2777            return %TypeAttr;
2778        }
2779        else {
2780            return ();
2781        }
2782    }
2783    elsif($TypeAttr{"Type"} eq "Array")
2784    {
2785        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2786        if(not $BTid) {
2787            return ();
2788        }
2789        if(my $Algn = getAlgn($TypeId)) {
2790            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
2791        }
2792        $TypeAttr{"BaseType"} = $BTid;
2793        if(my %BTAttr = getTypeAttr($BTid))
2794        {
2795            if(not $BTAttr{"Name"}) {
2796                return ();
2797            }
2798            if(my $NElems = getArraySize($TypeId, $BTAttr{"Name"}))
2799            {
2800                if(my $Size = getSize($TypeId)) {
2801                    $TypeAttr{"Size"} = $Size/$BYTE_SIZE;
2802                }
2803                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2804                    $TypeAttr{"Name"} = $1."[$NElems]".$2;
2805                }
2806                else {
2807                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[$NElems]";
2808                }
2809            }
2810            else
2811            {
2812                $TypeAttr{"Size"} = $WORD_SIZE{$Version}; # pointer
2813                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2814                    $TypeAttr{"Name"} = $1."[]".$2;
2815                }
2816                else {
2817                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[]";
2818                }
2819            }
2820            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
2821            if($BTAttr{"Header"})  {
2822                $TypeAttr{"Header"} = $BTAttr{"Header"};
2823            }
2824            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2825            $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2826            return %TypeAttr;
2827        }
2828        return ();
2829    }
2830    elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class|Vector)\Z/)
2831    {
2832        %TypeAttr = getTrivialTypeAttr($TypeId);
2833        if($TypeAttr{"Name"})
2834        {
2835            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2836
2837            if(not defined $IntrinsicNames{$TypeAttr{"Name"}}
2838            or getTypeDeclId($TypeAttr{"Tid"}))
2839            { # NOTE: register only one int: with built-in decl
2840                if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2841                    $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2842                }
2843            }
2844            return %TypeAttr;
2845        }
2846        else {
2847            return ();
2848        }
2849    }
2850    elsif($TypeAttr{"Type"}=~/TemplateParam|TypeName/)
2851    {
2852        %TypeAttr = getTrivialTypeAttr($TypeId);
2853        if($TypeAttr{"Name"})
2854        {
2855            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2856            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2857                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2858            }
2859            return %TypeAttr;
2860        }
2861        else {
2862            return ();
2863        }
2864    }
2865    elsif($TypeAttr{"Type"} eq "SizeOf")
2866    {
2867        $TypeAttr{"BaseType"} = getTreeAttr_Type($TypeId);
2868        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2869        $TypeAttr{"Name"} = "sizeof(".$BTAttr{"Name"}.")";
2870        if($TypeAttr{"Name"})
2871        {
2872            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2873            return %TypeAttr;
2874        }
2875        else {
2876            return ();
2877        }
2878    }
2879    else
2880    { # derived types
2881        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2882        if(not $BTid) {
2883            return ();
2884        }
2885        $TypeAttr{"BaseType"} = $BTid;
2886        if(defined $MissedTypedef{$Version}{$BTid})
2887        {
2888            if(my $MissedTDid = $MissedTypedef{$Version}{$BTid}{"TDid"})
2889            {
2890                if($MissedTDid ne $TypeDeclId) {
2891                    $TypeAttr{"BaseType"} = $MissedTypedef{$Version}{$BTid}{"Tid"};
2892                }
2893            }
2894        }
2895        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2896        if(not $BTAttr{"Name"})
2897        { # templates
2898            return ();
2899        }
2900        if($BTAttr{"Type"} eq "Typedef")
2901        { # relinking typedefs
2902            my %BaseBase = get_Type($BTAttr{"BaseType"}, $Version);
2903            if($BTAttr{"Name"} eq $BaseBase{"Name"}) {
2904                $TypeAttr{"BaseType"} = $BaseBase{"Tid"};
2905            }
2906        }
2907        if($BTSpec)
2908        {
2909            if($TypeAttr{"Type"} eq "Pointer"
2910            and $BTAttr{"Name"}=~/\([\*]+\)/)
2911            {
2912                $TypeAttr{"Name"} = $BTAttr{"Name"};
2913                $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g;
2914            }
2915            else {
2916                $TypeAttr{"Name"} = $BTAttr{"Name"}." ".$BTSpec;
2917            }
2918        }
2919        else {
2920            $TypeAttr{"Name"} = $BTAttr{"Name"};
2921        }
2922        if($TypeAttr{"Type"} eq "Typedef")
2923        {
2924            $TypeAttr{"Name"} = getNameByInfo($TypeDeclId);
2925
2926            if(index($TypeAttr{"Name"}, "tmp_add_type")==0) {
2927                return ();
2928            }
2929
2930            if(isAnon($TypeAttr{"Name"}))
2931            { # anon typedef to anon type: ._N
2932                return ();
2933            }
2934
2935            if($LibInfo{$Version}{"info"}{$TypeDeclId}=~/ artificial /i)
2936            { # artificial typedef of "struct X" to "X"
2937                $TypeAttr{"Artificial"} = 1;
2938            }
2939
2940            if(my $NS = getNameSpace($TypeDeclId))
2941            {
2942                my $TypeName = $TypeAttr{"Name"};
2943                if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/)
2944                { # "some_type" is the typedef to "struct some_type" in C++
2945                    if($3) {
2946                        $TypeAttr{"Name"} = $3."::".$TypeName;
2947                    }
2948                }
2949                else
2950                {
2951                    $TypeAttr{"NameSpace"} = $NS;
2952                    $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
2953
2954                    if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/
2955                    and $TypeAttr{"Name"}!~/>(::\w+)+\Z/)
2956                    {
2957                        if($BTAttr{"NameSpace"}
2958                        and $BTAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr{"Name"}=~/</)
2959                        { # types like "std::fpos<__mbstate_t>" are
2960                          # not covered by typedefs in the TU dump
2961                          # so trying to add such typedefs manually
2962                            $StdCxxTypedef{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
2963                            if(length($TypeAttr{"Name"})<=length($BTAttr{"Name"}))
2964                            {
2965                                if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/))
2966                                { # skip "other" in "std" and "type" in "boost"
2967                                    $Typedef_Eq{$Version}{$BTAttr{"Name"}} = $TypeAttr{"Name"};
2968                                }
2969                            }
2970                        }
2971                    }
2972                }
2973            }
2974            if($TypeAttr{"Name"} ne $BTAttr{"Name"} and not $TypeAttr{"Artificial"}
2975            and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BTAttr{"Name"}!~/>(::\w+)+\Z/)
2976            {
2977                if(not defined $Typedef_BaseName{$Version}{$TypeAttr{"Name"}})
2978                { # typedef int*const TYPEDEF; // first
2979                  # int foo(TYPEDEF p); // const is optimized out
2980                    $Typedef_BaseName{$Version}{$TypeAttr{"Name"}} = $BTAttr{"Name"};
2981                    if($BTAttr{"Name"}=~/</)
2982                    {
2983                        if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) {
2984                            $Typedef_Tr{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
2985                        }
2986                    }
2987                }
2988            }
2989            ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId);
2990        }
2991        if(not $TypeAttr{"Size"})
2992        {
2993            if($TypeAttr{"Type"} eq "Pointer") {
2994                $TypeAttr{"Size"} = $WORD_SIZE{$Version};
2995            }
2996            elsif($BTAttr{"Size"}) {
2997                $TypeAttr{"Size"} = $BTAttr{"Size"};
2998            }
2999        }
3000        if(my $Algn = getAlgn($TypeId)) {
3001            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3002        }
3003        $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3004        if(not $TypeAttr{"Header"} and $BTAttr{"Header"})  {
3005            $TypeAttr{"Header"} = $BTAttr{"Header"};
3006        }
3007        %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
3008        if($TypeAttr{"Name"} ne $BTAttr{"Name"})
3009        { # typedef to "class Class"
3010          # should not be registered in TName_Tid
3011            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
3012                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
3013            }
3014        }
3015        return %TypeAttr;
3016    }
3017}
3018
3019sub getTreeVec($)
3020{
3021    my %Vector = ();
3022    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3023    {
3024        while($Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
3025        { # string length is N-1 because of the null terminator
3026            $Vector{$1} = $2;
3027        }
3028    }
3029    return \%Vector;
3030}
3031
3032sub get_TemplateParam($$)
3033{
3034    my ($Pos, $Type_Id) = @_;
3035    return () if(not $Type_Id);
3036    my $NodeType = $LibInfo{$Version}{"info_type"}{$Type_Id};
3037    return () if(not $NodeType);
3038    if($NodeType eq "integer_cst")
3039    { # int (1), unsigned (2u), char ('c' as 99), ...
3040        my $CstTid = getTreeAttr_Type($Type_Id);
3041        my %CstType = getTypeAttr($CstTid); # without recursion
3042        my $Num = getNodeIntCst($Type_Id);
3043        if(my $CstSuffix = $ConstantSuffix{$CstType{"Name"}}) {
3044            return ($Num.$CstSuffix);
3045        }
3046        else {
3047            return ("(".$CstType{"Name"}.")".$Num);
3048        }
3049    }
3050    elsif($NodeType eq "string_cst") {
3051        return (getNodeStrCst($Type_Id));
3052    }
3053    elsif($NodeType eq "tree_vec")
3054    {
3055        my $Vector = getTreeVec($Type_Id);
3056        my @Params = ();
3057        foreach my $P1 (sort {int($a)<=>int($b)} keys(%{$Vector}))
3058        {
3059            foreach my $P2 (get_TemplateParam($Pos, $Vector->{$P1})) {
3060                push(@Params, $P2);
3061            }
3062        }
3063        return @Params;
3064    }
3065    elsif($NodeType eq "parm_decl")
3066    {
3067        (getNameByInfo($Type_Id));
3068    }
3069    else
3070    {
3071        my %ParamAttr = getTypeAttr($Type_Id);
3072        my $PName = $ParamAttr{"Name"};
3073        if(not $PName) {
3074            return ();
3075        }
3076        if($PName=~/\>/)
3077        {
3078            if(my $Cover = cover_stdcxx_typedef($PName)) {
3079                $PName = $Cover;
3080            }
3081        }
3082        if($Pos>=1 and
3083        $PName=~/\A$DEFAULT_STD_PARMS\</)
3084        { # template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
3085          # template<typename _Key, typename _Compare = std::less<_Key>
3086          # template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
3087          # template<typename _Ch_type, typename _Rx_traits = regex_traits<_Ch_type> >
3088          # template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
3089          # template<typename _CharT, typename _OutIter = ostreambuf_iterator<_CharT> >
3090            return ("\@skip\@");
3091        }
3092        return ($PName);
3093    }
3094}
3095
3096sub cover_stdcxx_typedef($)
3097{
3098    my $TypeName = $_[0];
3099    if(my @Covers = sort {length($a)<=>length($b)}
3100    sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3101    { # take the shortest typedef
3102      # FIXME: there may be more than
3103      # one typedefs to the same type
3104        return $Covers[0];
3105    }
3106    my $Covered = $TypeName;
3107    while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){};
3108    if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3109    {
3110        if(my $Cover = $Covers[0])
3111        {
3112            $Covered=~s/\b\Q$TypeName\E(\W|\Z)/$Cover$1/g;
3113            $Covered=~s/\b\Q$TypeName\E(\w|\Z)/$Cover $1/g;
3114        }
3115    }
3116    return formatName($Covered, "T");
3117}
3118
3119sub getNodeIntCst($)
3120{
3121    my $CstId = $_[0];
3122    my $CstTypeId = getTreeAttr_Type($CstId);
3123    if($EnumMembName_Id{$Version}{$CstId}) {
3124        return $EnumMembName_Id{$Version}{$CstId};
3125    }
3126    elsif((my $Value = getTreeValue($CstId)) ne "")
3127    {
3128        if($Value eq "0")
3129        {
3130            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3131                return "false";
3132            }
3133            else {
3134                return "0";
3135            }
3136        }
3137        elsif($Value eq "1")
3138        {
3139            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3140                return "true";
3141            }
3142            else {
3143                return "1";
3144            }
3145        }
3146        else {
3147            return $Value;
3148        }
3149    }
3150    return "";
3151}
3152
3153sub getNodeStrCst($)
3154{
3155    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3156    {
3157        if($Info=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/)
3158        {
3159            if($LibInfo{$Version}{"info_type"}{$_[0]} eq "string_cst")
3160            { # string length is N-1 because of the null terminator
3161                return substr($1, 0, $2-1);
3162            }
3163            else
3164            { # identifier_node
3165                return substr($1, 0, $2);
3166            }
3167        }
3168    }
3169    return "";
3170}
3171
3172sub getMemPtrAttr($$$)
3173{ # function, method and field pointers
3174    my ($PtrId, $TypeId, $Type) = @_;
3175    my $MemInfo = $LibInfo{$Version}{"info"}{$PtrId};
3176    if($Type eq "FieldPtr") {
3177        $MemInfo = $LibInfo{$Version}{"info"}{$TypeId};
3178    }
3179    my $MemInfo_Type = $LibInfo{$Version}{"info_type"}{$PtrId};
3180    my $MemPtrName = "";
3181    my %TypeAttr = ("Size"=>$WORD_SIZE{$Version}, "Type"=>$Type, "Tid"=>$TypeId);
3182    if($Type eq "MethodPtr")
3183    { # size of "method pointer" may be greater than WORD size
3184        if(my $Size = getSize($TypeId))
3185        {
3186            $Size/=$BYTE_SIZE;
3187            $TypeAttr{"Size"} = "$Size";
3188        }
3189    }
3190    if(my $Algn = getAlgn($TypeId)) {
3191        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3192    }
3193    # Return
3194    if($Type eq "FieldPtr")
3195    {
3196        my %ReturnAttr = getTypeAttr($PtrId);
3197        if($ReturnAttr{"Name"}) {
3198            $MemPtrName .= $ReturnAttr{"Name"};
3199        }
3200        $TypeAttr{"Return"} = $PtrId;
3201    }
3202    else
3203    {
3204        if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /)
3205        {
3206            my $ReturnTypeId = $1;
3207            my %ReturnAttr = getTypeAttr($ReturnTypeId);
3208            if(not $ReturnAttr{"Name"})
3209            { # templates
3210                return ();
3211            }
3212            $MemPtrName .= $ReturnAttr{"Name"};
3213            $TypeAttr{"Return"} = $ReturnTypeId;
3214        }
3215    }
3216    # Class
3217    if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /)
3218    {
3219        $TypeAttr{"Class"} = $2;
3220        my %Class = getTypeAttr($TypeAttr{"Class"});
3221        if($Class{"Name"}) {
3222            $MemPtrName .= " (".$Class{"Name"}."\:\:*)";
3223        }
3224        else {
3225            $MemPtrName .= " (*)";
3226        }
3227    }
3228    else {
3229        $MemPtrName .= " (*)";
3230    }
3231    # Parameters
3232    if($Type eq "FuncPtr"
3233    or $Type eq "MethodPtr")
3234    {
3235        my @ParamTypeName = ();
3236        if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /)
3237        {
3238            my $PTypeInfoId = $1;
3239            my ($Pos, $PPos) = (0, 0);
3240            while($PTypeInfoId)
3241            {
3242                my $PTypeInfo = $LibInfo{$Version}{"info"}{$PTypeInfoId};
3243                if($PTypeInfo=~/valu[ ]*:[ ]*@(\d+) /)
3244                {
3245                    my $PTypeId = $1;
3246                    my %ParamAttr = getTypeAttr($PTypeId);
3247                    if(not $ParamAttr{"Name"})
3248                    { # templates (template_type_parm), etc.
3249                        return ();
3250                    }
3251                    if($ParamAttr{"Name"} eq "void") {
3252                        last;
3253                    }
3254                    if($Pos!=0 or $Type ne "MethodPtr")
3255                    {
3256                        $TypeAttr{"Param"}{$PPos++}{"type"} = $PTypeId;
3257                        push(@ParamTypeName, $ParamAttr{"Name"});
3258                    }
3259                    if($PTypeInfoId = getNextElem($PTypeInfoId)) {
3260                        $Pos+=1;
3261                    }
3262                    else {
3263                        last;
3264                    }
3265                }
3266                else {
3267                    last;
3268                }
3269            }
3270        }
3271        $MemPtrName .= " (".join(", ", @ParamTypeName).")";
3272    }
3273    $TypeAttr{"Name"} = formatName($MemPtrName, "T");
3274    return %TypeAttr;
3275}
3276
3277sub getTreeTypeName($)
3278{
3279    my $TypeId = $_[0];
3280    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3281    {
3282        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "integer_type")
3283        {
3284            if(my $Name = getNameByInfo($TypeId))
3285            { # bit_size_type
3286                return $Name;
3287            }
3288            elsif($Info=~/unsigned/) {
3289                return "unsigned int";
3290            }
3291            else {
3292                return "int";
3293            }
3294        }
3295        elsif($Info=~/name[ ]*:[ ]*@(\d+) /) {
3296            return getNameByInfo($1);
3297        }
3298    }
3299    return "";
3300}
3301
3302sub isFuncPtr($)
3303{
3304    my $Ptd = pointTo($_[0]);
3305    return 0 if(not $Ptd);
3306    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3307    {
3308        if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/) {
3309            return 0;
3310        }
3311    }
3312    if(my $InfoT1 = $LibInfo{$Version}{"info_type"}{$_[0]}
3313    and my $InfoT2 = $LibInfo{$Version}{"info_type"}{$Ptd})
3314    {
3315        if($InfoT1 eq "pointer_type"
3316        and $InfoT2 eq "function_type") {
3317            return 1;
3318        }
3319    }
3320    return 0;
3321}
3322
3323sub isMethodPtr($)
3324{
3325    my $Ptd = pointTo($_[0]);
3326    return 0 if(not $Ptd);
3327    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3328    {
3329        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "record_type"
3330        and $LibInfo{$Version}{"info_type"}{$Ptd} eq "method_type"
3331        and $Info=~/ ptrmem /) {
3332            return 1;
3333        }
3334    }
3335    return 0;
3336}
3337
3338sub isFieldPtr($)
3339{
3340    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3341    {
3342        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "offset_type"
3343        and $Info=~/ ptrmem /) {
3344            return 1;
3345        }
3346    }
3347    return 0;
3348}
3349
3350sub pointTo($)
3351{
3352    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3353    {
3354        if($Info=~/ptd[ ]*:[ ]*@(\d+)/) {
3355            return $1;
3356        }
3357    }
3358    return "";
3359}
3360
3361sub getTypeTypeByTypeId($)
3362{
3363    my $TypeId = $_[0];
3364    if(my $TType = $LibInfo{$Version}{"info_type"}{$TypeId})
3365    {
3366        my $NType = $NodeType{$TType};
3367        if($NType eq "Intrinsic") {
3368            return $NType;
3369        }
3370        elsif(isFuncPtr($TypeId)) {
3371            return "FuncPtr";
3372        }
3373        elsif(isMethodPtr($TypeId)) {
3374            return "MethodPtr";
3375        }
3376        elsif(isFieldPtr($TypeId)) {
3377            return "FieldPtr";
3378        }
3379        elsif($NType ne "Other") {
3380            return $NType;
3381        }
3382    }
3383    return "Unknown";
3384}
3385
3386my %UnQual = (
3387    "r"=>"restrict",
3388    "v"=>"volatile",
3389    "c"=>"const",
3390    "cv"=>"const volatile"
3391);
3392
3393sub getQual($)
3394{
3395    my $TypeId = $_[0];
3396    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3397    {
3398        my ($Qual, $To) = ();
3399        if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) {
3400            $Qual = $UnQual{$1};
3401        }
3402        if($Info=~/unql[ ]*:[ ]*\@(\d+)/) {
3403            $To = $1;
3404        }
3405        if($Qual and $To) {
3406            return ($Qual, $To);
3407        }
3408    }
3409    return ();
3410}
3411
3412sub getQualType($)
3413{
3414    if($_[0] eq "const volatile") {
3415        return "ConstVolatile";
3416    }
3417    return ucfirst($_[0]);
3418}
3419
3420sub getTypeType($)
3421{
3422    my $TypeId = $_[0];
3423    my $TypeDeclId = getTypeDeclId($TypeId);
3424    if(defined $MissedTypedef{$Version}{$TypeId})
3425    { # support for old GCC versions
3426        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId) {
3427            return "Typedef";
3428        }
3429    }
3430    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3431    my ($Qual, $To) = getQual($TypeId);
3432    if(($Qual or $To) and $TypeDeclId
3433    and (getTypeId($TypeDeclId) ne $TypeId))
3434    { # qualified types (special)
3435        return getQualType($Qual);
3436    }
3437    elsif(not $MissedBase_R{$Version}{$TypeId}
3438    and isTypedef($TypeId)) {
3439        return "Typedef";
3440    }
3441    elsif($Qual)
3442    { # qualified types
3443        return getQualType($Qual);
3444    }
3445
3446    if($Info=~/unql[ ]*:[ ]*\@(\d+)/)
3447    { # typedef struct { ... } name
3448        $TypeTypedef{$Version}{$TypeId} = $1;
3449    }
3450
3451    my $TypeType = getTypeTypeByTypeId($TypeId);
3452    if($TypeType eq "Struct")
3453    {
3454        if($TypeDeclId
3455        and $LibInfo{$Version}{"info_type"}{$TypeDeclId} eq "template_decl") {
3456            return "Template";
3457        }
3458    }
3459    return $TypeType;
3460}
3461
3462sub isTypedef($)
3463{
3464    if($_[0])
3465    {
3466        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "vector_type")
3467        { # typedef float La_x86_64_xmm __attribute__ ((__vector_size__ (16)));
3468            return 0;
3469        }
3470        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3471        {
3472            if(my $TDid = getTypeDeclId($_[0]))
3473            {
3474                if(getTypeId($TDid) eq $_[0]
3475                and getNameByInfo($TDid))
3476                {
3477                    if($Info=~/unql[ ]*:[ ]*\@(\d+) /) {
3478                        return $1;
3479                    }
3480                }
3481            }
3482        }
3483    }
3484    return 0;
3485}
3486
3487sub selectBaseType($)
3488{
3489    my $TypeId = $_[0];
3490    if(defined $MissedTypedef{$Version}{$TypeId})
3491    { # add missed typedefs
3492        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq getTypeDeclId($TypeId)) {
3493            return ($TypeId, "");
3494        }
3495    }
3496    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3497    my $InfoType = $LibInfo{$Version}{"info_type"}{$TypeId};
3498
3499    my $MB_R = $MissedBase_R{$Version}{$TypeId};
3500    my $MB = $MissedBase{$Version}{$TypeId};
3501
3502    my ($Qual, $To) = getQual($TypeId);
3503    if(($Qual or $To) and $Info=~/name[ ]*:[ ]*\@(\d+) /
3504    and (getTypeId($1) ne $TypeId)
3505    and (not $MB_R or getTypeId($1) ne $MB_R))
3506    { # qualified types (special)
3507        return (getTypeId($1), $Qual);
3508    }
3509    elsif($MB)
3510    { # add base
3511        return ($MB, "");
3512    }
3513    elsif(not $MB_R and my $Bid = isTypedef($TypeId))
3514    { # typedefs
3515        return ($Bid, "");
3516    }
3517    elsif($Qual or $To)
3518    { # qualified types
3519        return ($To, $Qual);
3520    }
3521    elsif($InfoType eq "reference_type")
3522    {
3523        if($Info=~/refd[ ]*:[ ]*@(\d+) /) {
3524            return ($1, "&");
3525        }
3526    }
3527    elsif($InfoType eq "array_type")
3528    {
3529        if($Info=~/elts[ ]*:[ ]*@(\d+) /) {
3530            return ($1, "");
3531        }
3532    }
3533    elsif($InfoType eq "pointer_type")
3534    {
3535        if($Info=~/ptd[ ]*:[ ]*@(\d+) /) {
3536            return ($1, "*");
3537        }
3538    }
3539
3540    return (0, "");
3541}
3542
3543sub getSymbolInfo_All()
3544{
3545    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3546    { # reverse order
3547        if($LibInfo{$Version}{"info_type"}{$_} eq "function_decl") {
3548            getSymbolInfo($_);
3549        }
3550    }
3551
3552    if($ADD_TMPL_INSTANCES)
3553    {
3554        # templates
3555        foreach my $Sid (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$Version}}))
3556        {
3557            my %Map = ();
3558
3559            if(my $ClassId = $SymbolInfo{$Version}{$Sid}{"Class"})
3560            {
3561                if(defined $TemplateMap{$Version}{$ClassId})
3562                {
3563                    foreach (keys(%{$TemplateMap{$Version}{$ClassId}})) {
3564                        $Map{$_} = $TemplateMap{$Version}{$ClassId}{$_};
3565                    }
3566                }
3567            }
3568
3569            if(defined $TemplateMap{$Version}{$Sid})
3570            {
3571                foreach (keys(%{$TemplateMap{$Version}{$Sid}})) {
3572                    $Map{$_} = $TemplateMap{$Version}{$Sid}{$_};
3573                }
3574            }
3575
3576            if(defined $SymbolInfo{$Version}{$Sid}{"Param"})
3577            {
3578                foreach (keys(%{$SymbolInfo{$Version}{$Sid}{"Param"}}))
3579                {
3580                    my $PTid = $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"};
3581                    $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"} = instType(\%Map, $PTid, $Version);
3582                }
3583            }
3584            if(my $Return = $SymbolInfo{$Version}{$Sid}{"Return"}) {
3585                $SymbolInfo{$Version}{$Sid}{"Return"} = instType(\%Map, $Return, $Version);
3586            }
3587        }
3588    }
3589}
3590
3591sub getVarInfo_All()
3592{
3593    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3594    { # reverse order
3595        if($LibInfo{$Version}{"info_type"}{$_} eq "var_decl") {
3596            getVarInfo($_);
3597        }
3598    }
3599}
3600
3601sub isBuiltIn($) {
3602    return ($_[0] and $_[0]=~/\<built\-in\>|\<internal\>|\A\./);
3603}
3604
3605sub getVarInfo($)
3606{
3607    my $InfoId = $_[0];
3608    if(my $NSid = getTreeAttr_Scpe($InfoId))
3609    {
3610        my $NSInfoType = $LibInfo{$Version}{"info_type"}{$NSid};
3611        if($NSInfoType and $NSInfoType eq "function_decl") {
3612            return;
3613        }
3614    }
3615    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
3616    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
3617    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) {
3618        delete($SymbolInfo{$Version}{$InfoId});
3619        return;
3620    }
3621    my $ShortName = getTreeStr(getTreeAttr_Name($InfoId));
3622    if(not $ShortName) {
3623        delete($SymbolInfo{$Version}{$InfoId});
3624        return;
3625    }
3626    if($ShortName=~/\Atmp_add_class_\d+\Z/) {
3627        delete($SymbolInfo{$Version}{$InfoId});
3628        return;
3629    }
3630    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = $ShortName;
3631    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
3632    {
3633        if($OSgroup eq "windows")
3634        { # cut the offset
3635            $MnglName=~s/\@\d+\Z//g;
3636        }
3637        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
3638    }
3639    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
3640    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
3641    { # validate mangled name
3642        delete($SymbolInfo{$Version}{$InfoId});
3643        return;
3644    }
3645    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3646    and index($ShortName, "_Z")==0)
3647    { # _ZTS, etc.
3648        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3649    }
3650    if(isPrivateData($SymbolInfo{$Version}{$InfoId}{"MnglName"}))
3651    { # non-public global data
3652        delete($SymbolInfo{$Version}{$InfoId});
3653        return;
3654    }
3655    $SymbolInfo{$Version}{$InfoId}{"Data"} = 1;
3656    if(my $Rid = getTypeId($InfoId))
3657    {
3658        if(not defined $TypeInfo{$Version}{$Rid}
3659        or not $TypeInfo{$Version}{$Rid}{"Name"})
3660        {
3661            delete($SymbolInfo{$Version}{$InfoId});
3662            return;
3663        }
3664        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Rid;
3665        my $Val = getDataVal($InfoId, $Rid);
3666        if(defined $Val) {
3667            $SymbolInfo{$Version}{$InfoId}{"Value"} = $Val;
3668        }
3669    }
3670    set_Class_And_Namespace($InfoId);
3671    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
3672    {
3673        if(not defined $TypeInfo{$Version}{$ClassId}
3674        or not $TypeInfo{$Version}{$ClassId}{"Name"})
3675        {
3676            delete($SymbolInfo{$Version}{$InfoId});
3677            return;
3678        }
3679    }
3680    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
3681    { # extern "C"
3682        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
3683        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3684    }
3685    if($UserLang and $UserLang eq "C")
3686    { # --lang=C option
3687        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3688    }
3689    if(not $CheckHeadersOnly)
3690    {
3691        if(not $SymbolInfo{$Version}{$InfoId}{"Class"})
3692        {
3693            if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3694            or not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
3695            {
3696                if(link_symbol($ShortName, $Version, "-Deps"))
3697                { # "const" global data is mangled as _ZL... in the TU dump
3698                  # but not mangled when compiling a C shared library
3699                    $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3700                }
3701            }
3702        }
3703    }
3704    if($COMMON_LANGUAGE{$Version} eq "C++")
3705    {
3706        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3707        { # for some symbols (_ZTI) the short name is the mangled name
3708            if(index($ShortName, "_Z")==0) {
3709                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3710            }
3711        }
3712        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3713        { # try to mangle symbol (link with libraries)
3714            $SymbolInfo{$Version}{$InfoId}{"MnglName"} = linkSymbol($InfoId);
3715        }
3716        if($OStarget eq "windows")
3717        {
3718            if(my $Mangled = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
3719            { # link MS C++ symbols from library with GCC symbols from headers
3720                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
3721            }
3722        }
3723    }
3724    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) {
3725        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3726    }
3727    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3728    {
3729        if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
3730        { # non-target symbols
3731            delete($SymbolInfo{$Version}{$InfoId});
3732            return;
3733        }
3734    }
3735    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
3736    {
3737        if(defined $MissedTypedef{$Version}{$Rid})
3738        {
3739            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
3740                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
3741            }
3742        }
3743    }
3744    setFuncAccess($InfoId);
3745    if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_ZTV")==0) {
3746        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
3747    }
3748    if($ShortName=~/\A(_Z|\?)/) {
3749        delete($SymbolInfo{$Version}{$InfoId}{"ShortName"});
3750    }
3751
3752    if($ExtraDump) {
3753        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
3754    }
3755}
3756
3757sub isConstType($$)
3758{
3759    my ($TypeId, $LibVersion) = @_;
3760    my %Base = get_Type($TypeId, $LibVersion);
3761    while(defined $Base{"Type"} and $Base{"Type"} eq "Typedef") {
3762        %Base = get_OneStep_BaseType($Base{"Tid"}, $TypeInfo{$LibVersion});
3763    }
3764    return ($Base{"Type"} eq "Const");
3765}
3766
3767sub getTrivialName($$)
3768{
3769    my ($TypeInfoId, $TypeId) = @_;
3770    my %TypeAttr = ();
3771    $TypeAttr{"Name"} = getNameByInfo($TypeInfoId);
3772    if(not $TypeAttr{"Name"}) {
3773        $TypeAttr{"Name"} = getTreeTypeName($TypeId);
3774    }
3775    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3776    $TypeAttr{"Type"} = getTypeType($TypeId);
3777    $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name
3778    if(isAnon($TypeAttr{"Name"}))
3779    {
3780        my $NameSpaceId = $TypeId;
3781        while(my $NSId = getTreeAttr_Scpe(getTypeDeclId($NameSpaceId)))
3782        { # searching for a first not anon scope
3783            if($NSId eq $NameSpaceId) {
3784                last;
3785            }
3786            else
3787            {
3788                $TypeAttr{"NameSpace"} = getNameSpace(getTypeDeclId($TypeId));
3789                if(not $TypeAttr{"NameSpace"}
3790                or not isAnon($TypeAttr{"NameSpace"})) {
3791                    last;
3792                }
3793            }
3794            $NameSpaceId = $NSId;
3795        }
3796    }
3797    else
3798    {
3799        if(my $NameSpaceId = getTreeAttr_Scpe($TypeInfoId))
3800        {
3801            if($NameSpaceId ne $TypeId) {
3802                $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId);
3803            }
3804        }
3805    }
3806    if($TypeAttr{"NameSpace"} and not isAnon($TypeAttr{"Name"})) {
3807        $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3808    }
3809    $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3810    if(isAnon($TypeAttr{"Name"}))
3811    { # anon-struct-header.h-line
3812        $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-";
3813        $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"};
3814        if($TypeAttr{"NameSpace"}) {
3815            $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3816        }
3817    }
3818    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId}
3819    and getTypeDeclId($TypeId) eq $TypeInfoId)
3820    {
3821        if(my @TParams = getTParams($TypeId, "Type")) {
3822            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >", "T");
3823        }
3824        else {
3825            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."<...>", "T");
3826        }
3827    }
3828    return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"});
3829}
3830
3831sub getTrivialTypeAttr($)
3832{
3833    my $TypeId = $_[0];
3834    my $TypeInfoId = getTypeDeclId($_[0]);
3835
3836    my %TypeAttr = ();
3837
3838    if($TemplateDecl{$Version}{$TypeId})
3839    { # template_decl
3840        $TypeAttr{"Template"} = 1;
3841    }
3842
3843    setTypeAccess($TypeId, \%TypeAttr);
3844    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3845    if(isBuiltIn($TypeAttr{"Header"}))
3846    {
3847        delete($TypeAttr{"Header"});
3848        delete($TypeAttr{"Line"});
3849    }
3850
3851    $TypeAttr{"Type"} = getTypeType($TypeId);
3852    ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId);
3853    if(not $TypeAttr{"Name"}) {
3854        return ();
3855    }
3856    if(not $TypeAttr{"NameSpace"}) {
3857        delete($TypeAttr{"NameSpace"});
3858    }
3859
3860    if($TypeAttr{"Type"} eq "Intrinsic")
3861    {
3862        if(defined $TypeAttr{"Header"})
3863        {
3864            if($TypeAttr{"Header"}=~/\Adump[1-2]\.[ih]\Z/)
3865            { # support for SUSE 11.2
3866              # integer_type has srcp dump{1-2}.i
3867                delete($TypeAttr{"Header"});
3868            }
3869        }
3870    }
3871
3872    my $Tmpl = undef;
3873
3874    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId})
3875    {
3876        $Tmpl = $BasicTemplate{$Version}{$TypeId};
3877
3878        if(my @TParams = getTParams($TypeId, "Type"))
3879        {
3880            foreach my $Pos (0 .. $#TParams)
3881            {
3882                my $Val = $TParams[$Pos];
3883                $TypeAttr{"TParam"}{$Pos}{"name"} = $Val;
3884
3885                if(not defined $TypeAttr{"Template"})
3886                {
3887                    my %Base = get_BaseType($TemplateInstance{$Version}{"Type"}{$TypeId}{$Pos}, $Version);
3888
3889                    if($Base{"Type"} eq "TemplateParam"
3890                    or defined $Base{"Template"}) {
3891                        $TypeAttr{"Template"} = 1;
3892                    }
3893                }
3894
3895                if($Tmpl)
3896                {
3897                    if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
3898                    {
3899                        $TemplateMap{$Version}{$TypeId}{$Arg} = $Val;
3900
3901                        if($Val eq $Arg) {
3902                            $TypeAttr{"Template"} = 1;
3903                        }
3904                    }
3905                }
3906            }
3907
3908            if($Tmpl)
3909            {
3910                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
3911                {
3912                    if($Pos>$#TParams)
3913                    {
3914                        my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
3915                        $TemplateMap{$Version}{$TypeId}{$Arg} = "";
3916                    }
3917                }
3918            }
3919        }
3920
3921        if($ADD_TMPL_INSTANCES)
3922        {
3923            if($Tmpl)
3924            {
3925                if(my $MainInst = getTreeAttr_Type($Tmpl))
3926                {
3927                    if(not getTreeAttr_Flds($TypeId))
3928                    {
3929                        if(my $Flds = getTreeAttr_Flds($MainInst)) {
3930                            $LibInfo{$Version}{"info"}{$TypeId} .= " flds: \@$Flds ";
3931                        }
3932                    }
3933                    if(not getTreeAttr_Binf($TypeId))
3934                    {
3935                        if(my $Binf = getTreeAttr_Binf($MainInst)) {
3936                            $LibInfo{$Version}{"info"}{$TypeId} .= " binf: \@$Binf ";
3937                        }
3938                    }
3939                }
3940            }
3941        }
3942    }
3943
3944    my $StaticFields = setTypeMemb($TypeId, \%TypeAttr);
3945
3946    if(my $Size = getSize($TypeId))
3947    {
3948        $Size = $Size/$BYTE_SIZE;
3949        $TypeAttr{"Size"} = "$Size";
3950    }
3951    else
3952    {
3953        if($ExtraDump)
3954        {
3955            if(not defined $TypeAttr{"Memb"}
3956            and not $Tmpl)
3957            { # declaration only
3958                $TypeAttr{"Forward"} = 1;
3959            }
3960        }
3961    }
3962
3963    if($TypeAttr{"Type"} eq "Struct"
3964    and ($StaticFields or detect_lang($TypeId)))
3965    {
3966        $TypeAttr{"Type"} = "Class";
3967        $TypeAttr{"Copied"} = 1; # default, will be changed in getSymbolInfo()
3968    }
3969    if($TypeAttr{"Type"} eq "Struct"
3970    or $TypeAttr{"Type"} eq "Class")
3971    {
3972        my $Skip = setBaseClasses($TypeId, \%TypeAttr);
3973        if($Skip) {
3974            return ();
3975        }
3976    }
3977    if(my $Algn = getAlgn($TypeId)) {
3978        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3979    }
3980    setSpec($TypeId, \%TypeAttr);
3981
3982    if($TypeAttr{"Type"}=~/\A(Struct|Union|Enum)\Z/)
3983    {
3984        if(not $TypedefToAnon{$TypeId}
3985        and not defined $TemplateInstance{$Version}{"Type"}{$TypeId})
3986        {
3987            if(not isAnon($TypeAttr{"Name"})) {
3988                $TypeAttr{"Name"} = lc($TypeAttr{"Type"})." ".$TypeAttr{"Name"};
3989            }
3990        }
3991    }
3992
3993    $TypeAttr{"Tid"} = $TypeId;
3994    if(my $VTable = $ClassVTable_Content{$Version}{$TypeAttr{"Name"}})
3995    {
3996        my @Entries = split(/\n/, $VTable);
3997        foreach (1 .. $#Entries)
3998        {
3999            my $Entry = $Entries[$_];
4000            if($Entry=~/\A(\d+)\s+(.+)\Z/) {
4001                $TypeAttr{"VTable"}{$1} = simplifyVTable($2);
4002            }
4003        }
4004    }
4005
4006    if($TypeAttr{"Type"} eq "Enum")
4007    {
4008        if(not $TypeAttr{"NameSpace"})
4009        {
4010            foreach my $Pos (keys(%{$TypeAttr{"Memb"}}))
4011            {
4012                my $MName = $TypeAttr{"Memb"}{$Pos}{"name"};
4013                my $MVal = $TypeAttr{"Memb"}{$Pos}{"value"};
4014                $EnumConstants{$Version}{$MName} = {
4015                    "Value"=>$MVal,
4016                    "Header"=>$TypeAttr{"Header"}
4017                };
4018                if(isAnon($TypeAttr{"Name"}))
4019                {
4020                    if($ExtraDump
4021                    or is_target_header($TypeAttr{"Header"}, $Version))
4022                    {
4023                        %{$Constants{$Version}{$MName}} = (
4024                            "Value" => $MVal,
4025                            "Header" => $TypeAttr{"Header"}
4026                        );
4027                    }
4028                }
4029            }
4030        }
4031    }
4032    if($ExtraDump)
4033    {
4034        if(defined $TypedefToAnon{$TypeId}) {
4035            $TypeAttr{"AnonTypedef"} = 1;
4036        }
4037    }
4038
4039    return %TypeAttr;
4040}
4041
4042sub simplifyVTable($)
4043{
4044    my $Content = $_[0];
4045    if($Content=~s/ \[with (.+)]//)
4046    { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits<char>]
4047        if(my @Elems = separate_Params($1, 0, 0))
4048        {
4049            foreach my $Elem (@Elems)
4050            {
4051                if($Elem=~/\A(.+?)\s*=\s*(.+?)\Z/)
4052                {
4053                    my ($Arg, $Val) = ($1, $2);
4054
4055                    if(defined $DEFAULT_STD_ARGS{$Arg}) {
4056                        $Content=~s/,\s*$Arg\b//g;
4057                    }
4058                    else {
4059                        $Content=~s/\b$Arg\b/$Val/g;
4060                    }
4061                }
4062            }
4063        }
4064    }
4065
4066    return $Content;
4067}
4068
4069sub detect_lang($)
4070{
4071    my $TypeId = $_[0];
4072    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4073    if(check_gcc($GCC_PATH, "4"))
4074    { # GCC 4 fncs-node points to only non-artificial methods
4075        return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /);
4076    }
4077    else
4078    { # GCC 3
4079        my $Fncs = getTreeAttr_Fncs($TypeId);
4080        while($Fncs)
4081        {
4082            if($LibInfo{$Version}{"info"}{$Fncs}!~/artificial/) {
4083                return 1;
4084            }
4085            $Fncs = getTreeAttr_Chan($Fncs);
4086        }
4087    }
4088    return 0;
4089}
4090
4091sub setSpec($$)
4092{
4093    my ($TypeId, $TypeAttr) = @_;
4094    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4095    if($Info=~/\s+spec\s+/) {
4096        $TypeAttr->{"Spec"} = 1;
4097    }
4098}
4099
4100sub setBaseClasses($$)
4101{
4102    my ($TypeId, $TypeAttr) = @_;
4103    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4104    if(my $Binf = getTreeAttr_Binf($TypeId))
4105    {
4106        my $Info = $LibInfo{$Version}{"info"}{$Binf};
4107        my $Pos = 0;
4108        while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //)
4109        {
4110            my ($Access, $BInfoId) = ($1, $2);
4111            my $ClassId = getBinfClassId($BInfoId);
4112
4113            if($ClassId eq $TypeId)
4114            { # class A<N>:public A<N-1>
4115                next;
4116            }
4117
4118            my $CType = $LibInfo{$Version}{"info_type"}{$ClassId};
4119            if(not $CType or $CType eq "template_type_parm"
4120            or $CType eq "typename_type")
4121            { # skip
4122                # return 1;
4123            }
4124            my $BaseInfo = $LibInfo{$Version}{"info"}{$BInfoId};
4125            if($Access=~/prot/) {
4126                $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected";
4127            }
4128            elsif($Access=~/priv/) {
4129                $TypeAttr->{"Base"}{$ClassId}{"access"} = "private";
4130            }
4131            $TypeAttr->{"Base"}{$ClassId}{"pos"} = "$Pos";
4132            if($BaseInfo=~/virt/)
4133            { # virtual base
4134                $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1;
4135            }
4136            $Class_SubClasses{$Version}{$ClassId}{$TypeId}=1;
4137            $Pos += 1;
4138        }
4139    }
4140    return 0;
4141}
4142
4143sub getBinfClassId($)
4144{
4145    my $Info = $LibInfo{$Version}{"info"}{$_[0]};
4146    $Info=~/type[ ]*:[ ]*@(\d+) /;
4147    return $1;
4148}
4149
4150sub unmangledFormat($$)
4151{
4152    my ($Name, $LibVersion) = @_;
4153    $Name = uncover_typedefs($Name, $LibVersion);
4154    while($Name=~s/([^\w>*])(const|volatile)(,|>|\Z)/$1$3/g){};
4155    $Name=~s/\(\w+\)(\d)/$1/;
4156    return $Name;
4157}
4158
4159sub modelUnmangled($$)
4160{
4161    my ($InfoId, $Compiler) = @_;
4162    if($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}) {
4163        return $Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId};
4164    }
4165    my $PureSignature = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
4166    if($SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4167        $PureSignature = "~".$PureSignature;
4168    }
4169    if(not $SymbolInfo{$Version}{$InfoId}{"Data"})
4170    {
4171        my (@Params, @ParamTypes) = ();
4172        if(defined $SymbolInfo{$Version}{$InfoId}{"Param"}
4173        and not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4174            @Params = keys(%{$SymbolInfo{$Version}{$InfoId}{"Param"}});
4175        }
4176        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4177        { # checking parameters
4178            my $PId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"type"};
4179            my $PName = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"name"};
4180            my %PType = get_PureType($PId, $TypeInfo{$Version});
4181            my $PTName = unmangledFormat($PType{"Name"}, $Version);
4182
4183            if($PName eq "this"
4184            and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method")
4185            {
4186                next;
4187            }
4188
4189            $PTName=~s/\b(restrict|register)\b//g;
4190            if($Compiler eq "MSVC") {
4191                $PTName=~s/\blong long\b/__int64/;
4192            }
4193            @ParamTypes = (@ParamTypes, $PTName);
4194        }
4195        if(@ParamTypes) {
4196            $PureSignature .= "(".join(", ", @ParamTypes).")";
4197        }
4198        else
4199        {
4200            if($Compiler eq "MSVC")
4201            {
4202                $PureSignature .= "(void)";
4203            }
4204            else
4205            { # GCC
4206                $PureSignature .= "()";
4207            }
4208        }
4209        $PureSignature = delete_keywords($PureSignature);
4210    }
4211    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
4212    {
4213        my $ClassName = unmangledFormat($TypeInfo{$Version}{$ClassId}{"Name"}, $Version);
4214        $PureSignature = $ClassName."::".$PureSignature;
4215    }
4216    elsif(my $NS = $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) {
4217        $PureSignature = $NS."::".$PureSignature;
4218    }
4219    if($SymbolInfo{$Version}{$InfoId}{"Const"}) {
4220        $PureSignature .= " const";
4221    }
4222    if($SymbolInfo{$Version}{$InfoId}{"Volatile"}) {
4223        $PureSignature .= " volatile";
4224    }
4225    my $ShowReturn = 0;
4226    if($Compiler eq "MSVC"
4227    and $SymbolInfo{$Version}{$InfoId}{"Data"})
4228    {
4229        $ShowReturn=1;
4230    }
4231    elsif(defined $TemplateInstance{$Version}{"Func"}{$InfoId}
4232    and keys(%{$TemplateInstance{$Version}{"Func"}{$InfoId}}))
4233    {
4234        $ShowReturn=1;
4235    }
4236    if($ShowReturn)
4237    { # mangled names for template function specializations include return value
4238        if(my $ReturnId = $SymbolInfo{$Version}{$InfoId}{"Return"})
4239        {
4240            my %RType = get_PureType($ReturnId, $TypeInfo{$Version});
4241            my $ReturnName = unmangledFormat($RType{"Name"}, $Version);
4242            $PureSignature = $ReturnName." ".$PureSignature;
4243        }
4244    }
4245    return ($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId} = formatName($PureSignature, "S"));
4246}
4247
4248sub mangle_symbol($$$)
4249{ # mangling for simple methods
4250  # see gcc-4.6.0/gcc/cp/mangle.c
4251    my ($InfoId, $LibVersion, $Compiler) = @_;
4252    if($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}) {
4253        return $Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler};
4254    }
4255    my $Mangled = "";
4256    if($Compiler eq "GCC") {
4257        $Mangled = mangle_symbol_GCC($InfoId, $LibVersion);
4258    }
4259    elsif($Compiler eq "MSVC") {
4260        $Mangled = mangle_symbol_MSVC($InfoId, $LibVersion);
4261    }
4262    return ($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler} = $Mangled);
4263}
4264
4265sub mangle_symbol_MSVC($$)
4266{ # TODO
4267    my ($InfoId, $LibVersion) = @_;
4268    return "";
4269}
4270
4271sub mangle_symbol_GCC($$)
4272{ # see gcc-4.6.0/gcc/cp/mangle.c
4273    my ($InfoId, $LibVersion) = @_;
4274    my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, "");
4275    my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
4276    my %Repl = ();# SN_ replacements
4277    if($ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
4278    {
4279        my $MangledClass = mangle_param($ClassId, $LibVersion, \%Repl);
4280        if($MangledClass!~/\AN/) {
4281            $MangledClass = "N".$MangledClass;
4282        }
4283        else {
4284            $MangledClass=~s/E\Z//;
4285        }
4286        if($SymbolInfo{$LibVersion}{$InfoId}{"Volatile"}) {
4287            $MangledClass=~s/\AN/NV/;
4288        }
4289        if($SymbolInfo{$LibVersion}{$InfoId}{"Const"}) {
4290            $MangledClass=~s/\AN/NK/;
4291        }
4292        $Mangled .= $MangledClass;
4293    }
4294    elsif($NameSpace = $SymbolInfo{$LibVersion}{$InfoId}{"NameSpace"})
4295    { # mangled by name due to the absence of structured info
4296        my $MangledNS = mangle_ns($NameSpace, $LibVersion, \%Repl);
4297        if($MangledNS!~/\AN/) {
4298            $MangledNS = "N".$MangledNS;
4299        }
4300        else {
4301            $MangledNS=~s/E\Z//;
4302        }
4303        $Mangled .= $MangledNS;
4304    }
4305    my ($ShortName, $TmplParams) = template_Base($SymbolInfo{$LibVersion}{$InfoId}{"ShortName"});
4306    my @TParams = ();
4307    if(my @TPos = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}}))
4308    { # parsing mode
4309        foreach (@TPos) {
4310            push(@TParams, $SymbolInfo{$LibVersion}{$InfoId}{"TParam"}{$_}{"name"});
4311        }
4312    }
4313    elsif($TmplParams)
4314    { # remangling mode
4315      # support for old ABI dumps
4316        @TParams = separate_Params($TmplParams, 0, 0);
4317    }
4318    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) {
4319        $Mangled .= "C1";
4320    }
4321    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4322        $Mangled .= "D0";
4323    }
4324    elsif($ShortName)
4325    {
4326        if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4327        {
4328            if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
4329            and isConstType($Return, $LibVersion))
4330            { # "const" global data is mangled as _ZL...
4331                $Mangled .= "L";
4332            }
4333        }
4334        if($ShortName=~/\Aoperator(\W.*)\Z/)
4335        {
4336            my $Op = $1;
4337            $Op=~s/\A[ ]+//g;
4338            if(my $OpMngl = $OperatorMangling{$Op}) {
4339                $Mangled .= $OpMngl;
4340            }
4341            else { # conversion operator
4342                $Mangled .= "cv".mangle_param(getTypeIdByName($Op, $LibVersion), $LibVersion, \%Repl);
4343            }
4344        }
4345        else {
4346            $Mangled .= length($ShortName).$ShortName;
4347        }
4348        if(@TParams)
4349        { # templates
4350            $Mangled .= "I";
4351            foreach my $TParam (@TParams) {
4352                $Mangled .= mangle_template_param($TParam, $LibVersion, \%Repl);
4353            }
4354            $Mangled .= "E";
4355        }
4356        if(not $ClassId and @TParams) {
4357            add_substitution($ShortName, \%Repl, 0);
4358        }
4359    }
4360    if($ClassId or $NameSpace) {
4361        $Mangled .= "E";
4362    }
4363    if(@TParams)
4364    {
4365        if($Return) {
4366            $Mangled .= mangle_param($Return, $LibVersion, \%Repl);
4367        }
4368    }
4369    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4370    {
4371        my @Params = ();
4372        if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
4373        and not $SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4374            @Params = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
4375        }
4376        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4377        { # checking parameters
4378            my $ParamType_Id = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$ParamPos}{"type"};
4379            $Mangled .= mangle_param($ParamType_Id, $LibVersion, \%Repl);
4380        }
4381        if(not @Params) {
4382            $Mangled .= "v";
4383        }
4384    }
4385    $Mangled = correct_incharge($InfoId, $LibVersion, $Mangled);
4386    $Mangled = write_stdcxx_substitution($Mangled);
4387    if($Mangled eq "_Z") {
4388        return "";
4389    }
4390    return $Mangled;
4391}
4392
4393sub correct_incharge($$$)
4394{
4395    my ($InfoId, $LibVersion, $Mangled) = @_;
4396    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"})
4397    {
4398        if($MangledNames{$LibVersion}{$Mangled}) {
4399            $Mangled=~s/C1([EI])/C2$1/;
4400        }
4401    }
4402    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
4403    {
4404        if($MangledNames{$LibVersion}{$Mangled}) {
4405            $Mangled=~s/D0([EI])/D1$1/;
4406        }
4407        if($MangledNames{$LibVersion}{$Mangled}) {
4408            $Mangled=~s/D1([EI])/D2$1/;
4409        }
4410    }
4411    return $Mangled;
4412}
4413
4414sub template_Base($)
4415{ # NOTE: std::_Vector_base<mysqlpp::mysql_type_info>::_Vector_impl
4416  # NOTE: operators: >>, <<
4417    my $Name = $_[0];
4418    if($Name!~/>\Z/ or $Name!~/</) {
4419        return $Name;
4420    }
4421    my $TParams = $Name;
4422    while(my $CPos = find_center($TParams, "<"))
4423    { # search for the last <T>
4424        $TParams = substr($TParams, $CPos);
4425    }
4426    if($TParams=~s/\A<(.+)>\Z/$1/) {
4427        $Name=~s/<\Q$TParams\E>\Z//;
4428    }
4429    else
4430    { # error
4431        $TParams = "";
4432    }
4433    return ($Name, $TParams);
4434}
4435
4436sub get_sub_ns($)
4437{
4438    my $Name = $_[0];
4439    my @NS = ();
4440    while(my $CPos = find_center($Name, ":"))
4441    {
4442        push(@NS, substr($Name, 0, $CPos));
4443        $Name = substr($Name, $CPos);
4444        $Name=~s/\A:://;
4445    }
4446    return (join("::", @NS), $Name);
4447}
4448
4449sub mangle_ns($$$)
4450{
4451    my ($Name, $LibVersion, $Repl) = @_;
4452    if(my $Tid = $TName_Tid{$LibVersion}{$Name})
4453    {
4454        my $Mangled = mangle_param($Tid, $LibVersion, $Repl);
4455        $Mangled=~s/\AN(.+)E\Z/$1/;
4456        return $Mangled;
4457
4458    }
4459    else
4460    {
4461        my ($MangledNS, $SubNS) = ("", "");
4462        ($SubNS, $Name) = get_sub_ns($Name);
4463        if($SubNS) {
4464            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4465        }
4466        $MangledNS .= length($Name).$Name;
4467        add_substitution($MangledNS, $Repl, 0);
4468        return $MangledNS;
4469    }
4470}
4471
4472sub mangle_param($$$)
4473{
4474    my ($PTid, $LibVersion, $Repl) = @_;
4475    my ($MPrefix, $Mangled) = ("", "");
4476    my %ReplCopy = %{$Repl};
4477    my %BaseType = get_BaseType($PTid, $LibVersion);
4478    my $BaseType_Name = $BaseType{"Name"};
4479    $BaseType_Name=~s/\A(struct|union|enum) //g;
4480    if(not $BaseType_Name) {
4481        return "";
4482    }
4483    my ($ShortName, $TmplParams) = template_Base($BaseType_Name);
4484    my $Suffix = get_BaseTypeQual($PTid, $LibVersion);
4485    while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){};
4486    while($Suffix=~/(&|\*|const)\Z/)
4487    {
4488        if($Suffix=~s/[ ]*&\Z//) {
4489            $MPrefix .= "R";
4490        }
4491        if($Suffix=~s/[ ]*\*\Z//) {
4492            $MPrefix .= "P";
4493        }
4494        if($Suffix=~s/[ ]*const\Z//)
4495        {
4496            if($MPrefix=~/R|P/
4497            or $Suffix=~/&|\*/) {
4498                $MPrefix .= "K";
4499            }
4500        }
4501        if($Suffix=~s/[ ]*volatile\Z//) {
4502            $MPrefix .= "V";
4503        }
4504        #if($Suffix=~s/[ ]*restrict\Z//) {
4505            #$MPrefix .= "r";
4506        #}
4507    }
4508    if(my $Token = $IntrinsicMangling{$BaseType_Name}) {
4509        $Mangled .= $Token;
4510    }
4511    elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/)
4512    {
4513        my @TParams = ();
4514        if(my @TPos = keys(%{$BaseType{"TParam"}}))
4515        { # parsing mode
4516            foreach (@TPos) {
4517                push(@TParams, $BaseType{"TParam"}{$_}{"name"});
4518            }
4519        }
4520        elsif($TmplParams)
4521        { # remangling mode
4522          # support for old ABI dumps
4523            @TParams = separate_Params($TmplParams, 0, 0);
4524        }
4525        my $MangledNS = "";
4526        my ($SubNS, $SName) = get_sub_ns($ShortName);
4527        if($SubNS) {
4528            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4529        }
4530        $MangledNS .= length($SName).$SName;
4531        if(@TParams) {
4532            add_substitution($MangledNS, $Repl, 0);
4533        }
4534        $Mangled .= "N".$MangledNS;
4535        if(@TParams)
4536        { # templates
4537            $Mangled .= "I";
4538            foreach my $TParam (@TParams) {
4539                $Mangled .= mangle_template_param($TParam, $LibVersion, $Repl);
4540            }
4541            $Mangled .= "E";
4542        }
4543        $Mangled .= "E";
4544    }
4545    elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/)
4546    {
4547        if($BaseType{"Type"} eq "MethodPtr") {
4548            $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl)."F";
4549        }
4550        else {
4551            $Mangled .= "PF";
4552        }
4553        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4554        my @Params = keys(%{$BaseType{"Param"}});
4555        foreach my $Num (sort {int($a)<=>int($b)} @Params) {
4556            $Mangled .= mangle_param($BaseType{"Param"}{$Num}{"type"}, $LibVersion, $Repl);
4557        }
4558        if(not @Params) {
4559            $Mangled .= "v";
4560        }
4561        $Mangled .= "E";
4562    }
4563    elsif($BaseType{"Type"} eq "FieldPtr")
4564    {
4565        $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl);
4566        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4567    }
4568    $Mangled = $MPrefix.$Mangled;# add prefix (RPK)
4569    if(my $Optimized = write_substitution($Mangled, \%ReplCopy))
4570    {
4571        if($Mangled eq $Optimized)
4572        {
4573            if($ShortName!~/::/)
4574            { # remove "N ... E"
4575                if($MPrefix) {
4576                    $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g;
4577                }
4578                else {
4579                    $Mangled=~s/\AN(.+)E\Z/$1/g;
4580                }
4581            }
4582        }
4583        else {
4584            $Mangled = $Optimized;
4585        }
4586    }
4587    add_substitution($Mangled, $Repl, 1);
4588    return $Mangled;
4589}
4590
4591sub mangle_template_param($$$)
4592{ # types + literals
4593    my ($TParam, $LibVersion, $Repl) = @_;
4594    if(my $TPTid = $TName_Tid{$LibVersion}{$TParam}) {
4595        return mangle_param($TPTid, $LibVersion, $Repl);
4596    }
4597    elsif($TParam=~/\A(\d+)(\w+)\Z/)
4598    { # class_name<1u>::method(...)
4599        return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E";
4600    }
4601    elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/)
4602    { # class_name<(signed char)1>::method(...)
4603        return "L".$IntrinsicMangling{$1}.$2."E";
4604    }
4605    elsif($TParam eq "true")
4606    { # class_name<true>::method(...)
4607        return "Lb1E";
4608    }
4609    elsif($TParam eq "false")
4610    { # class_name<true>::method(...)
4611        return "Lb0E";
4612    }
4613    else { # internal error
4614        return length($TParam).$TParam;
4615    }
4616}
4617
4618sub add_substitution($$$)
4619{
4620    my ($Value, $Repl, $Rec) = @_;
4621    if($Rec)
4622    { # subtypes
4623        my @Subs = ($Value);
4624        while($Value=~s/\A(R|P|K)//) {
4625            push(@Subs, $Value);
4626        }
4627        foreach (reverse(@Subs)) {
4628            add_substitution($_, $Repl, 0);
4629        }
4630        return;
4631    }
4632    return if($Value=~/\AS(\d*)_\Z/);
4633    $Value=~s/\AN(.+)E\Z/$1/g;
4634    return if(defined $Repl->{$Value});
4635    return if(length($Value)<=1);
4636    return if($StdcxxMangling{$Value});
4637    # check for duplicates
4638    my $Base = $Value;
4639    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4640    {
4641        my $Num = $Repl->{$Type};
4642        my $Replace = macro_mangle($Num);
4643        $Base=~s/\Q$Replace\E/$Type/;
4644    }
4645    if(my $OldNum = $Repl->{$Base})
4646    {
4647        $Repl->{$Value} = $OldNum;
4648        return;
4649    }
4650    my @Repls = sort {$b<=>$a} values(%{$Repl});
4651    if(@Repls) {
4652        $Repl->{$Value} = $Repls[0]+1;
4653    }
4654    else {
4655        $Repl->{$Value} = -1;
4656    }
4657    # register duplicates
4658    # upward
4659    $Base = $Value;
4660    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4661    {
4662        next if($Base eq $Type);
4663        my $Num = $Repl->{$Type};
4664        my $Replace = macro_mangle($Num);
4665        $Base=~s/\Q$Type\E/$Replace/;
4666        $Repl->{$Base} = $Repl->{$Value};
4667    }
4668}
4669
4670sub macro_mangle($)
4671{
4672    my $Num = $_[0];
4673    if($Num==-1) {
4674        return "S_";
4675    }
4676    else
4677    {
4678        my $Code = "";
4679        if($Num<10)
4680        { # S0_, S1_, S2_, ...
4681            $Code = $Num;
4682        }
4683        elsif($Num>=10 and $Num<=35)
4684        { # SA_, SB_, SC_, ...
4685            $Code = chr(55+$Num);
4686        }
4687        else
4688        { # S10_, S11_, S12_
4689            $Code = $Num-26; # 26 is length of english alphabet
4690        }
4691        return "S".$Code."_";
4692    }
4693}
4694
4695sub write_stdcxx_substitution($)
4696{
4697    my $Mangled = $_[0];
4698    if($StdcxxMangling{$Mangled}) {
4699        return $StdcxxMangling{$Mangled};
4700    }
4701    else
4702    {
4703        my @Repls = keys(%StdcxxMangling);
4704        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4705        foreach my $MangledType (@Repls)
4706        {
4707            my $Replace = $StdcxxMangling{$MangledType};
4708            #if($Mangled!~/$Replace/) {
4709                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4710                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4711            #}
4712        }
4713    }
4714    return $Mangled;
4715}
4716
4717sub write_substitution($$)
4718{
4719    my ($Mangled, $Repl) = @_;
4720    if(defined $Repl->{$Mangled}
4721    and my $MnglNum = $Repl->{$Mangled}) {
4722        $Mangled = macro_mangle($MnglNum);
4723    }
4724    else
4725    {
4726        my @Repls = keys(%{$Repl});
4727        #@Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls;
4728        # FIXME: how to apply replacements? by num or by pos
4729        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4730        foreach my $MangledType (@Repls)
4731        {
4732            my $Replace = macro_mangle($Repl->{$MangledType});
4733            if($Mangled!~/$Replace/) {
4734                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4735                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4736            }
4737        }
4738    }
4739    return $Mangled;
4740}
4741
4742sub delete_keywords($)
4743{
4744    my $TypeName = $_[0];
4745    $TypeName=~s/\b(enum|struct|union|class) //g;
4746    return $TypeName;
4747}
4748
4749sub uncover_typedefs($$)
4750{
4751    my ($TypeName, $LibVersion) = @_;
4752    return "" if(not $TypeName);
4753    if(defined $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}) {
4754        return $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName};
4755    }
4756    my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName, "T"), "");
4757    while($TypeName_New ne $TypeName_Pre)
4758    {
4759        $TypeName_Pre = $TypeName_New;
4760        my $TypeName_Copy = $TypeName_New;
4761        my %Words = ();
4762        while($TypeName_Copy=~s/\b([a-z_]([\w:]*\w|))\b//io)
4763        {
4764            if(not $Intrinsic_Keywords{$1}) {
4765                $Words{$1} = 1;
4766            }
4767        }
4768        foreach my $Word (keys(%Words))
4769        {
4770            my $BaseType_Name = $Typedef_BaseName{$LibVersion}{$Word};
4771            next if(not $BaseType_Name);
4772            next if($TypeName_New=~/\b(struct|union|enum)\s\Q$Word\E\b/);
4773            if($BaseType_Name=~/\([\*]+\)/)
4774            { # FuncPtr
4775                if($TypeName_New=~/\Q$Word\E(.*)\Z/)
4776                {
4777                    my $Type_Suffix = $1;
4778                    $TypeName_New = $BaseType_Name;
4779                    if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) {
4780                        $TypeName_New = formatName($TypeName_New, "T");
4781                    }
4782                }
4783            }
4784            else
4785            {
4786                if($TypeName_New=~s/\b\Q$Word\E\b/$BaseType_Name/g) {
4787                    $TypeName_New = formatName($TypeName_New, "T");
4788                }
4789            }
4790        }
4791    }
4792    return ($Cache{"uncover_typedefs"}{$LibVersion}{$TypeName} = $TypeName_New);
4793}
4794
4795sub isInternal($)
4796{
4797    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
4798    {
4799        if($Info=~/mngl[ ]*:[ ]*@(\d+) /)
4800        {
4801            if($LibInfo{$Version}{"info"}{$1}=~/\*[ ]*INTERNAL[ ]*\*/)
4802            { # _ZN7mysqlpp8DateTimeC1ERKS0_ *INTERNAL*
4803                return 1;
4804            }
4805        }
4806    }
4807    return 0;
4808}
4809
4810sub getDataVal($$)
4811{
4812    my ($InfoId, $TypeId) = @_;
4813    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4814    {
4815        if($Info=~/init[ ]*:[ ]*@(\d+) /)
4816        {
4817            if(defined $LibInfo{$Version}{"info_type"}{$1}
4818            and $LibInfo{$Version}{"info_type"}{$1} eq "nop_expr")
4819            {
4820                if(my $Nop = getTreeAttr_Op($1))
4821                {
4822                    if(defined $LibInfo{$Version}{"info_type"}{$Nop}
4823                    and $LibInfo{$Version}{"info_type"}{$Nop} eq "addr_expr")
4824                    {
4825                        if(my $Addr = getTreeAttr_Op($1)) {
4826                            return getInitVal($Addr, $TypeId);
4827                        }
4828                    }
4829                }
4830            }
4831            else {
4832                return getInitVal($1, $TypeId);
4833            }
4834        }
4835    }
4836    return undef;
4837}
4838
4839sub getInitVal($$)
4840{
4841    my ($InfoId, $TypeId) = @_;
4842    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4843    {
4844        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$InfoId})
4845        {
4846            if($InfoType eq "integer_cst")
4847            {
4848                my $Val = getNodeIntCst($InfoId);
4849                if($TypeId and $TypeInfo{$Version}{$TypeId}{"Name"}=~/\Achar(| const)\Z/)
4850                { # characters
4851                    $Val = chr($Val);
4852                }
4853                return $Val;
4854            }
4855            elsif($InfoType eq "string_cst") {
4856                return getNodeStrCst($InfoId);
4857            }
4858            elsif($InfoType eq "var_decl")
4859            {
4860                if(my $Name = getNodeStrCst(getTreeAttr_Mngl($InfoId))) {
4861                    return $Name;
4862                }
4863            }
4864        }
4865    }
4866    return undef;
4867}
4868
4869sub set_Class_And_Namespace($)
4870{
4871    my $InfoId = $_[0];
4872    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4873    {
4874        if($Info=~/scpe[ ]*:[ ]*@(\d+) /)
4875        {
4876            my $NSInfoId = $1;
4877            if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
4878            {
4879                if($InfoType eq "namespace_decl") {
4880                    $SymbolInfo{$Version}{$InfoId}{"NameSpace"} = getNameSpace($InfoId);
4881                }
4882                elsif($InfoType eq "record_type") {
4883                    $SymbolInfo{$Version}{$InfoId}{"Class"} = $NSInfoId;
4884                }
4885            }
4886        }
4887    }
4888    if($SymbolInfo{$Version}{$InfoId}{"Class"}
4889    or $SymbolInfo{$Version}{$InfoId}{"NameSpace"})
4890    {
4891        if($COMMON_LANGUAGE{$Version} ne "C++")
4892        { # skip
4893            return 1;
4894        }
4895    }
4896
4897    return 0;
4898}
4899
4900sub debugMangling($)
4901{
4902    my $LibVersion = $_[0];
4903    my %Mangled = ();
4904    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
4905    {
4906        if(my $Mngl = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
4907        {
4908            if($Mngl=~/\A(_Z|\?)/) {
4909                $Mangled{$Mngl}=$InfoId;
4910            }
4911        }
4912    }
4913    translateSymbols(keys(%Mangled), $LibVersion);
4914    foreach my $Mngl (keys(%Mangled))
4915    {
4916        my $U1 = modelUnmangled($Mangled{$Mngl}, "GCC");
4917        my $U2 = $tr_name{$Mngl};
4918        if($U1 ne $U2) {
4919            printMsg("INFO", "INCORRECT MANGLING:\n  $Mngl\n  $U1\n  $U2\n");
4920        }
4921    }
4922}
4923
4924sub linkSymbol($)
4925{ # link symbols from shared libraries
4926  # with the symbols from header files
4927    my $InfoId = $_[0];
4928    # try to mangle symbol
4929    if((not check_gcc($GCC_PATH, "4") and $SymbolInfo{$Version}{$InfoId}{"Class"})
4930    or (check_gcc($GCC_PATH, "4") and not $SymbolInfo{$Version}{$InfoId}{"Class"})
4931    or $EMERGENCY_MODE_48)
4932    { # GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data)
4933      # GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions
4934      # GCC 4.8.[012] doesn't mangle anything
4935        if(not $CheckHeadersOnly)
4936        {
4937            if(my $Mangled = $mangled_name_gcc{modelUnmangled($InfoId, "GCC")}) {
4938                return correct_incharge($InfoId, $Version, $Mangled);
4939            }
4940        }
4941        if($CheckHeadersOnly
4942        or not $BinaryOnly
4943        or $EMERGENCY_MODE_48)
4944        { # 1. --headers-only mode
4945          # 2. not mangled src-only symbols
4946            if(my $Mangled = mangle_symbol($InfoId, $Version, "GCC")) {
4947                return $Mangled;
4948            }
4949        }
4950    }
4951    return "";
4952}
4953
4954sub setLanguage($$)
4955{
4956    my ($LibVersion, $Lang) = @_;
4957    if(not $UserLang) {
4958        $COMMON_LANGUAGE{$LibVersion} = $Lang;
4959    }
4960}
4961
4962sub getSymbolInfo($)
4963{
4964    my $InfoId = $_[0];
4965    if(isInternal($InfoId)) {
4966        return;
4967    }
4968    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
4969    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
4970    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"}))
4971    {
4972        delete($SymbolInfo{$Version}{$InfoId});
4973        return;
4974    }
4975    setFuncAccess($InfoId);
4976    setFuncKind($InfoId);
4977    if($SymbolInfo{$Version}{$InfoId}{"PseudoTemplate"})
4978    {
4979        delete($SymbolInfo{$Version}{$InfoId});
4980        return;
4981    }
4982
4983    $SymbolInfo{$Version}{$InfoId}{"Type"} = getFuncType($InfoId);
4984    if(my $Return = getFuncReturn($InfoId))
4985    {
4986        if(not defined $TypeInfo{$Version}{$Return}
4987        or not $TypeInfo{$Version}{$Return}{"Name"})
4988        {
4989            delete($SymbolInfo{$Version}{$InfoId});
4990            return;
4991        }
4992        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Return;
4993    }
4994    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
4995    {
4996        if(defined $MissedTypedef{$Version}{$Rid})
4997        {
4998            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
4999                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
5000            }
5001        }
5002    }
5003    if(not $SymbolInfo{$Version}{$InfoId}{"Return"}) {
5004        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5005    }
5006    my $Orig = getFuncOrig($InfoId);
5007    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getFuncShortName($Orig);
5008    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "\._")!=-1)
5009    {
5010        delete($SymbolInfo{$Version}{$InfoId});
5011        return;
5012    }
5013
5014    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "tmp_add_func")==0)
5015    {
5016        delete($SymbolInfo{$Version}{$InfoId});
5017        return;
5018    }
5019
5020    if(defined $TemplateInstance{$Version}{"Func"}{$Orig})
5021    {
5022        my $Tmpl = $BasicTemplate{$Version}{$InfoId};
5023
5024        my @TParams = getTParams($Orig, "Func");
5025        if(not @TParams)
5026        {
5027            delete($SymbolInfo{$Version}{$InfoId});
5028            return;
5029        }
5030        foreach my $Pos (0 .. $#TParams)
5031        {
5032            my $Val = $TParams[$Pos];
5033            $SymbolInfo{$Version}{$InfoId}{"TParam"}{$Pos}{"name"} = $Val;
5034
5035            if($Tmpl)
5036            {
5037                if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
5038                {
5039                    $TemplateMap{$Version}{$InfoId}{$Arg} = $Val;
5040                }
5041            }
5042        }
5043
5044        if($Tmpl)
5045        {
5046            foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
5047            {
5048                if($Pos>$#TParams)
5049                {
5050                    my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
5051                    $TemplateMap{$Version}{$InfoId}{$Arg} = "";
5052                }
5053            }
5054        }
5055
5056        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\Aoperator\W+\Z/)
5057        { # operator<< <T>, operator>> <T>
5058            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= " ";
5059        }
5060        if(@TParams) {
5061            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<".join(", ", @TParams).">";
5062        }
5063        else {
5064            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<...>";
5065        }
5066        $SymbolInfo{$Version}{$InfoId}{"ShortName"} = formatName($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "S");
5067    }
5068    else
5069    { # support for GCC 3.4
5070        $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5071    }
5072    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
5073    {
5074        if($OSgroup eq "windows")
5075        { # cut the offset
5076            $MnglName=~s/\@\d+\Z//g;
5077        }
5078        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
5079
5080        # NOTE: mangling of some symbols may change depending on GCC version
5081        # GCC 4.6: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2IT_EERKS_IT_E
5082        # GCC 4.7: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2ERKS1_
5083    }
5084
5085    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
5086    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
5087    {
5088        delete($SymbolInfo{$Version}{$InfoId});
5089        return;
5090    }
5091    if(not $SymbolInfo{$Version}{$InfoId}{"Destructor"})
5092    { # destructors have an empty parameter list
5093        my $Skip = setFuncParams($InfoId);
5094        if($Skip)
5095        {
5096            delete($SymbolInfo{$Version}{$InfoId});
5097            return;
5098        }
5099    }
5100    if($LibInfo{$Version}{"info"}{$InfoId}=~/ artificial /i) {
5101        $SymbolInfo{$Version}{$InfoId}{"Artificial"} = 1;
5102    }
5103
5104    if(set_Class_And_Namespace($InfoId))
5105    {
5106        delete($SymbolInfo{$Version}{$InfoId});
5107        return;
5108    }
5109
5110    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5111    {
5112        if(not defined $TypeInfo{$Version}{$ClassId}
5113        or not $TypeInfo{$Version}{$ClassId}{"Name"})
5114        {
5115            delete($SymbolInfo{$Version}{$InfoId});
5116            return;
5117        }
5118    }
5119    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
5120    { # extern "C"
5121        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
5122        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5123    }
5124    if($UserLang and $UserLang eq "C")
5125    { # --lang=C option
5126        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5127    }
5128    if($COMMON_LANGUAGE{$Version} eq "C++")
5129    { # correct mangled & short names
5130      # C++ or --headers-only mode
5131        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/)
5132        { # support for old GCC versions: reconstruct real names for constructors and destructors
5133            $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getNameByInfo(getTypeDeclId($SymbolInfo{$Version}{$InfoId}{"Class"}));
5134            $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5135        }
5136        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5137        { # try to mangle symbol (link with libraries)
5138            if(my $Mangled = linkSymbol($InfoId)) {
5139                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
5140            }
5141        }
5142        if($OStarget eq "windows")
5143        { # link MS C++ symbols from library with GCC symbols from headers
5144            if(my $Mangled1 = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
5145            { # exported symbols
5146                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled1;
5147            }
5148            elsif(my $Mangled2 = mangle_symbol($InfoId, $Version, "MSVC"))
5149            { # pure virtual symbols
5150                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled2;
5151            }
5152        }
5153    }
5154    else
5155    { # not mangled in C
5156        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5157    }
5158    if(not $CheckHeadersOnly
5159    and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function"
5160    and not $SymbolInfo{$Version}{$InfoId}{"Class"})
5161    {
5162        my $Incorrect = 0;
5163
5164        if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5165        {
5166            if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")==0
5167            and not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
5168            { # mangled in the TU dump, but not mangled in the library
5169                $Incorrect = 1;
5170            }
5171        }
5172        else
5173        {
5174            if($SymbolInfo{$Version}{$InfoId}{"Lang"} ne "C")
5175            { # all C++ functions are not mangled in the TU dump
5176                $Incorrect = 1;
5177            }
5178        }
5179        if($Incorrect)
5180        {
5181            if(link_symbol($SymbolInfo{$Version}{$InfoId}{"ShortName"}, $Version, "-Deps")) {
5182                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5183            }
5184        }
5185    }
5186    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5187    { # can't detect symbol name
5188        delete($SymbolInfo{$Version}{$InfoId});
5189        return;
5190    }
5191    if(not $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5192    and my $Spec = getVirtSpec($Orig))
5193    { # identify virtual and pure virtual functions
5194      # NOTE: constructors cannot be virtual
5195      # NOTE: in GCC 4.7 D1 destructors have no virtual spec
5196      # in the TU dump, so taking it from the original symbol
5197        if(not ($SymbolInfo{$Version}{$InfoId}{"Destructor"}
5198        and $SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/D2E/))
5199        { # NOTE: D2 destructors are not present in a v-table
5200            $SymbolInfo{$Version}{$InfoId}{$Spec} = 1;
5201        }
5202    }
5203    if(isInline($InfoId)) {
5204        $SymbolInfo{$Version}{$InfoId}{"InLine"} = 1;
5205    }
5206    if(hasThrow($InfoId)) {
5207        $SymbolInfo{$Version}{$InfoId}{"Throw"} = 1;
5208    }
5209    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5210    and my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5211    {
5212        if(not $SymbolInfo{$Version}{$InfoId}{"InLine"}
5213        and not $SymbolInfo{$Version}{$InfoId}{"Artificial"})
5214        { # inline or auto-generated constructor
5215            delete($TypeInfo{$Version}{$ClassId}{"Copied"});
5216        }
5217    }
5218    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5219    {
5220        if(not $ExtraDump)
5221        {
5222            if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
5223            { # non-target symbols
5224                delete($SymbolInfo{$Version}{$InfoId});
5225                return;
5226            }
5227        }
5228    }
5229    if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method"
5230    or $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5231    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}
5232    or $SymbolInfo{$Version}{$InfoId}{"Class"})
5233    {
5234        if($SymbolInfo{$Version}{$InfoId}{"MnglName"}!~/\A(_Z|\?)/)
5235        {
5236            delete($SymbolInfo{$Version}{$InfoId});
5237            return;
5238        }
5239    }
5240    if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5241    {
5242        if($MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5243        { # one instance for one mangled name only
5244            delete($SymbolInfo{$Version}{$InfoId});
5245            return;
5246        }
5247        else {
5248            $MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}} = 1;
5249        }
5250    }
5251    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5252    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
5253        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5254    }
5255    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/
5256    and $SymbolInfo{$Version}{$InfoId}{"Class"})
5257    {
5258        if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function")
5259        { # static methods
5260            $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5261        }
5262    }
5263    if(getFuncLink($InfoId) eq "Static") {
5264        $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5265    }
5266    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/)
5267    {
5268        if(my $Unmangled = $tr_name{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5269        {
5270            if($Unmangled=~/\.\_\d/)
5271            {
5272                delete($SymbolInfo{$Version}{$InfoId});
5273                return;
5274            }
5275        }
5276    }
5277
5278    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(V|)K/) {
5279        $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5280    }
5281    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(K|)V/) {
5282        $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5283    }
5284
5285    if($WeakSymbols{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) {
5286        $SymbolInfo{$Version}{$InfoId}{"Weak"} = 1;
5287    }
5288
5289    if($ExtraDump) {
5290        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
5291    }
5292}
5293
5294sub guessHeader($)
5295{
5296    my $InfoId = $_[0];
5297    my $ShortName = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5298    my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"};
5299    my $ClassName = $ClassId?get_ShortClass($ClassId, $Version):"";
5300    my $Header = $SymbolInfo{$Version}{$InfoId}{"Header"};
5301    if(my $HPath = $SymbolHeader{$Version}{$ClassName}{$ShortName})
5302    {
5303        if(get_filename($HPath) eq $Header)
5304        {
5305            my $HDir = get_filename(get_dirname($HPath));
5306            if($HDir ne "include"
5307            and $HDir=~/\A[a-z]+\Z/i) {
5308                return join_P($HDir, $Header);
5309            }
5310        }
5311    }
5312    return $Header;
5313}
5314
5315sub isInline($)
5316{ # "body: undefined" in the tree
5317  # -fkeep-inline-functions GCC option should be specified
5318    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5319    {
5320        if($Info=~/ undefined /i) {
5321            return 0;
5322        }
5323    }
5324    return 1;
5325}
5326
5327sub hasThrow($)
5328{
5329    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5330    {
5331        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5332            return getTreeAttr_Unql($1, "unql");
5333        }
5334    }
5335    return 1;
5336}
5337
5338sub getTypeId($)
5339{
5340    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5341    {
5342        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5343            return $1;
5344        }
5345    }
5346    return "";
5347}
5348
5349sub setTypeMemb($$)
5350{
5351    my ($TypeId, $TypeAttr) = @_;
5352    my $TypeType = $TypeAttr->{"Type"};
5353    my ($Pos, $UnnamedPos) = (0, 0);
5354    my $StaticFields = 0;
5355    if($TypeType eq "Enum")
5356    {
5357        my $MInfoId = getTreeAttr_Csts($TypeId);
5358        while($MInfoId)
5359        {
5360            $TypeAttr->{"Memb"}{$Pos}{"value"} = getEnumMembVal($MInfoId);
5361            my $MembName = getTreeStr(getTreeAttr_Purp($MInfoId));
5362            $TypeAttr->{"Memb"}{$Pos}{"name"} = $MembName;
5363            $EnumMembName_Id{$Version}{getTreeAttr_Valu($MInfoId)} = ($TypeAttr->{"NameSpace"})?$TypeAttr->{"NameSpace"}."::".$MembName:$MembName;
5364            $MInfoId = getNextElem($MInfoId);
5365            $Pos += 1;
5366        }
5367    }
5368    elsif($TypeType=~/\A(Struct|Class|Union)\Z/)
5369    {
5370        my $MInfoId = getTreeAttr_Flds($TypeId);
5371        while($MInfoId)
5372        {
5373            my $IType = $LibInfo{$Version}{"info_type"}{$MInfoId};
5374            my $MInfo = $LibInfo{$Version}{"info"}{$MInfoId};
5375            if(not $IType or $IType ne "field_decl")
5376            { # search for fields, skip other stuff in the declaration
5377
5378                if($IType eq "var_decl")
5379                { # static field
5380                    $StaticFields = 1;
5381                }
5382
5383                $MInfoId = getNextElem($MInfoId);
5384                next;
5385            }
5386            my $StructMembName = getTreeStr(getTreeAttr_Name($MInfoId));
5387            if(index($StructMembName, "_vptr.")==0)
5388            { # virtual tables
5389                $StructMembName = "_vptr";
5390            }
5391            if(not $StructMembName)
5392            { # unnamed fields
5393                if(index($TypeAttr->{"Name"}, "_type_info_pseudo")==-1)
5394                {
5395                    my $UnnamedTid = getTreeAttr_Type($MInfoId);
5396                    my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid));
5397                    if(isAnon($UnnamedTName))
5398                    { # rename unnamed fields to unnamed0, unnamed1, ...
5399                        $StructMembName = "unnamed".($UnnamedPos++);
5400                    }
5401                }
5402            }
5403            if(not $StructMembName)
5404            { # unnamed fields and base classes
5405                $MInfoId = getNextElem($MInfoId);
5406                next;
5407            }
5408            my $MembTypeId = getTreeAttr_Type($MInfoId);
5409            if(defined $MissedTypedef{$Version}{$MembTypeId})
5410            {
5411                if(my $AddedTid = $MissedTypedef{$Version}{$MembTypeId}{"Tid"}) {
5412                    $MembTypeId = $AddedTid;
5413                }
5414            }
5415
5416            $TypeAttr->{"Memb"}{$Pos}{"type"} = $MembTypeId;
5417            $TypeAttr->{"Memb"}{$Pos}{"name"} = $StructMembName;
5418            if((my $Access = getTreeAccess($MInfoId)) ne "public")
5419            { # marked only protected and private, public by default
5420                $TypeAttr->{"Memb"}{$Pos}{"access"} = $Access;
5421            }
5422            if($MInfo=~/spec:\s*mutable /)
5423            { # mutable fields
5424                $TypeAttr->{"Memb"}{$Pos}{"mutable"} = 1;
5425            }
5426            if(my $Algn = getAlgn($MInfoId)) {
5427                $TypeAttr->{"Memb"}{$Pos}{"algn"} = $Algn;
5428            }
5429            if(my $BFSize = getBitField($MInfoId))
5430            { # in bits
5431                $TypeAttr->{"Memb"}{$Pos}{"bitfield"} = $BFSize;
5432            }
5433            else
5434            { # in bytes
5435                if($TypeAttr->{"Memb"}{$Pos}{"algn"}==1)
5436                { # template
5437                    delete($TypeAttr->{"Memb"}{$Pos}{"algn"});
5438                }
5439                else {
5440                    $TypeAttr->{"Memb"}{$Pos}{"algn"} /= $BYTE_SIZE;
5441                }
5442            }
5443
5444            $MInfoId = getNextElem($MInfoId);
5445            $Pos += 1;
5446        }
5447    }
5448
5449    return $StaticFields;
5450}
5451
5452sub setFuncParams($)
5453{
5454    my $InfoId = $_[0];
5455    my $ParamInfoId = getTreeAttr_Args($InfoId);
5456
5457    my $FType = getFuncType($InfoId);
5458
5459    if($FType eq "Method")
5460    { # check type of "this" pointer
5461        my $ObjectTypeId = getTreeAttr_Type($ParamInfoId);
5462        if(my $ObjectName = $TypeInfo{$Version}{$ObjectTypeId}{"Name"})
5463        {
5464            if($ObjectName=~/\bconst(| volatile)\*const\b/) {
5465                $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5466            }
5467            if($ObjectName=~/\bvolatile\b/) {
5468                $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5469            }
5470        }
5471        else
5472        { # skip
5473            return 1;
5474        }
5475        # skip "this"-parameter
5476        # $ParamInfoId = getNextElem($ParamInfoId);
5477    }
5478    my ($Pos, $PPos, $Vtt_Pos) = (0, 0, -1);
5479    while($ParamInfoId)
5480    { # formal args
5481        my $ParamTypeId = getTreeAttr_Type($ParamInfoId);
5482        my $ParamName = getTreeStr(getTreeAttr_Name($ParamInfoId));
5483        if(not $ParamName)
5484        { # unnamed
5485            $ParamName = "p".($PPos+1);
5486        }
5487        if(defined $MissedTypedef{$Version}{$ParamTypeId})
5488        {
5489            if(my $AddedTid = $MissedTypedef{$Version}{$ParamTypeId}{"Tid"}) {
5490                $ParamTypeId = $AddedTid;
5491            }
5492        }
5493        my $PType = $TypeInfo{$Version}{$ParamTypeId}{"Type"};
5494        if(not $PType or $PType eq "Unknown") {
5495            return 1;
5496        }
5497        my $PTName = $TypeInfo{$Version}{$ParamTypeId}{"Name"};
5498        if(not $PTName) {
5499            return 1;
5500        }
5501        if($PTName eq "void") {
5502            last;
5503        }
5504        if($ParamName eq "__vtt_parm"
5505        and $TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void const**")
5506        {
5507            $Vtt_Pos = $Pos;
5508            $ParamInfoId = getNextElem($ParamInfoId);
5509            next;
5510        }
5511        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5512
5513        if(my %Base = get_BaseType($ParamTypeId, $Version))
5514        {
5515            if(defined $Base{"Template"}) {
5516                return 1;
5517            }
5518        }
5519
5520        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = $ParamName;
5521        if(my $Algn = getAlgn($ParamInfoId)) {
5522            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"algn"} = $Algn/$BYTE_SIZE;
5523        }
5524        if($LibInfo{$Version}{"info"}{$ParamInfoId}=~/spec:\s*register /)
5525        { # foo(register type arg)
5526            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"reg"} = 1;
5527        }
5528        $ParamInfoId = getNextElem($ParamInfoId);
5529        $Pos += 1;
5530        if($ParamName ne "this" or $FType ne "Method") {
5531            $PPos += 1;
5532        }
5533    }
5534    if(setFuncArgs($InfoId, $Vtt_Pos)) {
5535        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = "-1";
5536    }
5537    return 0;
5538}
5539
5540sub setFuncArgs($$)
5541{
5542    my ($InfoId, $Vtt_Pos) = @_;
5543    my $FuncTypeId = getFuncTypeId($InfoId);
5544    my $ParamListElemId = getTreeAttr_Prms($FuncTypeId);
5545    my $FType = getFuncType($InfoId);
5546
5547    if($FType eq "Method")
5548    {
5549        # skip "this"-parameter
5550        # $ParamListElemId = getNextElem($ParamListElemId);
5551    }
5552    if(not $ParamListElemId)
5553    { # foo(...)
5554        return 1;
5555    }
5556    my $HaveVoid = 0;
5557    my ($Pos, $PPos) = (0, 0);
5558    while($ParamListElemId)
5559    { # actual params: may differ from formal args
5560      # formal int*const
5561      # actual: int*
5562        if($Vtt_Pos!=-1 and $Pos==$Vtt_Pos)
5563        {
5564            $Vtt_Pos=-1;
5565            $ParamListElemId = getNextElem($ParamListElemId);
5566            next;
5567        }
5568        my $ParamTypeId = getTreeAttr_Valu($ParamListElemId);
5569        if($TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void")
5570        {
5571            $HaveVoid = 1;
5572            last;
5573        }
5574        else
5575        {
5576            if(not defined $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5577            {
5578                $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5579                if(not $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"})
5580                { # unnamed
5581                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = "p".($PPos+1);
5582                }
5583            }
5584            elsif(my $OldId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5585            {
5586                if($Pos>0 or getFuncType($InfoId) ne "Method")
5587                { # params
5588                    if($OldId ne $ParamTypeId)
5589                    {
5590                        my %Old_Pure = get_PureType($OldId, $TypeInfo{$Version});
5591                        my %New_Pure = get_PureType($ParamTypeId, $TypeInfo{$Version});
5592
5593                        if($Old_Pure{"Name"} ne $New_Pure{"Name"}) {
5594                            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5595                        }
5596                    }
5597                }
5598            }
5599        }
5600        if(my $PurpId = getTreeAttr_Purp($ParamListElemId))
5601        { # default arguments
5602            if(my $PurpType = $LibInfo{$Version}{"info_type"}{$PurpId})
5603            {
5604                if($PurpType eq "nop_expr")
5605                { # func ( const char* arg = (const char*)(void*)0 )
5606                    $PurpId = getTreeAttr_Op($PurpId);
5607                }
5608                my $Val = getInitVal($PurpId, $ParamTypeId);
5609                if(defined $Val) {
5610                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"default"} = $Val;
5611                }
5612            }
5613        }
5614        $ParamListElemId = getNextElem($ParamListElemId);
5615        if($Pos!=0 or $FType ne "Method") {
5616            $PPos += 1;
5617        }
5618        $Pos += 1;
5619    }
5620    return ($Pos>=1 and not $HaveVoid);
5621}
5622
5623sub getTreeAttr_Chan($)
5624{
5625    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5626    {
5627        if($Info=~/chan[ ]*:[ ]*@(\d+) /) {
5628            return $1;
5629        }
5630    }
5631    return "";
5632}
5633
5634sub getTreeAttr_Chain($)
5635{
5636    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5637    {
5638        if($Info=~/chain[ ]*:[ ]*@(\d+) /) {
5639            return $1;
5640        }
5641    }
5642    return "";
5643}
5644
5645sub getTreeAttr_Unql($)
5646{
5647    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5648    {
5649        if($Info=~/unql[ ]*:[ ]*@(\d+) /) {
5650            return $1;
5651        }
5652    }
5653    return "";
5654}
5655
5656sub getTreeAttr_Scpe($)
5657{
5658    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5659    {
5660        if($Info=~/scpe[ ]*:[ ]*@(\d+) /) {
5661            return $1;
5662        }
5663    }
5664    return "";
5665}
5666
5667sub getTreeAttr_Type($)
5668{
5669    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5670    {
5671        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5672            return $1;
5673        }
5674    }
5675    return "";
5676}
5677
5678sub getTreeAttr_Name($)
5679{
5680    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5681    {
5682        if($Info=~/name[ ]*:[ ]*@(\d+) /) {
5683            return $1;
5684        }
5685    }
5686    return "";
5687}
5688
5689sub getTreeAttr_Mngl($)
5690{
5691    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5692    {
5693        if($Info=~/mngl[ ]*:[ ]*@(\d+) /) {
5694            return $1;
5695        }
5696    }
5697    return "";
5698}
5699
5700sub getTreeAttr_Prms($)
5701{
5702    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5703    {
5704        if($Info=~/prms[ ]*:[ ]*@(\d+) /) {
5705            return $1;
5706        }
5707    }
5708    return "";
5709}
5710
5711sub getTreeAttr_Fncs($)
5712{
5713    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5714    {
5715        if($Info=~/fncs[ ]*:[ ]*@(\d+) /) {
5716            return $1;
5717        }
5718    }
5719    return "";
5720}
5721
5722sub getTreeAttr_Csts($)
5723{
5724    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5725    {
5726        if($Info=~/csts[ ]*:[ ]*@(\d+) /) {
5727            return $1;
5728        }
5729    }
5730    return "";
5731}
5732
5733sub getTreeAttr_Purp($)
5734{
5735    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5736    {
5737        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
5738            return $1;
5739        }
5740    }
5741    return "";
5742}
5743
5744sub getTreeAttr_Op($)
5745{
5746    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5747    {
5748        if($Info=~/op 0[ ]*:[ ]*@(\d+) /) {
5749            return $1;
5750        }
5751    }
5752    return "";
5753}
5754
5755sub getTreeAttr_Valu($)
5756{
5757    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5758    {
5759        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
5760            return $1;
5761        }
5762    }
5763    return "";
5764}
5765
5766sub getTreeAttr_Flds($)
5767{
5768    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5769    {
5770        if($Info=~/flds[ ]*:[ ]*@(\d+) /) {
5771            return $1;
5772        }
5773    }
5774    return "";
5775}
5776
5777sub getTreeAttr_Binf($)
5778{
5779    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5780    {
5781        if($Info=~/binf[ ]*:[ ]*@(\d+) /) {
5782            return $1;
5783        }
5784    }
5785    return "";
5786}
5787
5788sub getTreeAttr_Args($)
5789{
5790    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5791    {
5792        if($Info=~/args[ ]*:[ ]*@(\d+) /) {
5793            return $1;
5794        }
5795    }
5796    return "";
5797}
5798
5799sub getTreeValue($)
5800{
5801    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5802    {
5803        if($Info=~/low[ ]*:[ ]*([^ ]+) /) {
5804            return $1;
5805        }
5806    }
5807    return "";
5808}
5809
5810sub getTreeAccess($)
5811{
5812    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5813    {
5814        if($Info=~/accs[ ]*:[ ]*([a-zA-Z]+) /)
5815        {
5816            my $Access = $1;
5817            if($Access eq "prot") {
5818                return "protected";
5819            }
5820            elsif($Access eq "priv") {
5821                return "private";
5822            }
5823        }
5824        elsif($Info=~/ protected /)
5825        { # support for old GCC versions
5826            return "protected";
5827        }
5828        elsif($Info=~/ private /)
5829        { # support for old GCC versions
5830            return "private";
5831        }
5832    }
5833    return "public";
5834}
5835
5836sub setFuncAccess($)
5837{
5838    my $Access = getTreeAccess($_[0]);
5839    if($Access eq "protected") {
5840        $SymbolInfo{$Version}{$_[0]}{"Protected"} = 1;
5841    }
5842    elsif($Access eq "private") {
5843        $SymbolInfo{$Version}{$_[0]}{"Private"} = 1;
5844    }
5845}
5846
5847sub setTypeAccess($$)
5848{
5849    my ($TypeId, $TypeAttr) = @_;
5850    my $Access = getTreeAccess($TypeId);
5851    if($Access eq "protected") {
5852        $TypeAttr->{"Protected"} = 1;
5853    }
5854    elsif($Access eq "private") {
5855        $TypeAttr->{"Private"} = 1;
5856    }
5857}
5858
5859sub setFuncKind($)
5860{
5861    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5862    {
5863        if($Info=~/pseudo tmpl/) {
5864            $SymbolInfo{$Version}{$_[0]}{"PseudoTemplate"} = 1;
5865        }
5866        elsif($Info=~/ constructor /) {
5867            $SymbolInfo{$Version}{$_[0]}{"Constructor"} = 1;
5868        }
5869        elsif($Info=~/ destructor /) {
5870            $SymbolInfo{$Version}{$_[0]}{"Destructor"} = 1;
5871        }
5872    }
5873}
5874
5875sub getVirtSpec($)
5876{
5877    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5878    {
5879        if($Info=~/spec[ ]*:[ ]*pure /) {
5880            return "PureVirt";
5881        }
5882        elsif($Info=~/spec[ ]*:[ ]*virt /) {
5883            return "Virt";
5884        }
5885        elsif($Info=~/ pure\s+virtual /)
5886        { # support for old GCC versions
5887            return "PureVirt";
5888        }
5889        elsif($Info=~/ virtual /)
5890        { # support for old GCC versions
5891            return "Virt";
5892        }
5893    }
5894    return "";
5895}
5896
5897sub getFuncLink($)
5898{
5899    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5900    {
5901        if($Info=~/link[ ]*:[ ]*static /) {
5902            return "Static";
5903        }
5904        elsif($Info=~/link[ ]*:[ ]*([a-zA-Z]+) /) {
5905            return $1;
5906        }
5907    }
5908    return "";
5909}
5910
5911sub select_Symbol_NS($$)
5912{
5913    my ($Symbol, $LibVersion) = @_;
5914    return "" if(not $Symbol or not $LibVersion);
5915    my $NS = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
5916    if(not $NS)
5917    {
5918        if(my $Class = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) {
5919            $NS = $TypeInfo{$LibVersion}{$Class}{"NameSpace"};
5920        }
5921    }
5922    if($NS)
5923    {
5924        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5925            return $NS;
5926        }
5927        else
5928        {
5929            while($NS=~s/::[^:]+\Z//)
5930            {
5931                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5932                    return $NS;
5933                }
5934            }
5935        }
5936    }
5937
5938    return "";
5939}
5940
5941sub select_Type_NS($$)
5942{
5943    my ($TypeName, $LibVersion) = @_;
5944    return "" if(not $TypeName or not $LibVersion);
5945    if(my $NS = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$TypeName}}{"NameSpace"})
5946    {
5947        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5948            return $NS;
5949        }
5950        else
5951        {
5952            while($NS=~s/::[^:]+\Z//)
5953            {
5954                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5955                    return $NS;
5956                }
5957            }
5958        }
5959    }
5960    return "";
5961}
5962
5963sub getNameSpace($)
5964{
5965    my $InfoId = $_[0];
5966    if(my $NSInfoId = getTreeAttr_Scpe($InfoId))
5967    {
5968        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
5969        {
5970            if($InfoType eq "namespace_decl")
5971            {
5972                if($LibInfo{$Version}{"info"}{$NSInfoId}=~/name[ ]*:[ ]*@(\d+) /)
5973                {
5974                    my $NameSpace = getTreeStr($1);
5975                    if($NameSpace eq "::")
5976                    { # global namespace
5977                        return "";
5978                    }
5979                    if(my $BaseNameSpace = getNameSpace($NSInfoId)) {
5980                        $NameSpace = $BaseNameSpace."::".$NameSpace;
5981                    }
5982                    $NestedNameSpaces{$Version}{$NameSpace} = 1;
5983                    return $NameSpace;
5984                }
5985                else {
5986                    return "";
5987                }
5988            }
5989            elsif($InfoType ne "function_decl")
5990            { # inside data type
5991                my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NSInfoId), $NSInfoId);
5992                return $Name;
5993            }
5994        }
5995    }
5996    return "";
5997}
5998
5999sub getEnumMembVal($)
6000{
6001    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6002    {
6003        if($Info=~/valu[ ]*:[ ]*\@(\d+)/)
6004        {
6005            if(my $VInfo = $LibInfo{$Version}{"info"}{$1})
6006            {
6007                if($VInfo=~/cnst[ ]*:[ ]*\@(\d+)/)
6008                { # in newer versions of GCC the value is in the "const_decl->cnst" node
6009                    return getTreeValue($1);
6010                }
6011                else
6012                { # some old versions of GCC (3.3) have the value in the "integer_cst" node
6013                    return getTreeValue($1);
6014                }
6015            }
6016        }
6017    }
6018    return "";
6019}
6020
6021sub getSize($)
6022{
6023    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6024    {
6025        if($Info=~/size[ ]*:[ ]*\@(\d+)/) {
6026            return getTreeValue($1);
6027        }
6028    }
6029    return 0;
6030}
6031
6032sub getAlgn($)
6033{
6034    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6035    {
6036        if($Info=~/algn[ ]*:[ ]*(\d+) /) {
6037            return $1;
6038        }
6039    }
6040    return "";
6041}
6042
6043sub getBitField($)
6044{
6045    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6046    {
6047        if($Info=~/ bitfield /) {
6048            return getSize($_[0]);
6049        }
6050    }
6051    return 0;
6052}
6053
6054sub getNextElem($)
6055{
6056    if(my $Chan = getTreeAttr_Chan($_[0])) {
6057        return $Chan;
6058    }
6059    elsif(my $Chain = getTreeAttr_Chain($_[0])) {
6060        return $Chain;
6061    }
6062    return "";
6063}
6064
6065sub registerHeader($$)
6066{ # input: absolute path of header, relative path or name
6067    my ($Header, $LibVersion) = @_;
6068    if(not $Header) {
6069        return "";
6070    }
6071    if(is_abs($Header) and not -f $Header)
6072    { # incorrect absolute path
6073        exitStatus("Access_Error", "can't access \'$Header\'");
6074    }
6075    if(skipHeader($Header, $LibVersion))
6076    { # skip
6077        return "";
6078    }
6079    if(my $Header_Path = identifyHeader($Header, $LibVersion))
6080    {
6081        detect_header_includes($Header_Path, $LibVersion);
6082
6083        if(defined $Tolerance and $Tolerance=~/3/)
6084        { # 3 - skip headers that include non-Linux headers
6085            if($OSgroup ne "windows")
6086            {
6087                foreach my $Inc (keys(%{$Header_Includes{$LibVersion}{$Header_Path}}))
6088                {
6089                    if(specificHeader($Inc, "windows")) {
6090                        return "";
6091                    }
6092                }
6093            }
6094        }
6095
6096        if(my $RHeader_Path = $Header_ErrorRedirect{$LibVersion}{$Header_Path})
6097        { # redirect
6098            if($Registered_Headers{$LibVersion}{$RHeader_Path}{"Identity"}
6099            or skipHeader($RHeader_Path, $LibVersion))
6100            { # skip
6101                return "";
6102            }
6103            $Header_Path = $RHeader_Path;
6104        }
6105        elsif($Header_ShouldNotBeUsed{$LibVersion}{$Header_Path})
6106        { # skip
6107            return "";
6108        }
6109
6110        if(my $HName = get_filename($Header_Path))
6111        { # register
6112            $Registered_Headers{$LibVersion}{$Header_Path}{"Identity"} = $HName;
6113            $HeaderName_Paths{$LibVersion}{$HName}{$Header_Path} = 1;
6114        }
6115
6116        if(($Header=~/\.(\w+)\Z/ and $1 ne "h")
6117        or $Header!~/\.(\w+)\Z/)
6118        { # hpp, hh, etc.
6119            setLanguage($LibVersion, "C++");
6120            $CPP_HEADERS = 1;
6121        }
6122
6123        if($CheckHeadersOnly
6124        and $Header=~/(\A|\/)c\+\+(\/|\Z)/)
6125        { # /usr/include/c++/4.6.1/...
6126            $STDCXX_TESTING = 1;
6127        }
6128
6129        return $Header_Path;
6130    }
6131    return "";
6132}
6133
6134sub registerDir($$$)
6135{
6136    my ($Dir, $WithDeps, $LibVersion) = @_;
6137    $Dir=~s/[\/\\]+\Z//g;
6138    return if(not $LibVersion or not $Dir or not -d $Dir);
6139    $Dir = get_abs_path($Dir);
6140
6141    my $Mode = "All";
6142    if($WithDeps)
6143    {
6144        if($RegisteredDirs{$LibVersion}{$Dir}{1}) {
6145            return;
6146        }
6147        elsif($RegisteredDirs{$LibVersion}{$Dir}{0}) {
6148            $Mode = "DepsOnly";
6149        }
6150    }
6151    else
6152    {
6153        if($RegisteredDirs{$LibVersion}{$Dir}{1}
6154        or $RegisteredDirs{$LibVersion}{$Dir}{0}) {
6155            return;
6156        }
6157    }
6158    $Header_Dependency{$LibVersion}{$Dir} = 1;
6159    $RegisteredDirs{$LibVersion}{$Dir}{$WithDeps} = 1;
6160    if($Mode eq "DepsOnly")
6161    {
6162        foreach my $Path (cmd_find($Dir,"d")) {
6163            $Header_Dependency{$LibVersion}{$Path} = 1;
6164        }
6165        return;
6166    }
6167    foreach my $Path (sort {length($b)<=>length($a)} cmd_find($Dir,"f"))
6168    {
6169        if($WithDeps)
6170        {
6171            my $SubDir = $Path;
6172            while(($SubDir = get_dirname($SubDir)) ne $Dir)
6173            { # register all sub directories
6174                $Header_Dependency{$LibVersion}{$SubDir} = 1;
6175            }
6176        }
6177        next if(is_not_header($Path));
6178        next if(ignore_path($Path));
6179        # Neighbors
6180        foreach my $Part (get_prefixes($Path)) {
6181            $Include_Neighbors{$LibVersion}{$Part} = $Path;
6182        }
6183    }
6184    if(get_filename($Dir) eq "include")
6185    { # search for "lib/include/" directory
6186        my $LibDir = $Dir;
6187        if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) {
6188            registerDir($LibDir, $WithDeps, $LibVersion);
6189        }
6190    }
6191}
6192
6193sub parse_redirect($$$)
6194{
6195    my ($Content, $Path, $LibVersion) = @_;
6196    my @Errors = ();
6197    while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) {
6198        push(@Errors, $1);
6199    }
6200    my $Redirect = "";
6201    foreach (@Errors)
6202    {
6203        s/\s{2,}/ /g;
6204        if(/(only|must\ include
6205        |update\ to\ include
6206        |replaced\ with
6207        |replaced\ by|renamed\ to
6208        |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix)
6209        {
6210            $Redirect = $2;
6211            last;
6212        }
6213        elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i)
6214        {
6215            $Redirect = $2;
6216            last;
6217        }
6218        elsif(/this\ header\ should\ not\ be\ used
6219         |programs\ should\ not\ directly\ include
6220         |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header))
6221         |is\ not\ supported\ API\ for\ general\ use
6222         |do\ not\ use
6223         |should\ not\ be\ (used|using)
6224         |cannot\ be\ included\ directly/ix and not /\ from\ /i) {
6225            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6226        }
6227    }
6228    if($Redirect)
6229    {
6230        $Redirect=~s/\A<//g;
6231        $Redirect=~s/>\Z//g;
6232    }
6233    return $Redirect;
6234}
6235
6236sub parse_includes($$)
6237{
6238    my ($Content, $Path) = @_;
6239    my %Includes = ();
6240    while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m)
6241    { # C/C++: include, Objective C/C++: import directive
6242        my $Header = $2;
6243        my $Method = substr($Header, 0, 1, "");
6244        substr($Header, length($Header)-1, 1, "");
6245        $Header = path_format($Header, $OSgroup);
6246        if($Method eq "\"" or is_abs($Header))
6247        {
6248            if(-e join_P(get_dirname($Path), $Header))
6249            { # relative path exists
6250                $Includes{$Header} = -1;
6251            }
6252            else
6253            { # include "..." that doesn't exist is equal to include <...>
6254                $Includes{$Header} = 2;
6255            }
6256        }
6257        else {
6258            $Includes{$Header} = 1;
6259        }
6260    }
6261    if($ExtraInfo)
6262    {
6263        while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m)
6264        { # FT_FREETYPE_H
6265            $Includes{$2} = 0;
6266        }
6267    }
6268    return \%Includes;
6269}
6270
6271sub ignore_path($)
6272{
6273    my $Path = $_[0];
6274    if($Path=~/\~\Z/)
6275    {# skipping system backup files
6276        return 1;
6277    }
6278    if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/)
6279    {# skipping hidden .svn, .git, .bzr, .hg and CVS directories
6280        return 1;
6281    }
6282    return 0;
6283}
6284
6285sub sortByWord($$)
6286{
6287    my ($ArrRef, $W) = @_;
6288    return if(length($W)<2);
6289    @{$ArrRef} = sort {get_filename($b)=~/\Q$W\E/i<=>get_filename($a)=~/\Q$W\E/i} @{$ArrRef};
6290}
6291
6292sub sortHeaders($$)
6293{
6294    my ($H1, $H2) = @_;
6295
6296    $H1=~s/\.[a-z]+\Z//ig;
6297    $H2=~s/\.[a-z]+\Z//ig;
6298
6299    my $Hname1 = get_filename($H1);
6300    my $Hname2 = get_filename($H2);
6301    my $HDir1 = get_dirname($H1);
6302    my $HDir2 = get_dirname($H2);
6303    my $Dirname1 = get_filename($HDir1);
6304    my $Dirname2 = get_filename($HDir2);
6305
6306    $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6307    $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6308
6309    if($_[0] eq $_[1]
6310    or $H1 eq $H2) {
6311        return 0;
6312    }
6313    elsif($H1=~/\A\Q$H2\E/) {
6314        return 1;
6315    }
6316    elsif($H2=~/\A\Q$H1\E/) {
6317        return -1;
6318    }
6319    elsif($HDir1=~/\Q$Hname1\E/i
6320    and $HDir2!~/\Q$Hname2\E/i)
6321    { # include/glib-2.0/glib.h
6322        return -1;
6323    }
6324    elsif($HDir2=~/\Q$Hname2\E/i
6325    and $HDir1!~/\Q$Hname1\E/i)
6326    { # include/glib-2.0/glib.h
6327        return 1;
6328    }
6329    elsif($Hname1=~/\Q$Dirname1\E/i
6330    and $Hname2!~/\Q$Dirname2\E/i)
6331    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6332        return -1;
6333    }
6334    elsif($Hname2=~/\Q$Dirname2\E/i
6335    and $Hname1!~/\Q$Dirname1\E/i)
6336    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6337        return 1;
6338    }
6339    elsif($Hname1=~/(config|lib|util)/i
6340    and $Hname2!~/(config|lib|util)/i)
6341    { # include/alsa/asoundlib.h
6342        return -1;
6343    }
6344    elsif($Hname2=~/(config|lib|util)/i
6345    and $Hname1!~/(config|lib|util)/i)
6346    { # include/alsa/asoundlib.h
6347        return 1;
6348    }
6349    else
6350    {
6351        my $R1 = checkRelevance($H1);
6352        my $R2 = checkRelevance($H2);
6353        if($R1 and not $R2)
6354        { # libebook/e-book.h
6355            return -1;
6356        }
6357        elsif($R2 and not $R1)
6358        { # libebook/e-book.h
6359            return 1;
6360        }
6361        else
6362        {
6363            return (lc($H1) cmp lc($H2));
6364        }
6365    }
6366}
6367
6368sub searchForHeaders($)
6369{
6370    my $LibVersion = $_[0];
6371
6372    # gcc standard include paths
6373    registerGccHeaders();
6374
6375    if($COMMON_LANGUAGE{$LibVersion} eq "C++" and not $STDCXX_TESTING)
6376    { # c++ standard include paths
6377        registerCppHeaders();
6378    }
6379
6380    # processing header paths
6381    foreach my $Path (@{$Descriptor{$LibVersion}{"IncludePaths"}},
6382    @{$Descriptor{$LibVersion}{"AddIncludePaths"}})
6383    {
6384        my $IPath = $Path;
6385        if($SystemRoot)
6386        {
6387            if(is_abs($Path)) {
6388                $Path = $SystemRoot.$Path;
6389            }
6390        }
6391        if(not -e $Path) {
6392            exitStatus("Access_Error", "can't access \'$Path\'");
6393        }
6394        elsif(-f $Path) {
6395            exitStatus("Access_Error", "\'$Path\' - not a directory");
6396        }
6397        elsif(-d $Path)
6398        {
6399            $Path = get_abs_path($Path);
6400            registerDir($Path, 0, $LibVersion);
6401            if(grep {$IPath eq $_} @{$Descriptor{$LibVersion}{"AddIncludePaths"}}) {
6402                push(@{$Add_Include_Paths{$LibVersion}}, $Path);
6403            }
6404            else {
6405                push(@{$Include_Paths{$LibVersion}}, $Path);
6406            }
6407        }
6408    }
6409    if(@{$Include_Paths{$LibVersion}}) {
6410        $INC_PATH_AUTODETECT{$LibVersion} = 0;
6411    }
6412
6413    # registering directories
6414    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6415    {
6416        next if(not -e $Path);
6417        $Path = get_abs_path($Path);
6418        $Path = path_format($Path, $OSgroup);
6419        if(-d $Path) {
6420            registerDir($Path, 1, $LibVersion);
6421        }
6422        elsif(-f $Path)
6423        {
6424            my $Dir = get_dirname($Path);
6425            if(not grep { $Dir eq $_ } (@{$SystemPaths{"include"}})
6426            and not $LocalIncludes{$Dir})
6427            {
6428                registerDir($Dir, 1, $LibVersion);
6429                # if(my $OutDir = get_dirname($Dir))
6430                # { # registering the outer directory
6431                #     if(not grep { $OutDir eq $_ } (@{$SystemPaths{"include"}})
6432                #     and not $LocalIncludes{$OutDir}) {
6433                #         registerDir($OutDir, 0, $LibVersion);
6434                #     }
6435                # }
6436            }
6437        }
6438    }
6439
6440    # clean memory
6441    %RegisteredDirs = ();
6442
6443    # registering headers
6444    my $Position = 0;
6445    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6446    {
6447        if(is_abs($Dest) and not -e $Dest) {
6448            exitStatus("Access_Error", "can't access \'$Dest\'");
6449        }
6450        $Dest = path_format($Dest, $OSgroup);
6451        if(is_header($Dest, 1, $LibVersion))
6452        {
6453            if(my $HPath = registerHeader($Dest, $LibVersion)) {
6454                $Registered_Headers{$LibVersion}{$HPath}{"Pos"} = $Position++;
6455            }
6456        }
6457        elsif(-d $Dest)
6458        {
6459            my @Registered = ();
6460            foreach my $Path (cmd_find($Dest,"f"))
6461            {
6462                next if(ignore_path($Path));
6463                next if(not is_header($Path, 0, $LibVersion));
6464                if(my $HPath = registerHeader($Path, $LibVersion)) {
6465                    push(@Registered, $HPath);
6466                }
6467            }
6468            @Registered = sort {sortHeaders($a, $b)} @Registered;
6469            sortByWord(\@Registered, $TargetLibraryShortName);
6470            foreach my $Path (@Registered) {
6471                $Registered_Headers{$LibVersion}{$Path}{"Pos"} = $Position++;
6472            }
6473        }
6474        else {
6475            exitStatus("Access_Error", "can't identify \'$Dest\' as a header file");
6476        }
6477    }
6478
6479    if(defined $Tolerance and $Tolerance=~/4/)
6480    { # 4 - skip headers included by others
6481        foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
6482        {
6483            if(defined $Header_Includes_R{$LibVersion}{$Path}) {
6484                delete($Registered_Headers{$LibVersion}{$Path});
6485            }
6486        }
6487    }
6488
6489    if(my $HList = $Descriptor{$LibVersion}{"IncludePreamble"})
6490    { # preparing preamble headers
6491        foreach my $Header (split(/\s*\n\s*/, $HList))
6492        {
6493            if(is_abs($Header) and not -f $Header) {
6494                exitStatus("Access_Error", "can't access file \'$Header\'");
6495            }
6496            $Header = path_format($Header, $OSgroup);
6497            if(my $Header_Path = is_header($Header, 1, $LibVersion))
6498            {
6499                next if(skipHeader($Header_Path, $LibVersion));
6500                push_U($Include_Preamble{$LibVersion}, $Header_Path);
6501            }
6502            else {
6503                exitStatus("Access_Error", "can't identify \'$Header\' as a header file");
6504            }
6505        }
6506    }
6507    foreach my $Header_Name (keys(%{$HeaderName_Paths{$LibVersion}}))
6508    { # set relative paths (for duplicates)
6509        if(keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})>=2)
6510        { # search for duplicates
6511            my $FirstPath = (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))[0];
6512            my $Prefix = get_dirname($FirstPath);
6513            while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/)
6514            { # detect a shortest distinguishing prefix
6515                my $NewPrefix = $1;
6516                my %Identity = ();
6517                foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6518                {
6519                    if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) {
6520                        $Identity{$Path} = $1;
6521                    }
6522                }
6523                if(keys(%Identity)==keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6524                { # all names are different with current prefix
6525                    foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) {
6526                        $Registered_Headers{$LibVersion}{$Path}{"Identity"} = $Identity{$Path};
6527                    }
6528                    last;
6529                }
6530                $Prefix = $NewPrefix; # increase prefix
6531            }
6532        }
6533    }
6534
6535    # clean memory
6536    %HeaderName_Paths = ();
6537
6538    foreach my $HeaderName (keys(%{$Include_Order{$LibVersion}}))
6539    { # ordering headers according to descriptor
6540        my $PairName = $Include_Order{$LibVersion}{$HeaderName};
6541        my ($Pos, $PairPos) = (-1, -1);
6542        my ($Path, $PairPath) = ();
6543        my @Paths = keys(%{$Registered_Headers{$LibVersion}});
6544        @Paths = sort {int($Registered_Headers{$LibVersion}{$a}{"Pos"})<=>int($Registered_Headers{$LibVersion}{$b}{"Pos"})} @Paths;
6545        foreach my $Header_Path (@Paths)
6546        {
6547            if(get_filename($Header_Path) eq $PairName)
6548            {
6549                $PairPos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6550                $PairPath = $Header_Path;
6551            }
6552            if(get_filename($Header_Path) eq $HeaderName)
6553            {
6554                $Pos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6555                $Path = $Header_Path;
6556            }
6557        }
6558        if($PairPos!=-1 and $Pos!=-1
6559        and int($PairPos)<int($Pos))
6560        {
6561            my %Tmp = %{$Registered_Headers{$LibVersion}{$Path}};
6562            %{$Registered_Headers{$LibVersion}{$Path}} = %{$Registered_Headers{$LibVersion}{$PairPath}};
6563            %{$Registered_Headers{$LibVersion}{$PairPath}} = %Tmp;
6564        }
6565    }
6566    if(not keys(%{$Registered_Headers{$LibVersion}})) {
6567        exitStatus("Error", "header files are not found in the ".$Descriptor{$LibVersion}{"Version"});
6568    }
6569}
6570
6571sub detect_real_includes($$)
6572{
6573    my ($AbsPath, $LibVersion) = @_;
6574    return () if(not $LibVersion or not $AbsPath or not -e $AbsPath);
6575    if($Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}
6576    or keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6577        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6578    }
6579    $Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}=1;
6580
6581    my $Path = callPreprocessor($AbsPath, "", $LibVersion);
6582    return () if(not $Path);
6583    open(PREPROC, $Path);
6584    while(<PREPROC>)
6585    {
6586        if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/)
6587        {
6588            my $Include = path_format($1, $OSgroup);
6589            if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) {
6590                next;
6591            }
6592            if($Include eq $AbsPath) {
6593                next;
6594            }
6595            $RecursiveIncludes{$LibVersion}{$AbsPath}{$Include} = 1;
6596        }
6597    }
6598    close(PREPROC);
6599    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6600}
6601
6602sub detect_header_includes($$)
6603{
6604    my ($Path, $LibVersion) = @_;
6605    return if(not $LibVersion or not $Path);
6606    if(defined $Cache{"detect_header_includes"}{$LibVersion}{$Path}) {
6607        return;
6608    }
6609    $Cache{"detect_header_includes"}{$LibVersion}{$Path}=1;
6610
6611    if(not -e $Path) {
6612        return;
6613    }
6614
6615    my $Content = readFile($Path);
6616    if(my $Redirect = parse_redirect($Content, $Path, $LibVersion))
6617    { # detect error directive in headers
6618        if(my $RedirectPath = identifyHeader($Redirect, $LibVersion))
6619        {
6620            if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) {
6621                $RedirectPath = identifyHeader(get_filename($Redirect), $LibVersion);
6622            }
6623            if($RedirectPath ne $Path) {
6624                $Header_ErrorRedirect{$LibVersion}{$Path} = $RedirectPath;
6625            }
6626        }
6627        else
6628        { # can't find
6629            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6630        }
6631    }
6632    if(my $Inc = parse_includes($Content, $Path))
6633    {
6634        foreach my $Include (keys(%{$Inc}))
6635        { # detect includes
6636            $Header_Includes{$LibVersion}{$Path}{$Include} = $Inc->{$Include};
6637
6638            if(defined $Tolerance and $Tolerance=~/4/)
6639            {
6640                if(my $HPath = identifyHeader($Include, $LibVersion))
6641                {
6642                    $Header_Includes_R{$LibVersion}{$HPath}{$Path} = 1;
6643                }
6644            }
6645        }
6646    }
6647}
6648
6649sub fromLibc($)
6650{ # system GLIBC header
6651    my $Path = $_[0];
6652    my ($Dir, $Name) = separate_path($Path);
6653    if($OStarget eq "symbian")
6654    {
6655        if(get_filename($Dir) eq "libc" and $GlibcHeader{$Name})
6656        { # epoc32/include/libc/{stdio, ...}.h
6657            return 1;
6658        }
6659    }
6660    else
6661    {
6662        if($Dir eq "/usr/include" and $GlibcHeader{$Name})
6663        { # /usr/include/{stdio, ...}.h
6664            return 1;
6665        }
6666    }
6667    return 0;
6668}
6669
6670sub isLibcDir($)
6671{ # system GLIBC directory
6672    my $Dir = $_[0];
6673    my ($OutDir, $Name) = separate_path($Dir);
6674    if($OStarget eq "symbian")
6675    {
6676        if(get_filename($OutDir) eq "libc"
6677        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6678        { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h
6679            return 1;
6680        }
6681    }
6682    else
6683    { # linux
6684        if($OutDir eq "/usr/include"
6685        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6686        { # /usr/include/{sys,bits,asm,asm-*}/*.h
6687            return 1;
6688        }
6689    }
6690    return 0;
6691}
6692
6693sub detect_recursive_includes($$)
6694{
6695    my ($AbsPath, $LibVersion) = @_;
6696    return () if(not $AbsPath);
6697    if(isCyclical(\@RecurInclude, $AbsPath)) {
6698        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6699    }
6700    my ($AbsDir, $Name) = separate_path($AbsPath);
6701    if(isLibcDir($AbsDir))
6702    { # system GLIBC internals
6703        return () if(not $ExtraInfo);
6704    }
6705    if(keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6706        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6707    }
6708    return () if($OSgroup ne "windows" and $Name=~/windows|win32|win64/i);
6709
6710    if($MAIN_CPP_DIR and $AbsPath=~/\A\Q$MAIN_CPP_DIR\E/ and not $STDCXX_TESTING)
6711    { # skip /usr/include/c++/*/ headers
6712        return () if(not $ExtraInfo);
6713    }
6714
6715    push(@RecurInclude, $AbsPath);
6716    if(grep { $AbsDir eq $_ } @DefaultGccPaths
6717    or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath)))
6718    { # check "real" (non-"model") include paths
6719        my @Paths = detect_real_includes($AbsPath, $LibVersion);
6720        pop(@RecurInclude);
6721        return @Paths;
6722    }
6723    if(not keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) {
6724        detect_header_includes($AbsPath, $LibVersion);
6725    }
6726    foreach my $Include (keys(%{$Header_Includes{$LibVersion}{$AbsPath}}))
6727    {
6728        my $IncType = $Header_Includes{$LibVersion}{$AbsPath}{$Include};
6729        my $HPath = "";
6730        if($IncType<0)
6731        { # for #include "..."
6732            my $Candidate = join_P($AbsDir, $Include);
6733            if(-f $Candidate) {
6734                $HPath = realpath($Candidate);
6735            }
6736        }
6737        elsif($IncType>0
6738        and $Include=~/[\/\\]/) # and not find_in_defaults($Include)
6739        { # search for the nearest header
6740          # QtCore/qabstractanimation.h includes <QtCore/qobject.h>
6741            my $Candidate = join_P(get_dirname($AbsDir), $Include);
6742            if(-f $Candidate) {
6743                $HPath = $Candidate;
6744            }
6745        }
6746        if(not $HPath) {
6747            $HPath = identifyHeader($Include, $LibVersion);
6748        }
6749        next if(not $HPath);
6750        if($HPath eq $AbsPath) {
6751            next;
6752        }
6753
6754        if($Debug)
6755        { # boundary headers
6756#             if($HPath=~/vtk/ and $AbsPath!~/vtk/)
6757#             {
6758#                 print STDERR "$AbsPath -> $HPath\n";
6759#             }
6760        }
6761
6762        $RecursiveIncludes{$LibVersion}{$AbsPath}{$HPath} = $IncType;
6763        if($IncType>0)
6764        { # only include <...>, skip include "..." prefixes
6765            $Header_Include_Prefix{$LibVersion}{$AbsPath}{$HPath}{get_dirname($Include)} = 1;
6766        }
6767        foreach my $IncPath (detect_recursive_includes($HPath, $LibVersion))
6768        {
6769            if($IncPath eq $AbsPath) {
6770                next;
6771            }
6772            my $RIncType = $RecursiveIncludes{$LibVersion}{$HPath}{$IncPath};
6773            if($RIncType==-1)
6774            { # include "..."
6775                $RIncType = $IncType;
6776            }
6777            elsif($RIncType==2)
6778            {
6779                if($IncType!=-1) {
6780                    $RIncType = $IncType;
6781                }
6782            }
6783            $RecursiveIncludes{$LibVersion}{$AbsPath}{$IncPath} = $RIncType;
6784            foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$HPath}{$IncPath}})) {
6785                $Header_Include_Prefix{$LibVersion}{$AbsPath}{$IncPath}{$Prefix} = 1;
6786            }
6787        }
6788        foreach my $Dep (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}}))
6789        {
6790            if($GlibcHeader{get_filename($Dep)} and keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}})>=2
6791            and defined $Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""})
6792            { # distinguish math.h from glibc and math.h from the tested library
6793                delete($Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""});
6794                last;
6795            }
6796        }
6797    }
6798    pop(@RecurInclude);
6799    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6800}
6801
6802sub find_in_framework($$$)
6803{
6804    my ($Header, $Framework, $LibVersion) = @_;
6805    return "" if(not $Header or not $Framework or not $LibVersion);
6806    if(defined $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}) {
6807        return $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header};
6808    }
6809    foreach my $Dependency (sort {get_depth($a)<=>get_depth($b)} keys(%{$Header_Dependency{$LibVersion}}))
6810    {
6811        if(get_filename($Dependency) eq $Framework
6812        and -f get_dirname($Dependency)."/".$Header) {
6813            return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = get_dirname($Dependency));
6814        }
6815    }
6816    return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = "");
6817}
6818
6819sub find_in_defaults($)
6820{
6821    my $Header = $_[0];
6822    return "" if(not $Header);
6823    if(defined $Cache{"find_in_defaults"}{$Header}) {
6824        return $Cache{"find_in_defaults"}{$Header};
6825    }
6826    foreach my $Dir (@DefaultIncPaths,
6827                     @DefaultGccPaths,
6828                     @DefaultCppPaths,
6829                     @UsersIncPath)
6830    {
6831        next if(not $Dir);
6832        if(-f $Dir."/".$Header) {
6833            return ($Cache{"find_in_defaults"}{$Header}=$Dir);
6834        }
6835    }
6836    return ($Cache{"find_in_defaults"}{$Header}="");
6837}
6838
6839sub cmp_paths($$)
6840{
6841    my ($Path1, $Path2) = @_;
6842    my @Parts1 = split(/[\/\\]/, $Path1);
6843    my @Parts2 = split(/[\/\\]/, $Path2);
6844    foreach my $Num (0 .. $#Parts1)
6845    {
6846        my $Part1 = $Parts1[$Num];
6847        my $Part2 = $Parts2[$Num];
6848        if($GlibcDir{$Part1}
6849        and not $GlibcDir{$Part2}) {
6850            return 1;
6851        }
6852        elsif($GlibcDir{$Part2}
6853        and not $GlibcDir{$Part1}) {
6854            return -1;
6855        }
6856        elsif($Part1=~/glib/
6857        and $Part2!~/glib/) {
6858            return 1;
6859        }
6860        elsif($Part1!~/glib/
6861        and $Part2=~/glib/) {
6862            return -1;
6863        }
6864        elsif(my $CmpRes = ($Part1 cmp $Part2)) {
6865            return $CmpRes;
6866        }
6867    }
6868    return 0;
6869}
6870
6871sub checkRelevance($)
6872{
6873    my $Path = $_[0];
6874    return 0 if(not $Path);
6875
6876    if($SystemRoot) {
6877        $Path = cut_path_prefix($Path, $SystemRoot);
6878    }
6879
6880    my $Name = lc(get_filename($Path));
6881    my $Dir = lc(get_dirname($Path));
6882
6883    $Name=~s/\.\w+\Z//g; # remove extension (.h)
6884
6885    foreach my $Token (split(/[_\d\W]+/, $Name))
6886    {
6887        my $Len = length($Token);
6888        next if($Len<=1);
6889        if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/)
6890        { # include/evolution-data-server-1.4/libebook/e-book.h
6891            return 1;
6892        }
6893        if($Len>=4 and index($Dir, $Token)!=-1)
6894        { # include/gupnp-1.0/libgupnp/gupnp-context.h
6895            return 1;
6896        }
6897    }
6898    return 0;
6899}
6900
6901sub checkFamily(@)
6902{
6903    my @Paths = @_;
6904    return 1 if($#Paths<=0);
6905    my %Prefix = ();
6906    foreach my $Path (@Paths)
6907    {
6908        if($SystemRoot) {
6909            $Path = cut_path_prefix($Path, $SystemRoot);
6910        }
6911        if(my $Dir = get_dirname($Path))
6912        {
6913            $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix
6914            $Prefix{$Dir} += 1;
6915            $Prefix{get_dirname($Dir)} += 1;
6916        }
6917    }
6918    foreach (sort keys(%Prefix))
6919    {
6920        if(get_depth($_)>=3
6921        and $Prefix{$_}==$#Paths+1) {
6922            return 1;
6923        }
6924    }
6925    return 0;
6926}
6927
6928sub isAcceptable($$$)
6929{
6930    my ($Header, $Candidate, $LibVersion) = @_;
6931    my $HName = get_filename($Header);
6932    if(get_dirname($Header))
6933    { # with prefix
6934        return 1;
6935    }
6936    if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/)
6937    { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/
6938        return 1;
6939    }
6940    if(checkRelevance($Candidate))
6941    { # allow to search for atk.h in /usr/include/atk-1.0/atk/
6942        return 1;
6943    }
6944    if(checkFamily(getSystemHeaders($HName, $LibVersion)))
6945    { # /usr/include/qt4/QtNetwork/qsslconfiguration.h
6946      # /usr/include/qt4/Qt/qsslconfiguration.h
6947        return 1;
6948    }
6949    if($OStarget eq "symbian")
6950    {
6951        if($Candidate=~/[\/\\]stdapis[\/\\]/) {
6952            return 1;
6953        }
6954    }
6955    return 0;
6956}
6957
6958sub isRelevant($$$)
6959{ # disallow to search for "abstract" headers in too deep directories
6960    my ($Header, $Candidate, $LibVersion) = @_;
6961    my $HName = get_filename($Header);
6962    if($OStarget eq "symbian")
6963    {
6964        if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) {
6965            return 0;
6966        }
6967    }
6968    if($OStarget ne "bsd")
6969    {
6970        if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/)
6971        { # openssh: skip /usr/lib/bcc/include/bsd/signal.h
6972            return 0;
6973        }
6974    }
6975    if($OStarget ne "windows")
6976    {
6977        if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/)
6978        { # skip /usr/include/wine/msvcrt
6979            return 0;
6980        }
6981    }
6982    if(not get_dirname($Header)
6983    and $Candidate=~/[\/\\]wx[\/\\]/)
6984    { # do NOT search in system /wx/ directory
6985      # for headers without a prefix: sstream.h
6986        return 0;
6987    }
6988    if($Candidate=~/c\+\+[\/\\]\d+/ and $MAIN_CPP_DIR
6989    and $Candidate!~/\A\Q$MAIN_CPP_DIR\E/)
6990    { # skip ../c++/3.3.3/ if using ../c++/4.5/
6991        return 0;
6992    }
6993    if($Candidate=~/[\/\\]asm-/
6994    and (my $Arch = getArch($LibVersion)) ne "unknown")
6995    { # arch-specific header files
6996        if($Candidate!~/[\/\\]asm-\Q$Arch\E/)
6997        {# skip ../asm-arm/ if using x86 architecture
6998            return 0;
6999        }
7000    }
7001    my @Candidates = getSystemHeaders($HName, $LibVersion);
7002    if($#Candidates==1)
7003    { # unique header
7004        return 1;
7005    }
7006    my @SCandidates = getSystemHeaders($Header, $LibVersion);
7007    if($#SCandidates==1)
7008    { # unique name
7009        return 1;
7010    }
7011    my $SystemDepth = $SystemRoot?get_depth($SystemRoot):0;
7012    if(get_depth($Candidate)-$SystemDepth>=5)
7013    { # abstract headers in too deep directories
7014      # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/
7015        if(not isAcceptable($Header, $Candidate, $LibVersion)) {
7016            return 0;
7017        }
7018    }
7019    if($Header eq "parser.h"
7020    and $Candidate!~/\/libxml2\//)
7021    { # select parser.h from xml2 library
7022        return 0;
7023    }
7024    if(not get_dirname($Header)
7025    and keys(%{$SystemHeaders{$HName}})>=3)
7026    { # many headers with the same name
7027      # like thread.h included without a prefix
7028        if(not checkFamily(@Candidates)) {
7029            return 0;
7030        }
7031    }
7032    return 1;
7033}
7034
7035sub selectSystemHeader($$)
7036{ # cache function
7037    if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) {
7038        return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]};
7039    }
7040    return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_));
7041}
7042
7043sub selectSystemHeader_I($$)
7044{
7045    my ($Header, $LibVersion) = @_;
7046    if(-f $Header) {
7047        return $Header;
7048    }
7049    if(is_abs($Header) and not -f $Header)
7050    { # incorrect absolute path
7051        return "";
7052    }
7053    if(defined $ConfHeaders{lc($Header)})
7054    { # too abstract configuration headers
7055        return "";
7056    }
7057    my $HName = get_filename($Header);
7058    if($OSgroup ne "windows")
7059    {
7060        if(defined $WinHeaders{lc($HName)}
7061        or $HName=~/windows|win32|win64/i)
7062        { # windows headers
7063            return "";
7064        }
7065    }
7066    if($OSgroup ne "macos")
7067    {
7068        if($HName eq "fp.h")
7069        { # pngconf.h includes fp.h in Mac OS
7070            return "";
7071        }
7072    }
7073
7074    if(defined $ObsoleteHeaders{$HName})
7075    { # obsolete headers
7076        return "";
7077    }
7078    if($OSgroup eq "linux" or $OSgroup eq "bsd")
7079    {
7080        if(defined $AlienHeaders{$HName}
7081        or defined $AlienHeaders{$Header})
7082        { # alien headers from other systems
7083            return "";
7084        }
7085    }
7086
7087    foreach my $Path (@{$SystemPaths{"include"}})
7088    { # search in default paths
7089        if(-f $Path."/".$Header) {
7090            return join_P($Path,$Header);
7091        }
7092    }
7093    if(not defined $Cache{"checkSystemFiles"})
7094    { # register all headers in system include dirs
7095        checkSystemFiles();
7096    }
7097    foreach my $Candidate (sort {get_depth($a)<=>get_depth($b)}
7098    sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LibVersion))
7099    {
7100        if(isRelevant($Header, $Candidate, $LibVersion)) {
7101            return $Candidate;
7102        }
7103    }
7104    # error
7105    return "";
7106}
7107
7108sub getSystemHeaders($$)
7109{
7110    my ($Header, $LibVersion) = @_;
7111    my @Candidates = ();
7112    foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}}))
7113    {
7114        if(skipHeader($Candidate, $LibVersion)) {
7115            next;
7116        }
7117        push(@Candidates, $Candidate);
7118    }
7119    return @Candidates;
7120}
7121
7122sub cut_path_prefix($$)
7123{
7124    my ($Path, $Prefix) = @_;
7125    return $Path if(not $Prefix);
7126    $Prefix=~s/[\/\\]+\Z//;
7127    $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//;
7128    return $Path;
7129}
7130
7131sub is_default_include_dir($)
7132{
7133    my $Dir = $_[0];
7134    $Dir=~s/[\/\\]+\Z//;
7135    return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths);
7136}
7137
7138sub identifyHeader($$)
7139{ # cache function
7140    my ($Header, $LibVersion) = @_;
7141    if(not $Header) {
7142        return "";
7143    }
7144    $Header=~s/\A(\.\.[\\\/])+//g;
7145    if(defined $Cache{"identifyHeader"}{$LibVersion}{$Header}) {
7146        return $Cache{"identifyHeader"}{$LibVersion}{$Header};
7147    }
7148    return ($Cache{"identifyHeader"}{$LibVersion}{$Header} = identifyHeader_I($Header, $LibVersion));
7149}
7150
7151sub identifyHeader_I($$)
7152{ # search for header by absolute path, relative path or name
7153    my ($Header, $LibVersion) = @_;
7154    if(-f $Header)
7155    { # it's relative or absolute path
7156        return get_abs_path($Header);
7157    }
7158    elsif($GlibcHeader{$Header} and not $GLIBC_TESTING
7159    and my $HeaderDir = find_in_defaults($Header))
7160    { # search for libc headers in the /usr/include
7161      # for non-libc target library before searching
7162      # in the library paths
7163        return join_P($HeaderDir,$Header);
7164    }
7165    elsif(my $Path = $Include_Neighbors{$LibVersion}{$Header})
7166    { # search in the target library paths
7167        return $Path;
7168    }
7169    elsif(defined $DefaultGccHeader{$Header})
7170    { # search in the internal GCC include paths
7171        return $DefaultGccHeader{$Header};
7172    }
7173    elsif(my $DefaultDir = find_in_defaults($Header))
7174    { # search in the default GCC include paths
7175        return join_P($DefaultDir,$Header);
7176    }
7177    elsif(defined $DefaultCppHeader{$Header})
7178    { # search in the default G++ include paths
7179        return $DefaultCppHeader{$Header};
7180    }
7181    elsif(my $AnyPath = selectSystemHeader($Header, $LibVersion))
7182    { # search everywhere in the system
7183        return $AnyPath;
7184    }
7185    elsif($OSgroup eq "macos")
7186    { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h"
7187        if(my $Dir = get_dirname($Header))
7188        {
7189            my $RelPath = "Headers\/".get_filename($Header);
7190            if(my $HeaderDir = find_in_framework($RelPath, $Dir.".framework", $LibVersion)) {
7191                return join_P($HeaderDir, $RelPath);
7192            }
7193        }
7194    }
7195    # cannot find anything
7196    return "";
7197}
7198
7199sub getLocation($)
7200{
7201    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7202    {
7203        if($Info=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) {
7204            return (path_format($1, $OSgroup), $2);
7205        }
7206    }
7207    return ();
7208}
7209
7210sub getNameByInfo($)
7211{
7212    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7213    {
7214        if($Info=~/name[ ]*:[ ]*@(\d+) /)
7215        {
7216            if(my $NInfo = $LibInfo{$Version}{"info"}{$1})
7217            {
7218                if($NInfo=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/)
7219                { # short unsigned int (may include spaces)
7220                    my $Str = $1;
7221                    if($CppMode{$Version}
7222                    and $Str=~/\Ac99_(.+)\Z/)
7223                    {
7224                        if($CppKeywords_A{$1}) {
7225                            $Str=$1;
7226                        }
7227                    }
7228                    return $Str;
7229                }
7230            }
7231        }
7232    }
7233    return "";
7234}
7235
7236sub getTreeStr($)
7237{
7238    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7239    {
7240        if($Info=~/strg[ ]*:[ ]*([^ ]*)/)
7241        {
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    return "";
7254}
7255
7256sub getFuncShortName($)
7257{
7258    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7259    {
7260        if(index($Info, " operator ")!=-1)
7261        {
7262            if(index($Info, " conversion ")!=-1)
7263            {
7264                if(my $Rid = $SymbolInfo{$Version}{$_[0]}{"Return"})
7265                {
7266                    if(my $RName = $TypeInfo{$Version}{$Rid}{"Name"}) {
7267                        return "operator ".$RName;
7268                    }
7269                }
7270            }
7271            else
7272            {
7273                if($Info=~/ operator[ ]+([a-zA-Z]+) /)
7274                {
7275                    if(my $Ind = $Operator_Indication{$1}) {
7276                        return "operator".$Ind;
7277                    }
7278                    elsif(not $UnknownOperator{$1})
7279                    {
7280                        printMsg("WARNING", "unknown operator $1");
7281                        $UnknownOperator{$1} = 1;
7282                    }
7283                }
7284            }
7285        }
7286        else
7287        {
7288            if($Info=~/name[ ]*:[ ]*@(\d+) /) {
7289                return getTreeStr($1);
7290            }
7291        }
7292    }
7293    return "";
7294}
7295
7296sub getFuncReturn($)
7297{
7298    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7299    {
7300        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7301        {
7302            if($LibInfo{$Version}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) {
7303                return $1;
7304            }
7305        }
7306    }
7307    return "";
7308}
7309
7310sub getFuncOrig($)
7311{
7312    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7313    {
7314        if($Info=~/orig[ ]*:[ ]*@(\d+) /) {
7315            return $1;
7316        }
7317    }
7318    return $_[0];
7319}
7320
7321sub unmangleArray(@)
7322{
7323    if($_[0]=~/\A\?/)
7324    { # MSVC mangling
7325        my $UndNameCmd = get_CmdPath("undname");
7326        if(not $UndNameCmd) {
7327            exitStatus("Not_Found", "can't find \"undname\"");
7328        }
7329        writeFile("$TMP_DIR/unmangle", join("\n", @_));
7330        return split(/\n/, `$UndNameCmd 0x8386 \"$TMP_DIR/unmangle\"`);
7331    }
7332    else
7333    { # GCC mangling
7334        my $CppFiltCmd = get_CmdPath("c++filt");
7335        if(not $CppFiltCmd) {
7336            exitStatus("Not_Found", "can't find c++filt in PATH");
7337        }
7338        if(not defined $CPPFILT_SUPPORT_FILE)
7339        {
7340            my $Info = `$CppFiltCmd -h 2>&1`;
7341            $CPPFILT_SUPPORT_FILE = $Info=~/\@<file>/;
7342        }
7343        my $NoStrip = ($OSgroup=~/macos|windows/)?"-n":"";
7344        if($CPPFILT_SUPPORT_FILE)
7345        { # new versions of c++filt can take a file
7346            if($#_>$MAX_CPPFILT_FILE_SIZE)
7347            { # c++filt <= 2.22 may crash on large files (larger than 8mb)
7348              # this is fixed in the oncoming version of Binutils
7349                my @Half = splice(@_, 0, ($#_+1)/2);
7350                return (unmangleArray(@Half), unmangleArray(@_))
7351            }
7352            else
7353            {
7354                writeFile("$TMP_DIR/unmangle", join("\n", @_));
7355                my $Res = `$CppFiltCmd $NoStrip \@\"$TMP_DIR/unmangle\"`;
7356                if($?==139)
7357                { # segmentation fault
7358                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_CPPFILT_FILE_SIZE constant");
7359                }
7360                return split(/\n/, $Res);
7361            }
7362        }
7363        else
7364        { # old-style unmangling
7365            if($#_>$MAX_COMMAND_LINE_ARGUMENTS)
7366            {
7367                my @Half = splice(@_, 0, ($#_+1)/2);
7368                return (unmangleArray(@Half), unmangleArray(@_))
7369            }
7370            else
7371            {
7372                my $Strings = join(" ", @_);
7373                my $Res = `$CppFiltCmd $NoStrip $Strings`;
7374                if($?==139)
7375                { # segmentation fault
7376                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_COMMAND_LINE_ARGUMENTS constant");
7377                }
7378                return split(/\n/, $Res);
7379            }
7380        }
7381    }
7382}
7383
7384sub get_ChargeLevel($$)
7385{
7386    my ($Symbol, $LibVersion) = @_;
7387    return "" if($Symbol!~/\A(_Z|\?)/);
7388    if(defined $CompleteSignature{$LibVersion}{$Symbol}
7389    and $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
7390    {
7391        if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"})
7392        {
7393            if($Symbol=~/C1[EI]/) {
7394                return "[in-charge]";
7395            }
7396            elsif($Symbol=~/C2[EI]/) {
7397                return "[not-in-charge]";
7398            }
7399        }
7400        elsif($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"})
7401        {
7402            if($Symbol=~/D1[EI]/) {
7403                return "[in-charge]";
7404            }
7405            elsif($Symbol=~/D2[EI]/) {
7406                return "[not-in-charge]";
7407            }
7408            elsif($Symbol=~/D0[EI]/) {
7409                return "[in-charge-deleting]";
7410            }
7411        }
7412    }
7413    else
7414    {
7415        if($Symbol=~/C1[EI]/) {
7416            return "[in-charge]";
7417        }
7418        elsif($Symbol=~/C2[EI]/) {
7419            return "[not-in-charge]";
7420        }
7421        elsif($Symbol=~/D1[EI]/) {
7422            return "[in-charge]";
7423        }
7424        elsif($Symbol=~/D2[EI]/) {
7425            return "[not-in-charge]";
7426        }
7427        elsif($Symbol=~/D0[EI]/) {
7428            return "[in-charge-deleting]";
7429        }
7430    }
7431    return "";
7432}
7433
7434sub get_Signature_M($$)
7435{
7436    my ($Symbol, $LibVersion) = @_;
7437    my $Signature_M = $tr_name{$Symbol};
7438    if(my $RTid = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
7439    { # add return type name
7440        $Signature_M = $TypeInfo{$LibVersion}{$RTid}{"Name"}." ".$Signature_M;
7441    }
7442    return $Signature_M;
7443}
7444
7445sub get_Signature($$)
7446{
7447    my ($Symbol, $LibVersion) = @_;
7448    if($Cache{"get_Signature"}{$LibVersion}{$Symbol}) {
7449        return $Cache{"get_Signature"}{$LibVersion}{$Symbol};
7450    }
7451    my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
7452    my ($Signature, @Param_Types_FromUnmangledName) = ();
7453
7454    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
7455
7456    if($Symbol=~/\A(_Z|\?)/)
7457    {
7458        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
7459        {
7460            my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
7461            $ClassName=~s/\bstruct //g;
7462
7463            if(index($Symbol, "_ZTV")==0) {
7464                return "vtable for $ClassName [data]";
7465            }
7466
7467            $Signature .= $ClassName."::";
7468            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}) {
7469                $Signature .= "~";
7470            }
7471            $Signature .= $ShortName;
7472        }
7473        elsif(my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}) {
7474            $Signature .= $NameSpace."::".$ShortName;
7475        }
7476        else {
7477            $Signature .= $ShortName;
7478        }
7479        my ($Short, $Params) = split_Signature($tr_name{$MnglName});
7480        @Param_Types_FromUnmangledName = separate_Params($Params, 0, 1);
7481    }
7482    else
7483    {
7484        $Signature .= $MnglName;
7485    }
7486    my @ParamArray = ();
7487    foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
7488    {
7489        if($Pos eq "") {
7490            next;
7491        }
7492
7493        my $ParamTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"};
7494        if(not $ParamTypeId) {
7495            next;
7496        }
7497
7498        my $ParamTypeName = $TypeInfo{$LibVersion}{$ParamTypeId}{"Name"};
7499        if(not $ParamTypeName) {
7500            $ParamTypeName = $Param_Types_FromUnmangledName[$Pos];
7501        }
7502        foreach my $Typedef (keys(%ChangedTypedef))
7503        {
7504            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
7505                $ParamTypeName=~s/\b\Q$Typedef\E\b/$Base/g;
7506            }
7507        }
7508        if(my $ParamName = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"name"})
7509        {
7510            if($ParamName eq "this"
7511            and $Symbol=~/\A(_Z|\?)/)
7512            { # do NOT show first hidded "this"-parameter
7513                next;
7514            }
7515            push(@ParamArray, create_member_decl($ParamTypeName, $ParamName));
7516        }
7517        else {
7518            push(@ParamArray, $ParamTypeName);
7519        }
7520    }
7521    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"}
7522    or $GlobalDataObject{$LibVersion}{$Symbol}) {
7523        $Signature .= " [data]";
7524    }
7525    else
7526    {
7527        if(my $ChargeLevel = get_ChargeLevel($Symbol, $LibVersion))
7528        { # add [in-charge]
7529            $Signature .= " ".$ChargeLevel;
7530        }
7531        $Signature .= " (".join(", ", @ParamArray).")";
7532        if($CompleteSignature{$LibVersion}{$Symbol}{"Const"}
7533        or $Symbol=~/\A_ZN(V|)K/) {
7534            $Signature .= " const";
7535        }
7536        if($CompleteSignature{$LibVersion}{$Symbol}{"Volatile"}
7537        or $Symbol=~/\A_ZN(K|)V/) {
7538            $Signature .= " volatile";
7539        }
7540        if($CompleteSignature{$LibVersion}{$Symbol}{"Static"}
7541        and $Symbol=~/\A(_Z|\?)/)
7542        { # for static methods
7543            $Signature .= " [static]";
7544        }
7545    }
7546    if(defined $ShowRetVal
7547    and my $ReturnTId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) {
7548        $Signature .= ":".$TypeInfo{$LibVersion}{$ReturnTId}{"Name"};
7549    }
7550    if($SymbolVersion) {
7551        $Signature .= $VersionSpec.$SymbolVersion;
7552    }
7553    return ($Cache{"get_Signature"}{$LibVersion}{$Symbol} = $Signature);
7554}
7555
7556sub create_member_decl($$)
7557{
7558    my ($TName, $Member) = @_;
7559    if($TName=~/\([\*]+\)/)
7560    {
7561        $TName=~s/\(([\*]+)\)/\($1$Member\)/;
7562        return $TName;
7563    }
7564    else
7565    {
7566        my @ArraySizes = ();
7567        while($TName=~s/(\[[^\[\]]*\])\Z//) {
7568            push(@ArraySizes, $1);
7569        }
7570        return $TName." ".$Member.join("", @ArraySizes);
7571    }
7572}
7573
7574sub getFuncType($)
7575{
7576    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7577    {
7578        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7579        {
7580            if(my $Type = $LibInfo{$Version}{"info_type"}{$1})
7581            {
7582                if($Type eq "method_type") {
7583                    return "Method";
7584                }
7585                elsif($Type eq "function_type") {
7586                    return "Function";
7587                }
7588                else {
7589                    return "Other";
7590                }
7591            }
7592        }
7593    }
7594    return "";
7595}
7596
7597sub getFuncTypeId($)
7598{
7599    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7600    {
7601        if($Info=~/type[ ]*:[ ]*@(\d+)( |\Z)/) {
7602            return $1;
7603        }
7604    }
7605    return 0;
7606}
7607
7608sub isAnon($)
7609{ # "._N" or "$_N" in older GCC versions
7610    return ($_[0] and $_[0]=~/(\.|\$)\_\d+|anon\-/);
7611}
7612
7613sub formatName($$)
7614{ # type name correction
7615    if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) {
7616        return $Cache{"formatName"}{$_[1]}{$_[0]};
7617    }
7618
7619    my $N = $_[0];
7620
7621    if($_[1] ne "S")
7622    {
7623        $N=~s/\A[ ]+//g;
7624        $N=~s/[ ]+\Z//g;
7625        $N=~s/[ ]{2,}/ /g;
7626    }
7627
7628    $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const
7629
7630    $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const"
7631
7632    $N=~s/\bvolatile const\b/const volatile/g;
7633
7634    $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g;
7635    $N=~s/\b(short|long) int\b/$1/g;
7636
7637    $N=~s/([\)\]])(const|volatile)\b/$1 $2/g;
7638
7639    while($N=~s/>>/> >/g) {};
7640
7641    if($_[1] eq "S")
7642    {
7643        if(index($N, "operator")!=-1) {
7644            $N=~s/\b(operator[ ]*)> >/$1>>/;
7645        }
7646    }
7647
7648    $N=~s/,([^ ])/, $1/g;
7649
7650    return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N);
7651}
7652
7653sub get_HeaderDeps($$)
7654{
7655    my ($AbsPath, $LibVersion) = @_;
7656    return () if(not $AbsPath or not $LibVersion);
7657    if(defined $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}) {
7658        return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7659    }
7660    my %IncDir = ();
7661    detect_recursive_includes($AbsPath, $LibVersion);
7662    foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}))
7663    {
7664        next if(not $HeaderPath);
7665        next if($MAIN_CPP_DIR and $HeaderPath=~/\A\Q$MAIN_CPP_DIR\E([\/\\]|\Z)/);
7666        my $Dir = get_dirname($HeaderPath);
7667        foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$HeaderPath}}))
7668        {
7669            my $Dep = $Dir;
7670            if($Prefix)
7671            {
7672                if($OSgroup eq "windows")
7673                { # case insensitive seach on windows
7674                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) {
7675                        next;
7676                    }
7677                }
7678                elsif($OSgroup eq "macos")
7679                { # seach in frameworks
7680                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7681                    {
7682                        if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/)
7683                        {# frameworks
7684                            my ($HFramework, $HName) = ($1, $2);
7685                            $Dep = $HFramework;
7686                        }
7687                        else
7688                        {# mismatch
7689                            next;
7690                        }
7691                    }
7692                }
7693                elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7694                { # Linux, FreeBSD
7695                    next;
7696                }
7697            }
7698            if(not $Dep)
7699            { # nothing to include
7700                next;
7701            }
7702            if(is_default_include_dir($Dep))
7703            { # included by the compiler
7704                next;
7705            }
7706            if(get_depth($Dep)==1)
7707            { # too short
7708                next;
7709            }
7710            if(isLibcDir($Dep))
7711            { # do NOT include /usr/include/{sys,bits}
7712                next;
7713            }
7714            $IncDir{$Dep} = 1;
7715        }
7716    }
7717    $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LibVersion);
7718    return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7719}
7720
7721sub sortIncPaths($$)
7722{
7723    my ($ArrRef, $LibVersion) = @_;
7724    if(not $ArrRef or $#{$ArrRef}<0) {
7725        return $ArrRef;
7726    }
7727    @{$ArrRef} = sort {$b cmp $a} @{$ArrRef};
7728    @{$ArrRef} = sort {get_depth($a)<=>get_depth($b)} @{$ArrRef};
7729    @{$ArrRef} = sort {sortDeps($b, $a, $LibVersion)} @{$ArrRef};
7730    return $ArrRef;
7731}
7732
7733sub sortDeps($$$)
7734{
7735    if($Header_Dependency{$_[2]}{$_[0]}
7736    and not $Header_Dependency{$_[2]}{$_[1]}) {
7737        return 1;
7738    }
7739    elsif(not $Header_Dependency{$_[2]}{$_[0]}
7740    and $Header_Dependency{$_[2]}{$_[1]}) {
7741        return -1;
7742    }
7743    return 0;
7744}
7745
7746sub join_P($$)
7747{
7748    my $S = "/";
7749    if($OSgroup eq "windows") {
7750        $S = "\\";
7751    }
7752    return join($S, @_);
7753}
7754
7755sub get_namespace_additions($)
7756{
7757    my $NameSpaces = $_[0];
7758    my ($Additions, $AddNameSpaceId) = ("", 1);
7759    foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces}))
7760    {
7761        next if($SkipNameSpaces{$Version}{$NS});
7762        next if(not $NS or $NameSpaces->{$NS}==-1);
7763        next if($NS=~/(\A|::)iterator(::|\Z)/i);
7764        next if($NS=~/\A__/i);
7765        next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $STDCXX_TESTING);
7766        $NestedNameSpaces{$Version}{$NS} = 1; # for future use in reports
7767        my ($TypeDecl_Prefix, $TypeDecl_Suffix) = ();
7768        my @NS_Parts = split(/::/, $NS);
7769        next if($#NS_Parts==-1);
7770        next if($NS_Parts[0]=~/\A(random|or)\Z/);
7771        foreach my $NS_Part (@NS_Parts)
7772        {
7773            $TypeDecl_Prefix .= "namespace $NS_Part\{";
7774            $TypeDecl_Suffix .= "}";
7775        }
7776        my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix;
7777        my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};";
7778        $Additions.="  $TypeDecl\n  $FuncDecl\n";
7779        $AddNameSpaceId+=1;
7780    }
7781    return $Additions;
7782}
7783
7784sub path_format($$)
7785{
7786    my ($Path, $Fmt) = @_;
7787    $Path=~s/[\/\\]+\.?\Z//g;
7788    if($Fmt eq "windows")
7789    {
7790        $Path=~s/\//\\/g;
7791        $Path=lc($Path);
7792    }
7793    else
7794    { # forward slash to pass into MinGW GCC
7795        $Path=~s/\\/\//g;
7796    }
7797    return $Path;
7798}
7799
7800sub inc_opt($$)
7801{
7802    my ($Path, $Style) = @_;
7803    if($Style eq "GCC")
7804    { # GCC options
7805        if($OSgroup eq "windows")
7806        { # to MinGW GCC
7807            return "-I\"".path_format($Path, "unix")."\"";
7808        }
7809        elsif($OSgroup eq "macos"
7810        and $Path=~/\.framework\Z/)
7811        { # to Apple's GCC
7812            return "-F".esc(get_dirname($Path));
7813        }
7814        else {
7815            return "-I".esc($Path);
7816        }
7817    }
7818    elsif($Style eq "CL") {
7819        return "/I \"".$Path."\"";
7820    }
7821    return "";
7822}
7823
7824sub platformSpecs($)
7825{
7826    my $LibVersion = $_[0];
7827    my $Arch = getArch($LibVersion);
7828    if($OStarget eq "symbian")
7829    { # options for GCCE compiler
7830        my %Symbian_Opts = map {$_=>1} (
7831            "-D__GCCE__",
7832            "-DUNICODE",
7833            "-fexceptions",
7834            "-D__SYMBIAN32__",
7835            "-D__MARM_INTERWORK__",
7836            "-D_UNICODE",
7837            "-D__S60_50__",
7838            "-D__S60_3X__",
7839            "-D__SERIES60_3X__",
7840            "-D__EPOC32__",
7841            "-D__MARM__",
7842            "-D__EABI__",
7843            "-D__MARM_ARMV5__",
7844            "-D__SUPPORT_CPP_EXCEPTIONS__",
7845            "-march=armv5t",
7846            "-mapcs",
7847            "-mthumb-interwork",
7848            "-DEKA2",
7849            "-DSYMBIAN_ENABLE_SPLIT_HEADERS"
7850        );
7851        return join(" ", keys(%Symbian_Opts));
7852    }
7853    elsif($OSgroup eq "windows"
7854    and get_dumpmachine($GCC_PATH)=~/mingw/i)
7855    { # add options to MinGW compiler
7856      # to simulate the MSVC compiler
7857        my %MinGW_Opts = map {$_=>1} (
7858            "-D_WIN32",
7859            "-D_STDCALL_SUPPORTED",
7860            "-D__int64=\"long long\"",
7861            "-D__int32=int",
7862            "-D__int16=short",
7863            "-D__int8=char",
7864            "-D__possibly_notnullterminated=\" \"",
7865            "-D__nullterminated=\" \"",
7866            "-D__nullnullterminated=\" \"",
7867            "-D__w64=\" \"",
7868            "-D__ptr32=\" \"",
7869            "-D__ptr64=\" \"",
7870            "-D__forceinline=inline",
7871            "-D__inline=inline",
7872            "-D__uuidof(x)=IID()",
7873            "-D__try=",
7874            "-D__except(x)=",
7875            "-D__declspec(x)=__attribute__((x))",
7876            "-D__pragma(x)=",
7877            "-D_inline=inline",
7878            "-D__forceinline=__inline",
7879            "-D__stdcall=__attribute__((__stdcall__))",
7880            "-D__cdecl=__attribute__((__cdecl__))",
7881            "-D__fastcall=__attribute__((__fastcall__))",
7882            "-D__thiscall=__attribute__((__thiscall__))",
7883            "-D_stdcall=__attribute__((__stdcall__))",
7884            "-D_cdecl=__attribute__((__cdecl__))",
7885            "-D_fastcall=__attribute__((__fastcall__))",
7886            "-D_thiscall=__attribute__((__thiscall__))",
7887            "-DSHSTDAPI_(x)=x",
7888            "-D_MSC_EXTENSIONS",
7889            "-DSECURITY_WIN32",
7890            "-D_MSC_VER=1500",
7891            "-D_USE_DECLSPECS_FOR_SAL",
7892            "-D__noop=\" \"",
7893            "-DDECLSPEC_DEPRECATED=\" \"",
7894            "-D__builtin_alignof(x)=__alignof__(x)",
7895            "-DSORTPP_PASS");
7896        if($Arch eq "x86") {
7897            $MinGW_Opts{"-D_M_IX86=300"}=1;
7898        }
7899        elsif($Arch eq "x86_64") {
7900            $MinGW_Opts{"-D_M_AMD64=300"}=1;
7901        }
7902        elsif($Arch eq "ia64") {
7903            $MinGW_Opts{"-D_M_IA64=300"}=1;
7904        }
7905        return join(" ", keys(%MinGW_Opts));
7906    }
7907    return "";
7908}
7909
7910my %C_Structure = map {$_=>1} (
7911# FIXME: Can't separate union and struct data types before dumping,
7912# so it sometimes cause compilation errors for unknown reason
7913# when trying to declare TYPE* tmp_add_class_N
7914# This is a list of such structures + list of other C structures
7915    "sigval",
7916    "sigevent",
7917    "sigaction",
7918    "sigvec",
7919    "sigstack",
7920    "timeval",
7921    "timezone",
7922    "rusage",
7923    "rlimit",
7924    "wait",
7925    "flock",
7926    "stat",
7927    "_stat",
7928    "stat32",
7929    "_stat32",
7930    "stat64",
7931    "_stat64",
7932    "_stati64",
7933    "if_nameindex",
7934    "usb_device",
7935    "sigaltstack",
7936    "sysinfo",
7937    "timeLocale",
7938    "tcp_debug",
7939    "rpc_createerr",
7940 # Other
7941    "timespec",
7942    "random_data",
7943    "drand48_data",
7944    "_IO_marker",
7945    "_IO_FILE",
7946    "lconv",
7947    "sched_param",
7948    "tm",
7949    "itimerspec",
7950    "_pthread_cleanup_buffer",
7951    "fd_set",
7952    "siginfo",
7953    "mallinfo",
7954    "timex",
7955    "sigcontext",
7956    "ucontext",
7957 # Mac
7958    "_timex",
7959    "_class_t",
7960    "_category_t",
7961    "_class_ro_t",
7962    "_protocol_t",
7963    "_message_ref_t",
7964    "_super_message_ref_t",
7965    "_ivar_t",
7966    "_ivar_list_t"
7967);
7968
7969sub getCompileCmd($$$)
7970{
7971    my ($Path, $Opt, $Inc) = @_;
7972    my $GccCall = $GCC_PATH;
7973    if($Opt) {
7974        $GccCall .= " ".$Opt;
7975    }
7976    $GccCall .= " -x ";
7977    if($OSgroup eq "macos") {
7978        $GccCall .= "objective-";
7979    }
7980
7981    if($EMERGENCY_MODE_48)
7982    { # workaround for GCC 4.8 (C only)
7983        $GccCall .= "c++";
7984    }
7985    elsif(check_gcc($GCC_PATH, "4"))
7986    { # compile as "C++" header
7987      # to obtain complete dump using GCC 4.0
7988        $GccCall .= "c++-header";
7989    }
7990    else
7991    { # compile as "C++" source
7992      # GCC 3.3 cannot compile headers
7993        $GccCall .= "c++";
7994    }
7995    if(my $Opts = platformSpecs($Version))
7996    { # platform-specific options
7997        $GccCall .= " ".$Opts;
7998    }
7999    # allow extra qualifications
8000    # and other nonconformant code
8001    $GccCall .= " -fpermissive";
8002    $GccCall .= " -w";
8003    if($NoStdInc)
8004    {
8005        $GccCall .= " -nostdinc";
8006        $GccCall .= " -nostdinc++";
8007    }
8008    if(my $Opts_GCC = getGCC_Opts($Version))
8009    { # user-defined options
8010        $GccCall .= " ".$Opts_GCC;
8011    }
8012    $GccCall .= " \"$Path\"";
8013    if($Inc)
8014    { # include paths
8015        $GccCall .= " ".$Inc;
8016    }
8017    return $GccCall;
8018}
8019
8020sub detectPreamble($$)
8021{
8022    my ($Content, $LibVersion) = @_;
8023    my %HeaderElems = (
8024        # Types
8025        "stdio.h" => ["FILE", "va_list"],
8026        "stddef.h" => ["NULL", "ptrdiff_t"],
8027        "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t",
8028                       "int8_t", "int16_t", "int32_t", "int64_t"],
8029        "time.h" => ["time_t"],
8030        "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char",
8031                          "u_int", "off_t", "u_quad_t", "u_long", "mode_t"],
8032        "unistd.h" => ["gid_t", "uid_t", "socklen_t"],
8033        "stdbool.h" => ["_Bool"],
8034        "rpc/xdr.h" => ["bool_t"],
8035        "in_systm.h" => ["n_long", "n_short"],
8036        # Fields
8037        "arpa/inet.h" => ["fw_src", "ip_src"],
8038        # Functions
8039        "stdlib.h" => ["free", "malloc", "size_t"],
8040        "string.h" => ["memmove", "strcmp"]
8041    );
8042    my %AutoPreamble = ();
8043    foreach (keys(%HeaderElems))
8044    {
8045        foreach my $Elem (@{$HeaderElems{$_}}) {
8046            $AutoPreamble{$Elem} = $_;
8047        }
8048    }
8049    my %Types = ();
8050    while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//)
8051    { # error: 'FILE' has not been declared
8052        $Types{$2} = 1;
8053    }
8054    if(keys(%Types))
8055    {
8056        my %AddHeaders = ();
8057        foreach my $Type (keys(%Types))
8058        {
8059            if(my $Header = $AutoPreamble{$Type})
8060            {
8061                if(my $Path = identifyHeader($Header, $LibVersion))
8062                {
8063                    if(skipHeader($Path, $LibVersion)) {
8064                        next;
8065                    }
8066                    $Path = path_format($Path, $OSgroup);
8067                    $AddHeaders{$Path}{"Type"} = $Type;
8068                    $AddHeaders{$Path}{"Header"} = $Header;
8069                }
8070            }
8071        }
8072        if(keys(%AddHeaders)) {
8073            return \%AddHeaders;
8074        }
8075    }
8076    return undef;
8077}
8078
8079sub checkCTags($)
8080{
8081    my $Path = $_[0];
8082    if(not $Path) {
8083        return;
8084    }
8085    my $CTags = undef;
8086
8087    if($OSgroup eq "bsd")
8088    { # use ectags on BSD
8089        $CTags = get_CmdPath("ectags");
8090        if(not $CTags) {
8091            printMsg("WARNING", "can't find \'ectags\' program");
8092        }
8093    }
8094    if(not $CTags) {
8095        $CTags = get_CmdPath("ctags");
8096    }
8097    if(not $CTags)
8098    {
8099        printMsg("WARNING", "can't find \'ctags\' program");
8100        return;
8101    }
8102
8103    if($OSgroup ne "linux")
8104    { # macos, freebsd, etc.
8105        my $Info = `$CTags --version 2>\"$TMP_DIR/null\"`;
8106        if($Info!~/exuberant/i)
8107        {
8108            printMsg("WARNING", "incompatible version of \'ctags\' program");
8109            return;
8110        }
8111    }
8112
8113    my $Out = $TMP_DIR."/ctags.txt";
8114    system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TMP_DIR/null\"");
8115    if($Debug) {
8116        copy($Out, $DEBUG_PATH{$Version}."/ctags.txt");
8117    }
8118    open(CTAGS, "<", $Out);
8119    while(my $Line = <CTAGS>)
8120    {
8121        chomp($Line);
8122        my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line);
8123        if(defined $Intrinsic_Keywords{$Name})
8124        { # noise
8125            next;
8126        }
8127        if($Type eq "n")
8128        {
8129            if(index($Scpe, "class:")==0) {
8130                next;
8131            }
8132            if(index($Scpe, "struct:")==0) {
8133                next;
8134            }
8135            if(index($Scpe, "namespace:")==0)
8136            {
8137                if($Scpe=~s/\Anamespace://) {
8138                    $Name = $Scpe."::".$Name;
8139                }
8140            }
8141            $TUnit_NameSpaces{$Version}{$Name} = 1;
8142        }
8143        elsif($Type eq "p")
8144        {
8145            if(not $Scpe or index($Scpe, "namespace:")==0) {
8146                $TUnit_Funcs{$Version}{$Name} = 1;
8147            }
8148        }
8149        elsif($Type eq "x")
8150        {
8151            if(not $Scpe or index($Scpe, "namespace:")==0) {
8152                $TUnit_Vars{$Version}{$Name} = 1;
8153            }
8154        }
8155    }
8156    close(CTAGS);
8157}
8158
8159sub preChange($$)
8160{
8161    my ($HeaderPath, $IncStr) = @_;
8162
8163    my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr);
8164    my $Content = undef;
8165
8166    if($OStarget eq "windows"
8167    and get_dumpmachine($GCC_PATH)=~/mingw/i
8168    and $MinGWMode{$Version}!=-1)
8169    { # modify headers to compile by MinGW
8170        if(not $Content)
8171        { # preprocessing
8172            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8173        }
8174        if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g)
8175        { # __asm { ... }
8176            $MinGWMode{$Version}=1;
8177        }
8178        if($Content=~s/\s+(\/ \/.*?)\n/\n/g)
8179        { # comments after preprocessing
8180            $MinGWMode{$Version}=1;
8181        }
8182        if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g)
8183        { # 0xffui8
8184            $MinGWMode{$Version}=1;
8185        }
8186
8187        if($MinGWMode{$Version}) {
8188            printMsg("INFO", "Using MinGW compatibility mode");
8189        }
8190    }
8191
8192    if(($COMMON_LANGUAGE{$Version} eq "C" or $CheckHeadersOnly)
8193    and $CppMode{$Version}!=-1 and not $CppCompat and not $CPP_HEADERS)
8194    { # rename C++ keywords in C code
8195      # disable this code by -cpp-compatible option
8196        if(not $Content)
8197        { # preprocessing
8198            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8199        }
8200        my $RegExp_C = join("|", keys(%CppKeywords_C));
8201        my $RegExp_F = join("|", keys(%CppKeywords_F));
8202        my $RegExp_O = join("|", keys(%CppKeywords_O));
8203
8204        my $Detected = undef;
8205
8206        while($Content=~s/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/$1$2c99_$3$4/g)
8207        { # MATCH:
8208          # int foo(int new, int class, int (*new)(int));
8209          # int foo(char template[], char*);
8210          # unsigned private: 8;
8211          # DO NOT MATCH:
8212          # #pragma GCC visibility push(default)
8213            $CppMode{$Version} = 1;
8214            $Detected = "$1$2$3$4" if(not defined $Detected);
8215        }
8216        if($Content=~s/([^\w\s]|\w\s+)(?<!operator )(delete)(\s*\()/$1c99_$2$3/g)
8217        { # MATCH:
8218          # int delete(...);
8219          # int explicit(...);
8220          # DO NOT MATCH:
8221          # void operator delete(...)
8222            $CppMode{$Version} = 1;
8223            $Detected = "$1$2$3" if(not defined $Detected);
8224        }
8225        if($Content=~s/(\s+)($RegExp_O)(\s*(\;|\:))/$1c99_$2$3/g)
8226        { # MATCH:
8227          # int bool;
8228          # DO NOT MATCH:
8229          # bool X;
8230          # return *this;
8231          # throw;
8232            $CppMode{$Version} = 1;
8233            $Detected = "$1$2$3" if(not defined $Detected);
8234        }
8235        if($Content=~s/(\s+)(operator)(\s*(\(\s*\)\s*[^\(\s]|\(\s*[^\)\s]))/$1c99_$2$3/g)
8236        { # MATCH:
8237          # int operator(...);
8238          # DO NOT MATCH:
8239          # int operator()(...);
8240            $CppMode{$Version} = 1;
8241            $Detected = "$1$2$3" if(not defined $Detected);
8242        }
8243        if($Content=~s/([^\w\(\,\s]\s*|\s+)(operator)(\s*(\,\s*[^\(\s]|\)))/$1c99_$2$3/g)
8244        { # MATCH:
8245          # int foo(int operator);
8246          # int foo(int operator, int other);
8247          # DO NOT MATCH:
8248          # int operator,(...);
8249            $CppMode{$Version} = 1;
8250            $Detected = "$1$2$3" if(not defined $Detected);
8251        }
8252        if($Content=~s/(\*\s*|\w\s+)(bool)(\s*(\,|\)))/$1c99_$2$3/g)
8253        { # MATCH:
8254          # int foo(gboolean *bool);
8255          # DO NOT MATCH:
8256          # void setTabEnabled(int index, bool);
8257            $CppMode{$Version} = 1;
8258            $Detected = "$1$2$3" if(not defined $Detected);
8259        }
8260        if($Content=~s/(\w)(\s*[^\w\(\,\s]\s*|\s+)(this|throw)(\s*[\,\)])/$1$2c99_$3$4/g)
8261        { # MATCH:
8262          # int foo(int* this);
8263          # int bar(int this);
8264          # int baz(int throw);
8265          # DO NOT MATCH:
8266          # foo(X, this);
8267            $CppMode{$Version} = 1;
8268            $Detected = "$1$2$3$4" if(not defined $Detected);
8269        }
8270        if($Content=~s/(struct |extern )(template) /$1c99_$2 /g)
8271        { # MATCH:
8272          # struct template {...};
8273          # extern template foo(...);
8274            $CppMode{$Version} = 1;
8275            $Detected = "$1$2" if(not defined $Detected);
8276        }
8277
8278        if($CppMode{$Version} == 1)
8279        {
8280            if($Debug)
8281            {
8282                $Detected=~s/\A\s+//g;
8283                printMsg("INFO", "Detected code: \"$Detected\"");
8284            }
8285        }
8286
8287        # remove typedef enum NAME NAME;
8288        my @FwdTypedefs = $Content=~/typedef\s+enum\s+(\w+)\s+(\w+);/g;
8289        my $N = 0;
8290        while($N<=$#FwdTypedefs-1)
8291        {
8292            my $S = $FwdTypedefs[$N];
8293            if($S eq $FwdTypedefs[$N+1])
8294            {
8295                $Content=~s/typedef\s+enum\s+\Q$S\E\s+\Q$S\E;//g;
8296                $CppMode{$Version} = 1;
8297
8298                if($Debug) {
8299                    printMsg("INFO", "Detected code: \"typedef enum $S $S;\"");
8300                }
8301            }
8302            $N+=2;
8303        }
8304
8305        if($CppMode{$Version}==1) {
8306            printMsg("INFO", "Using C++ compatibility mode");
8307        }
8308    }
8309
8310    if($CppMode{$Version}==1
8311    or $MinGWMode{$Version}==1)
8312    {
8313        my $IPath = $TMP_DIR."/dump$Version.i";
8314        writeFile($IPath, $Content);
8315        return $IPath;
8316    }
8317
8318    return undef;
8319}
8320
8321sub getDump()
8322{
8323    if(not $GCC_PATH) {
8324        exitStatus("Error", "internal error - GCC path is not set");
8325    }
8326
8327    my @Headers = keys(%{$Registered_Headers{$Version}});
8328    @Headers = sort {int($Registered_Headers{$Version}{$a}{"Pos"})<=>int($Registered_Headers{$Version}{$b}{"Pos"})} @Headers;
8329
8330    my $IncludeString = getIncString(getIncPaths(@{$Include_Preamble{$Version}}, @Headers), "GCC");
8331
8332    my $TmpHeaderPath = $TMP_DIR."/dump".$Version.".h";
8333    my $HeaderPath = $TmpHeaderPath;
8334
8335    # write tmp-header
8336    open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n");
8337    if(my $AddDefines = $Descriptor{$Version}{"Defines"})
8338    {
8339        $AddDefines=~s/\n\s+/\n  /g;
8340        print TMP_HEADER "\n  // add defines\n  ".$AddDefines."\n";
8341    }
8342    print TMP_HEADER "\n  // add includes\n";
8343    foreach my $HPath (@{$Include_Preamble{$Version}}) {
8344        print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8345    }
8346    foreach my $HPath (@Headers)
8347    {
8348        if(not grep {$HPath eq $_} (@{$Include_Preamble{$Version}})) {
8349            print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8350        }
8351    }
8352    close(TMP_HEADER);
8353
8354    if($ExtraInfo)
8355    { # extra information for other tools
8356        if($IncludeString) {
8357            writeFile($ExtraInfo."/include-string", $IncludeString);
8358        }
8359        writeFile($ExtraInfo."/recursive-includes", Dumper($RecursiveIncludes{$Version}));
8360        writeFile($ExtraInfo."/direct-includes", Dumper($Header_Includes{$Version}));
8361
8362        if(my @Redirects = keys(%{$Header_ErrorRedirect{$Version}}))
8363        {
8364            my $REDIR = "";
8365            foreach my $P1 (sort @Redirects) {
8366                $REDIR .= $P1.";".$Header_ErrorRedirect{$Version}{$P1}."\n";
8367            }
8368            writeFile($ExtraInfo."/include-redirect", $REDIR);
8369        }
8370    }
8371
8372    if(not keys(%{$TargetHeaders{$Version}}))
8373    { # Target headers
8374        addTargetHeaders($Version);
8375    }
8376
8377    # clean memory
8378    %RecursiveIncludes = ();
8379    %Header_Include_Prefix = ();
8380    %Header_Includes = ();
8381
8382    # clean cache
8383    delete($Cache{"identifyHeader"});
8384    delete($Cache{"detect_header_includes"});
8385    delete($Cache{"selectSystemHeader"});
8386
8387    # preprocessing stage
8388    my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $Version);
8389    checkPreprocessedUnit($Pre);
8390
8391    if($ExtraInfo)
8392    { # extra information for other tools
8393        writeFile($ExtraInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$Version}})));
8394    }
8395
8396    # clean memory
8397    delete($Include_Neighbors{$Version});
8398    delete($PreprocessedHeaders{$Version});
8399
8400    if($COMMON_LANGUAGE{$Version} eq "C++") {
8401        checkCTags($Pre);
8402    }
8403
8404    if(my $PrePath = preChange($TmpHeaderPath, $IncludeString))
8405    { # try to correct the preprocessor output
8406        $HeaderPath = $PrePath;
8407    }
8408
8409    if($COMMON_LANGUAGE{$Version} eq "C++")
8410    { # add classes and namespaces to the dump
8411        my $CHdump = "-fdump-class-hierarchy -c";
8412        if($CppMode{$Version}==1
8413        or $MinGWMode{$Version}==1) {
8414            $CHdump .= " -fpreprocessed";
8415        }
8416        my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString);
8417        chdir($TMP_DIR);
8418        system($ClassHierarchyCmd." >null 2>&1");
8419        chdir($ORIG_DIR);
8420        if(my $ClassDump = (cmd_find($TMP_DIR,"f","*.class",1))[0])
8421        {
8422            my $Content = readFile($ClassDump);
8423            foreach my $ClassInfo (split(/\n\n/, $Content))
8424            {
8425                if($ClassInfo=~/\AClass\s+(.+)\s*/i)
8426                {
8427                    my $CName = $1;
8428                    next if($CName=~/\A(__|_objc_|_opaque_)/);
8429                    $TUnit_NameSpaces{$Version}{$CName} = -1;
8430                    if($CName=~/\A[\w:]+\Z/)
8431                    { # classes
8432                        $TUnit_Classes{$Version}{$CName} = 1;
8433                    }
8434                    if($CName=~/(\w[\w:]*)::/)
8435                    { # namespaces
8436                        my $NS = $1;
8437                        if(not defined $TUnit_NameSpaces{$Version}{$NS}) {
8438                            $TUnit_NameSpaces{$Version}{$NS} = 1;
8439                        }
8440                    }
8441                }
8442                elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
8443                { # read v-tables (advanced approach)
8444                    my ($CName, $VTable) = ($1, $2);
8445                    $ClassVTable_Content{$Version}{$CName} = $VTable;
8446                }
8447            }
8448            foreach my $NS (keys(%{$AddNameSpaces{$Version}}))
8449            { # add user-defined namespaces
8450                $TUnit_NameSpaces{$Version}{$NS} = 1;
8451            }
8452            if($Debug)
8453            { # debug mode
8454                mkpath($DEBUG_PATH{$Version});
8455                copy($ClassDump, $DEBUG_PATH{$Version}."/class-hierarchy-dump.txt");
8456            }
8457            unlink($ClassDump);
8458        }
8459
8460        # add namespaces and classes
8461        if(my $NS_Add = get_namespace_additions($TUnit_NameSpaces{$Version}))
8462        { # GCC on all supported platforms does not include namespaces to the dump by default
8463            appendFile($HeaderPath, "\n  // add namespaces\n".$NS_Add);
8464        }
8465        # some GCC versions don't include class methods to the TU dump by default
8466        my ($AddClass, $ClassNum) = ("", 0);
8467        my $GCC_44 = check_gcc($GCC_PATH, "4.4"); # support for old GCC versions
8468        foreach my $CName (sort keys(%{$TUnit_Classes{$Version}}))
8469        {
8470            next if($C_Structure{$CName});
8471            next if(not $STDCXX_TESTING and $CName=~/\Astd::/);
8472            next if($SkipTypes{$Version}{$CName});
8473            if(not $Force and $GCC_44
8474            and $OSgroup eq "linux")
8475            { # optimization for linux with GCC >= 4.4
8476              # disable this code by -force option
8477                if(index($CName, "::")!=-1)
8478                { # should be added by name space
8479                    next;
8480                }
8481            }
8482            else
8483            {
8484                if($CName=~/\A(.+)::[^:]+\Z/
8485                and $TUnit_Classes{$Version}{$1})
8486                { # classes inside other classes
8487                    next;
8488                }
8489            }
8490            if(defined $TUnit_Funcs{$Version}{$CName})
8491            { # the same name for a function and type
8492                next;
8493            }
8494            if(defined $TUnit_Vars{$Version}{$CName})
8495            { # the same name for a variable and type
8496                next;
8497            }
8498            $AddClass .= "  $CName* tmp_add_class_".($ClassNum++).";\n";
8499        }
8500        if($AddClass) {
8501            appendFile($HeaderPath, "\n  // add classes\n".$AddClass);
8502        }
8503    }
8504    writeLog($Version, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n");
8505    # create TU dump
8506    my $TUdump = "-fdump-translation-unit -fkeep-inline-functions -c";
8507    if($UserLang eq "C") {
8508        $TUdump .= " -U__cplusplus -D_Bool=\"bool\"";
8509    }
8510    if($CppMode{$Version}==1
8511    or $MinGWMode{$Version}==1) {
8512        $TUdump .= " -fpreprocessed";
8513    }
8514    my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString);
8515    writeLog($Version, "The GCC parameters:\n  $SyntaxTreeCmd\n\n");
8516    chdir($TMP_DIR);
8517    system($SyntaxTreeCmd." >\"$TMP_DIR/tu_errors\" 2>&1");
8518    my $Errors = "";
8519    if($?)
8520    { # failed to compile, but the TU dump still can be created
8521        if($Errors = readFile($TMP_DIR."/tu_errors"))
8522        { # try to recompile
8523          # FIXME: handle other errors and try to recompile
8524            if($CppMode{$Version}==1
8525            and index($Errors, "c99_")!=-1
8526            and not defined $CppIncompat)
8527            { # disable c99 mode and try again
8528                $CppMode{$Version}=-1;
8529
8530                if($Debug)
8531                {
8532                    # printMsg("INFO", $Errors);
8533                }
8534
8535                printMsg("INFO", "Disabling C++ compatibility mode");
8536                resetLogging($Version);
8537                $TMP_DIR = tempdir(CLEANUP=>1);
8538                return getDump();
8539            }
8540            elsif($AutoPreambleMode{$Version}!=-1
8541            and my $AddHeaders = detectPreamble($Errors, $Version))
8542            { # add auto preamble headers and try again
8543                $AutoPreambleMode{$Version}=-1;
8544                my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first
8545                foreach my $Num (0 .. $#Headers)
8546                {
8547                    my $Path = $Headers[$Num];
8548                    if(not grep {$Path eq $_} (@{$Include_Preamble{$Version}}))
8549                    {
8550                        push_U($Include_Preamble{$Version}, $Path);
8551                        printMsg("INFO", "Add \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'");
8552                    }
8553                }
8554                resetLogging($Version);
8555                $TMP_DIR = tempdir(CLEANUP=>1);
8556                return getDump();
8557            }
8558            elsif($Cpp0xMode{$Version}!=-1
8559            and ($Errors=~/\Q-std=c++0x\E/
8560            or $Errors=~/is not a class or namespace/))
8561            { # c++0x: enum class
8562                if(check_gcc($GCC_PATH, "4.6"))
8563                {
8564                    $Cpp0xMode{$Version}=-1;
8565                    printMsg("INFO", "Enabling c++0x mode");
8566                    resetLogging($Version);
8567                    $TMP_DIR = tempdir(CLEANUP=>1);
8568                    $CompilerOptions{$Version} .= " -std=c++0x";
8569                    return getDump();
8570                }
8571                else {
8572                    printMsg("WARNING", "Probably c++0x construction detected");
8573                }
8574
8575            }
8576            elsif($MinGWMode{$Version}==1)
8577            { # disable MinGW mode and try again
8578                $MinGWMode{$Version}=-1;
8579                resetLogging($Version);
8580                $TMP_DIR = tempdir(CLEANUP=>1);
8581                return getDump();
8582            }
8583            writeLog($Version, $Errors);
8584        }
8585        else {
8586            writeLog($Version, "$!: $?\n");
8587        }
8588        printMsg("ERROR", "some errors occurred when compiling headers");
8589        printErrorLog($Version);
8590        $COMPILE_ERRORS = $ERROR_CODE{"Compile_Error"};
8591        writeLog($Version, "\n"); # new line
8592    }
8593    chdir($ORIG_DIR);
8594    unlink($TmpHeaderPath);
8595    unlink($HeaderPath);
8596
8597    if(my @TUs = cmd_find($TMP_DIR,"f","*.tu",1)) {
8598        return $TUs[0];
8599    }
8600    else
8601    {
8602        my $Msg = "can't compile header(s)";
8603        if($Errors=~/error trying to exec \W+cc1plus\W+/) {
8604            $Msg .= "\nDid you install G++?";
8605        }
8606        exitStatus("Cannot_Compile", $Msg);
8607    }
8608}
8609
8610sub cmd_file($)
8611{
8612    my $Path = $_[0];
8613    return "" if(not $Path or not -e $Path);
8614    if(my $CmdPath = get_CmdPath("file")) {
8615        return `$CmdPath -b \"$Path\"`;
8616    }
8617    return "";
8618}
8619
8620sub getIncString($$)
8621{
8622    my ($ArrRef, $Style) = @_;
8623    return "" if(not $ArrRef or $#{$ArrRef}<0);
8624    my $String = "";
8625    foreach (@{$ArrRef}) {
8626        $String .= " ".inc_opt($_, $Style);
8627    }
8628    return $String;
8629}
8630
8631sub getIncPaths(@)
8632{
8633    my @HeaderPaths = @_;
8634    my @IncPaths = @{$Add_Include_Paths{$Version}};
8635    if($INC_PATH_AUTODETECT{$Version})
8636    { # auto-detecting dependencies
8637        my %Includes = ();
8638        foreach my $HPath (@HeaderPaths)
8639        {
8640            foreach my $Dir (get_HeaderDeps($HPath, $Version))
8641            {
8642                if($Skip_Include_Paths{$Version}{$Dir}) {
8643                    next;
8644                }
8645                if($SystemRoot)
8646                {
8647                    if($Skip_Include_Paths{$Version}{$SystemRoot.$Dir}) {
8648                        next;
8649                    }
8650                }
8651                $Includes{$Dir} = 1;
8652            }
8653        }
8654        foreach my $Dir (@{sortIncPaths([keys(%Includes)], $Version)}) {
8655            push_U(\@IncPaths, $Dir);
8656        }
8657    }
8658    else
8659    { # user-defined paths
8660        @IncPaths = @{$Include_Paths{$Version}};
8661    }
8662    return \@IncPaths;
8663}
8664
8665sub push_U($@)
8666{ # push unique
8667    if(my $Array = shift @_)
8668    {
8669        if(@_)
8670        {
8671            my %Exist = map {$_=>1} @{$Array};
8672            foreach my $Elem (@_)
8673            {
8674                if(not defined $Exist{$Elem})
8675                {
8676                    push(@{$Array}, $Elem);
8677                    $Exist{$Elem} = 1;
8678                }
8679            }
8680        }
8681    }
8682}
8683
8684sub callPreprocessor($$$)
8685{
8686    my ($Path, $Inc, $LibVersion) = @_;
8687    return "" if(not $Path or not -f $Path);
8688    my $IncludeString=$Inc;
8689    if(not $Inc) {
8690        $IncludeString = getIncString(getIncPaths($Path), "GCC");
8691    }
8692    my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString);
8693    my $Out = $TMP_DIR."/preprocessed.h";
8694    system($Cmd." >\"$Out\" 2>\"$TMP_DIR/null\"");
8695    return $Out;
8696}
8697
8698sub cmd_find($;$$$$)
8699{ # native "find" is much faster than File::Find (~6x)
8700  # also the File::Find doesn't support --maxdepth N option
8701  # so using the cross-platform wrapper for the native one
8702    my ($Path, $Type, $Name, $MaxDepth, $UseRegex) = @_;
8703    return () if(not $Path or not -e $Path);
8704    if($OSgroup eq "windows")
8705    {
8706        $Path = get_abs_path($Path);
8707        $Path = path_format($Path, $OSgroup);
8708        my $Cmd = "dir \"$Path\" /B /O";
8709        if($MaxDepth!=1) {
8710            $Cmd .= " /S";
8711        }
8712        if($Type eq "d") {
8713            $Cmd .= " /AD";
8714        }
8715        elsif($Type eq "f") {
8716            $Cmd .= " /A-D";
8717        }
8718        my @Files = split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`);
8719        if($Name)
8720        {
8721            if(not $UseRegex)
8722            { # FIXME: how to search file names in MS shell?
8723              # wildcard to regexp
8724                $Name=~s/\*/.*/g;
8725                $Name='\A'.$Name.'\Z';
8726            }
8727            @Files = grep { /$Name/i } @Files;
8728        }
8729        my @AbsPaths = ();
8730        foreach my $File (@Files)
8731        {
8732            if(not is_abs($File)) {
8733                $File = join_P($Path, $File);
8734            }
8735            if($Type eq "f" and not -f $File)
8736            { # skip dirs
8737                next;
8738            }
8739            push(@AbsPaths, path_format($File, $OSgroup));
8740        }
8741        if($Type eq "d") {
8742            push(@AbsPaths, $Path);
8743        }
8744        return @AbsPaths;
8745    }
8746    else
8747    {
8748        my $FindCmd = get_CmdPath("find");
8749        if(not $FindCmd) {
8750            exitStatus("Not_Found", "can't find a \"find\" command");
8751        }
8752        $Path = get_abs_path($Path);
8753        if(-d $Path and -l $Path
8754        and $Path!~/\/\Z/)
8755        { # for directories that are symlinks
8756            $Path.="/";
8757        }
8758        my $Cmd = $FindCmd." \"$Path\"";
8759        if($MaxDepth) {
8760            $Cmd .= " -maxdepth $MaxDepth";
8761        }
8762        if($Type) {
8763            $Cmd .= " -type $Type";
8764        }
8765        if($Name and not $UseRegex)
8766        { # wildcards
8767            $Cmd .= " -name \"$Name\"";
8768        }
8769        my $Res = `$Cmd 2>\"$TMP_DIR/null\"`;
8770        if($? and $!) {
8771            printMsg("ERROR", "problem with \'find\' utility ($?): $!");
8772        }
8773        my @Files = split(/\n/, $Res);
8774        if($Name and $UseRegex)
8775        { # regex
8776            @Files = grep { /$Name/ } @Files;
8777        }
8778        return @Files;
8779    }
8780}
8781
8782sub unpackDump($)
8783{
8784    my $Path = $_[0];
8785    return "" if(not $Path or not -e $Path);
8786    $Path = get_abs_path($Path);
8787    $Path = path_format($Path, $OSgroup);
8788    my ($Dir, $FileName) = separate_path($Path);
8789    my $UnpackDir = $TMP_DIR."/unpack";
8790    rmtree($UnpackDir);
8791    mkpath($UnpackDir);
8792    if($FileName=~s/\Q.zip\E\Z//g)
8793    { # *.zip
8794        my $UnzipCmd = get_CmdPath("unzip");
8795        if(not $UnzipCmd) {
8796            exitStatus("Not_Found", "can't find \"unzip\" command");
8797        }
8798        chdir($UnpackDir);
8799        system("$UnzipCmd \"$Path\" >\"$TMP_DIR/null\"");
8800        if($?) {
8801            exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8802        }
8803        chdir($ORIG_DIR);
8804        my @Contents = cmd_find($UnpackDir, "f");
8805        if(not @Contents) {
8806            exitStatus("Error", "can't extract \'$Path\'");
8807        }
8808        return $Contents[0];
8809    }
8810    elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g)
8811    { # *.tar.gz
8812      # *.tar.gz.amd64 (dh & cdbs)
8813        if($OSgroup eq "windows")
8814        { # -xvzf option is not implemented in tar.exe (2003)
8815          # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
8816            my $TarCmd = get_CmdPath("tar");
8817            if(not $TarCmd) {
8818                exitStatus("Not_Found", "can't find \"tar\" command");
8819            }
8820            my $GzipCmd = get_CmdPath("gzip");
8821            if(not $GzipCmd) {
8822                exitStatus("Not_Found", "can't find \"gzip\" command");
8823            }
8824            chdir($UnpackDir);
8825            system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k)
8826            if($?) {
8827                exitStatus("Error", "can't extract \'$Path\'");
8828            }
8829            system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TMP_DIR/null\"");
8830            if($?) {
8831                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8832            }
8833            chdir($ORIG_DIR);
8834            unlink($Dir."/".$FileName.".tar");
8835            my @Contents = cmd_find($UnpackDir, "f");
8836            if(not @Contents) {
8837                exitStatus("Error", "can't extract \'$Path\'");
8838            }
8839            return $Contents[0];
8840        }
8841        else
8842        { # Unix, Mac
8843            my $TarCmd = get_CmdPath("tar");
8844            if(not $TarCmd) {
8845                exitStatus("Not_Found", "can't find \"tar\" command");
8846            }
8847            chdir($UnpackDir);
8848            system("$TarCmd -xvzf \"$Path\" >\"$TMP_DIR/null\"");
8849            if($?) {
8850                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8851            }
8852            chdir($ORIG_DIR);
8853            my @Contents = cmd_find($UnpackDir, "f");
8854            if(not @Contents) {
8855                exitStatus("Error", "can't extract \'$Path\'");
8856            }
8857            return $Contents[0];
8858        }
8859    }
8860}
8861
8862sub createArchive($$)
8863{
8864    my ($Path, $To) = @_;
8865    if(not $To) {
8866        $To = ".";
8867    }
8868    if(not $Path or not -e $Path
8869    or not -d $To) {
8870        return "";
8871    }
8872    my ($From, $Name) = separate_path($Path);
8873    if($OSgroup eq "windows")
8874    { # *.zip
8875        my $ZipCmd = get_CmdPath("zip");
8876        if(not $ZipCmd) {
8877            exitStatus("Not_Found", "can't find \"zip\"");
8878        }
8879        my $Pkg = $To."/".$Name.".zip";
8880        unlink($Pkg);
8881        chdir($To);
8882        system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TMP_DIR/null\"");
8883        if($?)
8884        { # cannot allocate memory (or other problems with "zip")
8885            unlink($Path);
8886            exitStatus("Error", "can't pack the ABI dump: ".$!);
8887        }
8888        chdir($ORIG_DIR);
8889        unlink($Path);
8890        return $Pkg;
8891    }
8892    else
8893    { # *.tar.gz
8894        my $TarCmd = get_CmdPath("tar");
8895        if(not $TarCmd) {
8896            exitStatus("Not_Found", "can't find \"tar\"");
8897        }
8898        my $GzipCmd = get_CmdPath("gzip");
8899        if(not $GzipCmd) {
8900            exitStatus("Not_Found", "can't find \"gzip\"");
8901        }
8902        my $Pkg = abs_path($To)."/".$Name.".tar.gz";
8903        unlink($Pkg);
8904        chdir($From);
8905        system($TarCmd, "-czf", $Pkg, $Name);
8906        if($?)
8907        { # cannot allocate memory (or other problems with "tar")
8908            unlink($Path);
8909            exitStatus("Error", "can't pack the ABI dump: ".$!);
8910        }
8911        chdir($ORIG_DIR);
8912        unlink($Path);
8913        return $To."/".$Name.".tar.gz";
8914    }
8915}
8916
8917sub is_header_file($)
8918{
8919    if($_[0]=~/\.($HEADER_EXT)\Z/i) {
8920        return $_[0];
8921    }
8922    return 0;
8923}
8924
8925sub is_not_header($)
8926{
8927    if($_[0]=~/\.\w+\Z/
8928    and $_[0]!~/\.($HEADER_EXT)\Z/i) {
8929        return 1;
8930    }
8931    return 0;
8932}
8933
8934sub is_header($$$)
8935{
8936    my ($Header, $UserDefined, $LibVersion) = @_;
8937    return 0 if(-d $Header);
8938    if(-f $Header) {
8939        $Header = get_abs_path($Header);
8940    }
8941    else
8942    {
8943        if(is_abs($Header))
8944        { # incorrect absolute path
8945            return 0;
8946        }
8947        if(my $HPath = identifyHeader($Header, $LibVersion)) {
8948            $Header = $HPath;
8949        }
8950        else
8951        { # can't find header
8952            return 0;
8953        }
8954    }
8955    if($Header=~/\.\w+\Z/)
8956    { # have an extension
8957        return is_header_file($Header);
8958    }
8959    else
8960    {
8961        if($UserDefined==2)
8962        { # specified on the command line
8963            if(cmd_file($Header)!~/HTML|XML/i) {
8964                return $Header;
8965            }
8966        }
8967        elsif($UserDefined)
8968        { # specified in the XML-descriptor
8969          # header file without an extension
8970            return $Header;
8971        }
8972        else
8973        {
8974            if(index($Header, "/include/")!=-1
8975            or cmd_file($Header)=~/C[\+]*\s+program/i)
8976            { # !~/HTML|XML|shared|dynamic/i
8977                return $Header;
8978            }
8979        }
8980    }
8981    return 0;
8982}
8983
8984sub addTargetHeaders($)
8985{
8986    my $LibVersion = $_[0];
8987    foreach my $RegHeader (keys(%{$Registered_Headers{$LibVersion}}))
8988    {
8989        my $RegDir = get_dirname($RegHeader);
8990        $TargetHeaders{$LibVersion}{get_filename($RegHeader)} = 1;
8991
8992        if(not $INC_PATH_AUTODETECT{$LibVersion}) {
8993            detect_recursive_includes($RegHeader, $LibVersion);
8994        }
8995
8996        foreach my $RecInc (keys(%{$RecursiveIncludes{$LibVersion}{$RegHeader}}))
8997        {
8998            my $Dir = get_dirname($RecInc);
8999
9000            if(familiarDirs($RegDir, $Dir)
9001            or $RecursiveIncludes{$LibVersion}{$RegHeader}{$RecInc}!=1)
9002            { # in the same directory or included by #include "..."
9003                $TargetHeaders{$LibVersion}{get_filename($RecInc)} = 1;
9004            }
9005        }
9006    }
9007}
9008
9009sub familiarDirs($$)
9010{
9011    my ($D1, $D2) = @_;
9012    if($D1 eq $D2) {
9013        return 1;
9014    }
9015
9016    my $U1 = index($D1, "/usr/");
9017    my $U2 = index($D2, "/usr/");
9018
9019    if($U1==0 and $U2!=0) {
9020        return 0;
9021    }
9022
9023    if($U2==0 and $U1!=0) {
9024        return 0;
9025    }
9026
9027    if(index($D2, $D1."/")==0) {
9028        return 1;
9029    }
9030
9031    # /usr/include/DIR
9032    # /home/user/DIR
9033
9034    my $DL = get_depth($D1);
9035
9036    my @Dirs1 = ($D1);
9037    while($DL - get_depth($D1)<=2
9038    and get_depth($D1)>=4
9039    and $D1=~s/[\/\\]+[^\/\\]*?\Z//) {
9040        push(@Dirs1, $D1);
9041    }
9042
9043    my @Dirs2 = ($D2);
9044    while(get_depth($D2)>=4
9045    and $D2=~s/[\/\\]+[^\/\\]*?\Z//) {
9046        push(@Dirs2, $D2);
9047    }
9048
9049    foreach my $P1 (@Dirs1)
9050    {
9051        foreach my $P2 (@Dirs2)
9052        {
9053
9054            if($P1 eq $P2) {
9055                return 1;
9056            }
9057        }
9058    }
9059    return 0;
9060}
9061
9062sub readHeaders($)
9063{
9064    $Version = $_[0];
9065    printMsg("INFO", "checking header(s) ".$Descriptor{$Version}{"Version"}." ...");
9066    my $DumpPath = getDump();
9067    if($Debug)
9068    { # debug mode
9069        mkpath($DEBUG_PATH{$Version});
9070        copy($DumpPath, $DEBUG_PATH{$Version}."/translation-unit-dump.txt");
9071    }
9072    getInfo($DumpPath);
9073}
9074
9075sub prepareTypes($)
9076{
9077    my $LibVersion = $_[0];
9078    if(not checkDump($LibVersion, "2.0"))
9079    { # support for old ABI dumps
9080      # type names have been corrected in ACC 1.22 (dump 2.0 format)
9081        foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9082        {
9083            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9084            if($TName=~/\A(\w+)::(\w+)/) {
9085                my ($P1, $P2) = ($1, $2);
9086                if($P1 eq $P2) {
9087                    $TName=~s/\A$P1:\:$P1(\W)/$P1$1/;
9088                }
9089                else {
9090                    $TName=~s/\A(\w+:\:)$P2:\:$P2(\W)/$1$P2$2/;
9091                }
9092            }
9093            $TypeInfo{$LibVersion}{$TypeId}{"Name"} = $TName;
9094        }
9095    }
9096    if(not checkDump($LibVersion, "2.5"))
9097    { # support for old ABI dumps
9098      # V < 2.5: array size == "number of elements"
9099      # V >= 2.5: array size in bytes
9100        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9101        {
9102            my %Type = get_PureType($TypeId, $TypeInfo{$LibVersion});
9103            if($Type{"Type"} eq "Array")
9104            {
9105                if(my $Size = $Type{"Size"})
9106                { # array[N]
9107                    my %Base = get_OneStep_BaseType($Type{"Tid"}, $TypeInfo{$LibVersion});
9108                    $Size *= $Base{"Size"};
9109                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = "$Size";
9110                }
9111                else
9112                { # array[] is a pointer
9113                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $WORD_SIZE{$LibVersion};
9114                }
9115            }
9116        }
9117    }
9118    my $V2 = ($LibVersion==1)?2:1;
9119    if(not checkDump($LibVersion, "2.7"))
9120    { # support for old ABI dumps
9121      # size of "method ptr" corrected in 2.7
9122        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9123        {
9124            my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
9125            if($PureType{"Type"} eq "MethodPtr")
9126            {
9127                my %Type = get_Type($TypeId, $LibVersion);
9128                my $TypeId_2 = getTypeIdByName($PureType{"Name"}, $V2);
9129                my %Type2 = get_Type($TypeId_2, $V2);
9130                if($Type{"Size"} ne $Type2{"Size"}) {
9131                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $Type2{"Size"};
9132                }
9133            }
9134        }
9135    }
9136}
9137
9138sub prepareSymbols($)
9139{
9140    my $LibVersion = $_[0];
9141
9142    if(not keys(%{$SymbolInfo{$LibVersion}}))
9143    { # check if input is valid
9144        if(not $ExtendedCheck)
9145        {
9146            if($CheckHeadersOnly) {
9147                exitStatus("Empty_Set", "the set of public symbols is empty (".$Descriptor{$LibVersion}{"Version"}.")");
9148            }
9149            else {
9150                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection (".$Descriptor{$LibVersion}{"Version"}.")");
9151            }
9152        }
9153    }
9154
9155    my $Remangle = 0;
9156    if(not checkDump(1, "2.10")
9157    or not checkDump(2, "2.10"))
9158    { # different formats
9159        $Remangle = 1;
9160    }
9161    if($CheckHeadersOnly)
9162    { # different languages
9163        if($UserLang)
9164        { # --lang=LANG for both versions
9165            if(($UsedDump{1}{"V"} and $UserLang ne $UsedDump{1}{"L"})
9166            or ($UsedDump{2}{"V"} and $UserLang ne $UsedDump{2}{"L"}))
9167            {
9168                if($UserLang eq "C++")
9169                { # remangle symbols
9170                    $Remangle = 1;
9171                }
9172                elsif($UserLang eq "C")
9173                { # remove mangling
9174                    $Remangle = -1;
9175                }
9176            }
9177        }
9178    }
9179
9180    foreach my $InfoId (sort {int($b)<=>int($a)} keys(%{$SymbolInfo{$LibVersion}}))
9181    { # reverse order: D0, D1, D2, D0 (artificial, GCC < 4.5), C1, C2
9182        if(not checkDump($LibVersion, "2.13"))
9183        { # support for old ABI dumps
9184            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
9185            {
9186                foreach my $P (keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}))
9187                {
9188                    my $TypeId = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"type"};
9189                    my $DVal = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"};
9190                    my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9191                    if(defined $DVal and $DVal ne "")
9192                    {
9193                        if($TName eq "char") {
9194                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = chr($DVal);
9195                        }
9196                        elsif($TName eq "bool") {
9197                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = $DVal?"true":"false";
9198                        }
9199                    }
9200                }
9201            }
9202        }
9203        if($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
9204        {
9205            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
9206            and keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})
9207            and $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
9208            { # support for old GCC < 4.5: skip artificial ~dtor(int __in_chrg)
9209              # + support for old ABI dumps
9210                next;
9211            }
9212        }
9213        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9214        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9215        my $ClassID = $SymbolInfo{$LibVersion}{$InfoId}{"Class"};
9216        my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
9217
9218        my $SRemangle = 0;
9219        if(not checkDump(1, "2.12")
9220        or not checkDump(2, "2.12"))
9221        { # support for old ABI dumps
9222            if($ShortName eq "operator>>")
9223            {
9224                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9225                { # corrected mangling of operator>>
9226                    $SRemangle = 1;
9227                }
9228            }
9229            if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9230            {
9231                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
9232                and isConstType($Return, $LibVersion) and $MnglName!~/L\d+$ShortName/)
9233                { # corrected mangling of const global data
9234                  # some global data is not mangled in the TU dump: qt_sine_table (Qt 4.8)
9235                  # and incorrectly mangled by old ACC versions
9236                    $SRemangle = 1;
9237                }
9238            }
9239        }
9240        if(not $CheckHeadersOnly)
9241        { # support for old ABI dumps
9242            if(not checkDump(1, "2.17")
9243            or not checkDump(2, "2.17"))
9244            {
9245                if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9246                {
9247                    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9248                    {
9249                        if(link_symbol($ShortName, $LibVersion, "-Deps"))
9250                        {
9251                            $MnglName = $ShortName;
9252                            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9253                        }
9254                    }
9255                }
9256            }
9257        }
9258        if($Remangle==1 or $SRemangle==1)
9259        { # support for old ABI dumps: some symbols are not mangled in old dumps
9260          # mangle both sets of symbols (old and new)
9261          # NOTE: remangling all symbols by the same mangler
9262            if($MnglName=~/\A_ZN(V|)K/)
9263            { # mangling may be incorrect on old ABI dumps
9264              # because of absent "Const" attribute
9265                $SymbolInfo{$LibVersion}{$InfoId}{"Const"} = 1;
9266            }
9267            if($MnglName=~/\A_ZN(K|)V/)
9268            { # mangling may be incorrect on old ABI dumps
9269              # because of absent "Volatile" attribute
9270                $SymbolInfo{$LibVersion}{$InfoId}{"Volatile"} = 1;
9271            }
9272            if(($ClassID and $MnglName!~/\A(_Z|\?)/)
9273            or (not $ClassID and $CheckHeadersOnly)
9274            or (not $ClassID and not link_symbol($MnglName, $LibVersion, "-Deps")))
9275            { # support for old ABI dumps, GCC >= 4.0
9276              # remangling all manually mangled symbols
9277                if($MnglName = mangle_symbol($InfoId, $LibVersion, "GCC"))
9278                {
9279                    $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9280                    $MangledNames{$LibVersion}{$MnglName} = 1;
9281                }
9282            }
9283        }
9284        elsif($Remangle==-1)
9285        { # remove mangling
9286            $MnglName = "";
9287            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = "";
9288        }
9289        if(not $MnglName) {
9290            next;
9291        }
9292
9293        # NOTE: duplicated entries in the ABI Dump
9294        if(defined $CompleteSignature{$LibVersion}{$MnglName})
9295        {
9296            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
9297            {
9298                if($SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} eq "p1")
9299                {
9300                    next;
9301                }
9302            }
9303        }
9304
9305        if(not $CompleteSignature{$LibVersion}{$MnglName}{"MnglName"})
9306        { # NOTE: global data may enter here twice
9307            %{$CompleteSignature{$LibVersion}{$MnglName}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9308
9309        }
9310        if(not checkDump($LibVersion, "2.6"))
9311        { # support for old dumps
9312          # add "Volatile" attribute
9313            if($MnglName=~/_Z(K|)V/) {
9314                $CompleteSignature{$LibVersion}{$MnglName}{"Volatile"}=1;
9315            }
9316        }
9317        # symbol and its symlink have same signatures
9318        if($SymVer{$LibVersion}{$MnglName}) {
9319            %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$MnglName}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9320        }
9321
9322        if(my $Alias = $CompleteSignature{$LibVersion}{$MnglName}{"Alias"})
9323        {
9324            %{$CompleteSignature{$LibVersion}{$Alias}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9325
9326            if($SymVer{$LibVersion}{$Alias}) {
9327                %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$Alias}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9328            }
9329        }
9330
9331        # clean memory
9332        delete($SymbolInfo{$LibVersion}{$InfoId});
9333    }
9334    if($COMMON_LANGUAGE{$LibVersion} eq "C++" or $OSgroup eq "windows") {
9335        translateSymbols(keys(%{$CompleteSignature{$LibVersion}}), $LibVersion);
9336    }
9337    if($ExtendedCheck)
9338    { # --ext option
9339        addExtension($LibVersion);
9340    }
9341
9342    # clean memory
9343    delete($SymbolInfo{$LibVersion});
9344
9345    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
9346    { # detect allocable classes with public exported constructors
9347      # or classes with auto-generated or inline-only constructors
9348      # and other temp info
9349        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
9350        {
9351            my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
9352            if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"}
9353            and not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"})
9354            { # Class() { ... } will not be exported
9355                if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9356                {
9357                    if($CheckHeadersOnly or link_symbol($Symbol, $LibVersion, "-Deps")) {
9358                        $AllocableClass{$LibVersion}{$ClassName} = 1;
9359                    }
9360                }
9361            }
9362            if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9363            { # all imported class methods
9364                if(symbolFilter($Symbol, $LibVersion, "Affected", "Binary"))
9365                {
9366                    if($CheckHeadersOnly)
9367                    {
9368                        if(not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
9369                        or $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
9370                        { # all symbols except non-virtual inline
9371                            $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9372                        }
9373                    }
9374                    else {
9375                        $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9376                    }
9377                }
9378                if(symbolFilter($Symbol, $LibVersion, "Affected", "Source")) {
9379                    $ClassMethods{"Source"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9380                }
9381            }
9382            $ClassNames{$LibVersion}{$ClassName} = 1;
9383        }
9384        if(my $RetId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
9385        {
9386            my %Base = get_BaseType($RetId, $LibVersion);
9387            if(defined $Base{"Type"}
9388            and $Base{"Type"}=~/Struct|Class/)
9389            {
9390                my $Name = $TypeInfo{$LibVersion}{$Base{"Tid"}}{"Name"};
9391                if($Name=~/<([^<>\s]+)>/)
9392                {
9393                    if(my $Tid = getTypeIdByName($1, $LibVersion)) {
9394                        $ReturnedClass{$LibVersion}{$Tid} = 1;
9395                    }
9396                }
9397                else {
9398                    $ReturnedClass{$LibVersion}{$Base{"Tid"}} = 1;
9399                }
9400            }
9401        }
9402        foreach my $Num (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
9403        {
9404            my $PId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Num}{"type"};
9405            if(get_PLevel($PId, $LibVersion)>=1)
9406            {
9407                if(my %Base = get_BaseType($PId, $LibVersion))
9408                {
9409                    if($Base{"Type"}=~/Struct|Class/)
9410                    {
9411                        $ParamClass{$LibVersion}{$Base{"Tid"}}{$Symbol} = 1;
9412                        foreach my $SubId (get_sub_classes($Base{"Tid"}, $LibVersion, 1))
9413                        { # mark all derived classes
9414                            $ParamClass{$LibVersion}{$SubId}{$Symbol} = 1;
9415                        }
9416                    }
9417                }
9418            }
9419        }
9420
9421        # mapping {short name => symbols}
9422        $Func_ShortName{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}}{$Symbol} = 1;
9423    }
9424    foreach my $MnglName (keys(%VTableClass))
9425    { # reconstruct attributes of v-tables
9426        if(index($MnglName, "_ZTV")==0)
9427        {
9428            if(my $ClassName = $VTableClass{$MnglName})
9429            {
9430                if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
9431                {
9432                    $CompleteSignature{$LibVersion}{$MnglName}{"Header"} = $TypeInfo{$LibVersion}{$ClassId}{"Header"};
9433                    $CompleteSignature{$LibVersion}{$MnglName}{"Class"} = $ClassId;
9434                }
9435            }
9436        }
9437    }
9438
9439    # types
9440    foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9441    {
9442        if(my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"})
9443        {
9444            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"}) {
9445                $ClassNames{$LibVersion}{$TName} = 1;
9446            }
9447            if(defined $TypeInfo{$LibVersion}{$TypeId}{"Base"})
9448            {
9449                $ClassNames{$LibVersion}{$TName} = 1;
9450                foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"Base"}}))
9451                {
9452                    if(my $BName = $TypeInfo{$LibVersion}{$Bid}{"Name"}) {
9453                        $ClassNames{$LibVersion}{$BName} = 1;
9454                    }
9455                }
9456            }
9457        }
9458    }
9459}
9460
9461sub getFirst($$)
9462{
9463    my ($Tid, $LibVersion) = @_;
9464    if(not $Tid) {
9465        return $Tid;
9466    }
9467
9468    if(my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"})
9469    {
9470        if($TName_Tid{$LibVersion}{$Name}) {
9471            return $TName_Tid{$LibVersion}{$Name};
9472        }
9473    }
9474
9475    return $Tid;
9476}
9477
9478sub register_SymbolUsage($$$)
9479{
9480    my ($InfoId, $UsedType, $LibVersion) = @_;
9481
9482    my %FuncInfo = %{$SymbolInfo{$LibVersion}{$InfoId}};
9483    if(my $RTid = getFirst($FuncInfo{"Return"}, $LibVersion))
9484    {
9485        register_TypeUsage($RTid, $UsedType, $LibVersion);
9486        $SymbolInfo{$LibVersion}{$InfoId}{"Return"} = $RTid;
9487    }
9488    if(my $FCid = getFirst($FuncInfo{"Class"}, $LibVersion))
9489    {
9490        register_TypeUsage($FCid, $UsedType, $LibVersion);
9491        $SymbolInfo{$LibVersion}{$InfoId}{"Class"} = $FCid;
9492
9493        if(my $ThisId = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."*const", $LibVersion))
9494        { # register "this" pointer
9495            register_TypeUsage($ThisId, $UsedType, $LibVersion);
9496        }
9497        if(my $ThisId_C = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."const*const", $LibVersion))
9498        { # register "this" pointer (const method)
9499            register_TypeUsage($ThisId_C, $UsedType, $LibVersion);
9500        }
9501    }
9502    foreach my $PPos (keys(%{$FuncInfo{"Param"}}))
9503    {
9504        if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LibVersion))
9505        {
9506            register_TypeUsage($PTid, $UsedType, $LibVersion);
9507            $FuncInfo{"Param"}{$PPos}{"type"} = $PTid;
9508        }
9509    }
9510    foreach my $TPos (keys(%{$FuncInfo{"TParam"}}))
9511    {
9512        my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"};
9513        if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9514            register_TypeUsage($TTid, $UsedType, $LibVersion);
9515        }
9516    }
9517}
9518
9519sub register_TypeUsage($$$)
9520{
9521    my ($TypeId, $UsedType, $LibVersion) = @_;
9522    if(not $TypeId) {
9523        return;
9524    }
9525    if($UsedType->{$TypeId})
9526    { # already registered
9527        return;
9528    }
9529
9530    my %TInfo = get_Type($TypeId, $LibVersion);
9531    if($TInfo{"Type"})
9532    {
9533        if(my $NS = $TInfo{"NameSpace"})
9534        {
9535            if(my $NSTid = $TName_Tid{$LibVersion}{$NS}) {
9536                register_TypeUsage($NSTid, $UsedType, $LibVersion);
9537            }
9538        }
9539
9540        if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/)
9541        {
9542            $UsedType->{$TypeId} = 1;
9543            if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
9544            {
9545                foreach my $BaseId (keys(%{$TInfo{"Base"}})) {
9546                    register_TypeUsage($BaseId, $UsedType, $LibVersion);
9547                }
9548                foreach my $TPos (keys(%{$TInfo{"TParam"}}))
9549                {
9550                    my $TPName = $TInfo{"TParam"}{$TPos}{"name"};
9551                    if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9552                        register_TypeUsage($TTid, $UsedType, $LibVersion);
9553                    }
9554                }
9555            }
9556            foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}}))
9557            {
9558                if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LibVersion))
9559                {
9560                    register_TypeUsage($MTid, $UsedType, $LibVersion);
9561                    $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid;
9562                }
9563            }
9564            if($TInfo{"Type"} eq "FuncPtr"
9565            or $TInfo{"Type"} eq "MethodPtr"
9566            or $TInfo{"Type"} eq "Func")
9567            {
9568                if(my $RTid = $TInfo{"Return"}) {
9569                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9570                }
9571                foreach my $PPos (keys(%{$TInfo{"Param"}}))
9572                {
9573                    if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) {
9574                        register_TypeUsage($PTid, $UsedType, $LibVersion);
9575                    }
9576                }
9577            }
9578            if($TInfo{"Type"} eq "FieldPtr")
9579            {
9580                if(my $RTid = $TInfo{"Return"}) {
9581                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9582                }
9583                if(my $CTid = $TInfo{"Class"}) {
9584                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9585                }
9586            }
9587            if($TInfo{"Type"} eq "MethodPtr")
9588            {
9589                if(my $CTid = $TInfo{"Class"}) {
9590                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9591                }
9592            }
9593        }
9594        elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/)
9595        {
9596            $UsedType->{$TypeId} = 1;
9597            if(my $BTid = getFirst($TInfo{"BaseType"}, $LibVersion))
9598            {
9599                register_TypeUsage($BTid, $UsedType, $LibVersion);
9600                $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $BTid;
9601            }
9602        }
9603        else
9604        { # Intrinsic, TemplateParam, TypeName, SizeOf, etc.
9605            $UsedType->{$TypeId} = 1;
9606        }
9607    }
9608}
9609
9610sub selectSymbol($$$$)
9611{ # select symbol to check or to dump
9612    my ($Symbol, $SInfo, $Level, $LibVersion) = @_;
9613
9614    if($Level eq "Dump")
9615    {
9616        if($SInfo->{"Virt"} or $SInfo->{"PureVirt"})
9617        { # TODO: check if this symbol is from
9618          # base classes of other target symbols
9619            return 1;
9620        }
9621    }
9622
9623    if(not $STDCXX_TESTING and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/)
9624    { # stdc++ interfaces
9625        return 0;
9626    }
9627
9628    my $Target = 0;
9629    if(my $Header = $SInfo->{"Header"}) {
9630        $Target = (is_target_header($Header, 1) or is_target_header($Header, 2));
9631    }
9632    if($ExtendedCheck)
9633    {
9634        if(index($Symbol, "external_func_")==0) {
9635            $Target = 1;
9636        }
9637    }
9638    if($CheckHeadersOnly or $Level eq "Source")
9639    {
9640        if($Target)
9641        {
9642            if($Level eq "Dump")
9643            { # dumped
9644                if($BinaryOnly)
9645                {
9646                    if(not $SInfo->{"InLine"} or $SInfo->{"Data"}) {
9647                        return 1;
9648                    }
9649                }
9650                else {
9651                    return 1;
9652                }
9653            }
9654            elsif($Level eq "Source")
9655            { # checked
9656                return 1;
9657            }
9658            elsif($Level eq "Binary")
9659            { # checked
9660                if(not $SInfo->{"InLine"} or $SInfo->{"Data"}
9661                or $SInfo->{"Virt"} or $SInfo->{"PureVirt"}) {
9662                    return 1;
9663                }
9664            }
9665        }
9666    }
9667    else
9668    { # library is available
9669        if(link_symbol($Symbol, $LibVersion, "-Deps"))
9670        { # exported symbols
9671            return 1;
9672        }
9673        if($Level eq "Dump")
9674        { # dumped
9675            if($BinaryOnly)
9676            {
9677                if($SInfo->{"Data"})
9678                {
9679                    if($Target) {
9680                        return 1;
9681                    }
9682                }
9683            }
9684            else
9685            { # SrcBin
9686                if($Target) {
9687                    return 1;
9688                }
9689            }
9690        }
9691        elsif($Level eq "Source")
9692        { # checked
9693            if($SInfo->{"PureVirt"} or $SInfo->{"Data"} or $SInfo->{"InLine"}
9694            or isInLineInst($SInfo, $LibVersion))
9695            { # skip LOCAL symbols
9696                if($Target) {
9697                    return 1;
9698                }
9699            }
9700        }
9701        elsif($Level eq "Binary")
9702        { # checked
9703            if($SInfo->{"PureVirt"} or $SInfo->{"Data"})
9704            {
9705                if($Target) {
9706                    return 1;
9707                }
9708            }
9709        }
9710    }
9711    return 0;
9712}
9713
9714sub cleanDump($)
9715{ # clean data
9716    my $LibVersion = $_[0];
9717    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
9718    {
9719        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}}))
9720        {
9721            delete($SymbolInfo{$LibVersion}{$InfoId});
9722            next;
9723        }
9724        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9725        if(not $MnglName)
9726        {
9727            delete($SymbolInfo{$LibVersion}{$InfoId});
9728            next;
9729        }
9730        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9731        if(not $ShortName)
9732        {
9733            delete($SymbolInfo{$LibVersion}{$InfoId});
9734            next;
9735        }
9736        if($MnglName eq $ShortName)
9737        { # remove duplicate data
9738            delete($SymbolInfo{$LibVersion}{$InfoId}{"MnglName"});
9739        }
9740        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})) {
9741            delete($SymbolInfo{$LibVersion}{$InfoId}{"Param"});
9742        }
9743        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}})) {
9744            delete($SymbolInfo{$LibVersion}{$InfoId}{"TParam"});
9745        }
9746        delete($SymbolInfo{$LibVersion}{$InfoId}{"Type"});
9747    }
9748    foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
9749    {
9750        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}}))
9751        {
9752            delete($TypeInfo{$LibVersion}{$Tid});
9753            next;
9754        }
9755        delete($TypeInfo{$LibVersion}{$Tid}{"Tid"});
9756        foreach my $Attr ("Header", "Line", "Size", "NameSpace")
9757        {
9758            if(not $TypeInfo{$LibVersion}{$Tid}{$Attr}) {
9759                delete($TypeInfo{$LibVersion}{$Tid}{$Attr});
9760            }
9761        }
9762        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}{"TParam"}})) {
9763            delete($TypeInfo{$LibVersion}{$Tid}{"TParam"});
9764        }
9765    }
9766}
9767
9768sub pickType($$)
9769{
9770    my ($Tid, $LibVersion) = @_;
9771
9772    if(my $Dupl = $TypeTypedef{$LibVersion}{$Tid})
9773    {
9774        if(defined $TypeInfo{$LibVersion}{$Dupl})
9775        {
9776            if($TypeInfo{$LibVersion}{$Dupl}{"Name"} eq $TypeInfo{$LibVersion}{$Tid}{"Name"})
9777            { # duplicate
9778                return 0;
9779            }
9780        }
9781    }
9782
9783    my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"};
9784
9785    if(isBuiltIn($THeader)) {
9786        return 0;
9787    }
9788
9789    if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/) {
9790        return 0;
9791    }
9792
9793    if(isAnon($TypeInfo{$LibVersion}{$Tid}{"Name"})) {
9794        return 0;
9795    }
9796
9797    if(selfTypedef($Tid, $LibVersion)) {
9798        return 0;
9799    }
9800
9801    if(not isTargetType($Tid, $LibVersion)) {
9802        return 0;
9803    }
9804
9805    return 0;
9806}
9807
9808sub isTargetType($$)
9809{
9810    my ($Tid, $LibVersion) = @_;
9811
9812    if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/)
9813    { # derived
9814        return 1;
9815    }
9816
9817    if(my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"})
9818    { # NOTE: header is defined to source if undefined (DWARF dumps)
9819        if(not is_target_header($THeader, $LibVersion))
9820        { # from target headers
9821            return 0;
9822        }
9823    }
9824    else
9825    { # NOTE: if type is defined in source
9826        if($UsedDump{$LibVersion}{"Public"})
9827        {
9828            if(isPrivateABI($Tid, $LibVersion)) {
9829                return 0;
9830            }
9831            else {
9832                return 1;
9833            }
9834        }
9835        else {
9836            return 0;
9837        }
9838    }
9839
9840    if($SkipInternalTypes)
9841    {
9842        if($TypeInfo{$LibVersion}{$Tid}{"Name"}=~/($SkipInternalTypes)/)
9843        {
9844            return 0;
9845        }
9846    }
9847
9848    return 1;
9849}
9850
9851sub remove_Unused($$)
9852{ # remove unused data types from the ABI dump
9853    my ($LibVersion, $Kind) = @_;
9854
9855    my %UsedType = ();
9856
9857    foreach my $InfoId (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}}))
9858    {
9859        register_SymbolUsage($InfoId, \%UsedType, $LibVersion);
9860    }
9861    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9862    {
9863        if($UsedType{$Tid})
9864        { # All & Extended
9865            next;
9866        }
9867
9868        if($Kind eq "Extended")
9869        {
9870            if(pickType($Tid, $LibVersion))
9871            {
9872                my %Tree = ();
9873                register_TypeUsage($Tid, \%Tree, $LibVersion);
9874
9875                my $Tmpl = 0;
9876                foreach (sort {int($a)<=>int($b)} keys(%Tree))
9877                {
9878                    if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9879                    or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
9880                    {
9881                        $Tmpl = 1;
9882                        last;
9883                    }
9884                }
9885                if(not $Tmpl)
9886                {
9887                    foreach (keys(%Tree)) {
9888                        $UsedType{$_} = 1;
9889                    }
9890                }
9891            }
9892        }
9893    }
9894
9895    my %Delete = ();
9896
9897    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9898    { # remove unused types
9899        if($UsedType{$Tid})
9900        { # All & Extended
9901            next;
9902        }
9903
9904        if($Kind eq "Extra")
9905        {
9906            my %Tree = ();
9907            register_TypeUsage($Tid, \%Tree, $LibVersion);
9908
9909            foreach (sort {int($a)<=>int($b)} keys(%Tree))
9910            {
9911                if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9912                or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
9913                {
9914                    $Delete{$Tid} = 1;
9915                    last;
9916                }
9917            }
9918        }
9919        else
9920        {
9921            # remove type
9922            delete($TypeInfo{$LibVersion}{$Tid});
9923        }
9924    }
9925
9926    if($Kind eq "Extra")
9927    { # remove duplicates
9928        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9929        {
9930            if($UsedType{$Tid})
9931            { # All & Extended
9932                next;
9933            }
9934
9935            my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"};
9936
9937            if($TName_Tid{$LibVersion}{$Name} ne $Tid) {
9938                delete($TypeInfo{$LibVersion}{$Tid});
9939            }
9940        }
9941    }
9942
9943    foreach my $Tid (keys(%Delete))
9944    {
9945        delete($TypeInfo{$LibVersion}{$Tid});
9946    }
9947}
9948
9949sub check_Completeness($$)
9950{
9951    my ($Info, $LibVersion) = @_;
9952
9953    # data types
9954    if(defined $Info->{"Memb"})
9955    {
9956        foreach my $Pos (keys(%{$Info->{"Memb"}}))
9957        {
9958            if(defined $Info->{"Memb"}{$Pos}{"type"}) {
9959                check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}, $LibVersion);
9960            }
9961        }
9962    }
9963    if(defined $Info->{"Base"})
9964    {
9965        foreach my $Bid (keys(%{$Info->{"Base"}})) {
9966            check_TypeInfo($Bid, $LibVersion);
9967        }
9968    }
9969    if(defined $Info->{"BaseType"}) {
9970        check_TypeInfo($Info->{"BaseType"}, $LibVersion);
9971    }
9972    if(defined $Info->{"TParam"})
9973    {
9974        foreach my $Pos (keys(%{$Info->{"TParam"}}))
9975        {
9976            my $TName = $Info->{"TParam"}{$Pos}{"name"};
9977            if($TName=~/\A\(.+\)(true|false|\d.*)\Z/) {
9978                next;
9979            }
9980            if($TName eq "_BoolType") {
9981                next;
9982            }
9983            if($TName=~/\Asizeof\(/) {
9984                next;
9985            }
9986            if(my $Tid = $TName_Tid{$LibVersion}{$TName}) {
9987                check_TypeInfo($Tid, $LibVersion);
9988            }
9989            else
9990            {
9991                if(defined $Debug) {
9992                    printMsg("WARNING", "missed type $TName");
9993                }
9994            }
9995        }
9996    }
9997
9998    # symbols
9999    if(defined $Info->{"Param"})
10000    {
10001        foreach my $Pos (keys(%{$Info->{"Param"}}))
10002        {
10003            if(defined $Info->{"Param"}{$Pos}{"type"}) {
10004                check_TypeInfo($Info->{"Param"}{$Pos}{"type"}, $LibVersion);
10005            }
10006        }
10007    }
10008    if(defined $Info->{"Return"}) {
10009        check_TypeInfo($Info->{"Return"}, $LibVersion);
10010    }
10011    if(defined $Info->{"Class"}) {
10012        check_TypeInfo($Info->{"Class"}, $LibVersion);
10013    }
10014}
10015
10016sub check_TypeInfo($$)
10017{
10018    my ($Tid, $LibVersion) = @_;
10019
10020    if(defined $CheckedTypeInfo{$LibVersion}{$Tid}) {
10021        return;
10022    }
10023    $CheckedTypeInfo{$LibVersion}{$Tid} = 1;
10024
10025    if(defined $TypeInfo{$LibVersion}{$Tid})
10026    {
10027        if(not $TypeInfo{$LibVersion}{$Tid}{"Name"}) {
10028            printMsg("ERROR", "missed type name ($Tid)");
10029        }
10030        check_Completeness($TypeInfo{$LibVersion}{$Tid}, $LibVersion);
10031    }
10032    else {
10033        printMsg("ERROR", "missed type id $Tid");
10034    }
10035}
10036
10037sub selfTypedef($$)
10038{
10039    my ($TypeId, $LibVersion) = @_;
10040    my %Type = get_Type($TypeId, $LibVersion);
10041    if($Type{"Type"} eq "Typedef")
10042    {
10043        my %Base = get_OneStep_BaseType($TypeId, $TypeInfo{$LibVersion});
10044        if($Base{"Type"}=~/Class|Struct/)
10045        {
10046            if($Type{"Name"} eq $Base{"Name"}) {
10047                return 1;
10048            }
10049            elsif($Type{"Name"}=~/::(\w+)\Z/)
10050            {
10051                if($Type{"Name"} eq $Base{"Name"}."::".$1)
10052                { # QPointer<QWidget>::QPointer
10053                    return 1;
10054                }
10055            }
10056        }
10057    }
10058    return 0;
10059}
10060
10061sub addExtension($)
10062{
10063    my $LibVersion = $_[0];
10064    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
10065    {
10066        if(pickType($Tid, $LibVersion))
10067        {
10068            my $TName = $TypeInfo{$LibVersion}{$Tid}{"Name"};
10069            $TName=~s/\A(struct|union|class|enum) //;
10070            my $Symbol = "external_func_".$TName;
10071
10072            %{$CompleteSignature{$LibVersion}{$Symbol}} = (
10073                "Header" => "extended.h",
10074                "ShortName" => $Symbol,
10075                "MnglName" => $Symbol,
10076                "Param" => { 0 => { "type"=>$Tid, "name"=>"p1" } }
10077            );
10078
10079            $ExtendedSymbols{$Symbol} = 1;
10080            $CheckedSymbols{"Binary"}{$Symbol} = 1;
10081            $CheckedSymbols{"Source"}{$Symbol} = 1;
10082        }
10083    }
10084    $ExtendedSymbols{"external_func_0"} = 1;
10085    $CheckedSymbols{"Binary"}{"external_func_0"} = 1;
10086    $CheckedSymbols{"Source"}{"external_func_0"} = 1;
10087}
10088
10089sub findMethod($$$)
10090{
10091    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10092    foreach my $BaseClass_Id (keys(%{$TypeInfo{$LibVersion}{$ClassId}{"Base"}}))
10093    {
10094        if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LibVersion)) {
10095            return $VirtMethodInClass;
10096        }
10097        elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LibVersion)) {
10098            return $VirtMethodInBaseClasses;
10099        }
10100    }
10101    return "";
10102}
10103
10104sub findMethod_Class($$$)
10105{
10106    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10107    my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
10108    return "" if(not defined $VirtualTable{$LibVersion}{$ClassName});
10109    my $TargetSuffix = get_symbol_suffix($VirtFunc, 1);
10110    my $TargetShortName = $CompleteSignature{$LibVersion}{$VirtFunc}{"ShortName"};
10111    foreach my $Candidate (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10112    { # search for interface with the same parameters suffix (overridden)
10113        if($TargetSuffix eq get_symbol_suffix($Candidate, 1))
10114        {
10115            if($CompleteSignature{$LibVersion}{$VirtFunc}{"Destructor"})
10116            {
10117                if($CompleteSignature{$LibVersion}{$Candidate}{"Destructor"})
10118                {
10119                    if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/)
10120                    or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/)
10121                    or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) {
10122                        return $Candidate;
10123                    }
10124                }
10125            }
10126            else
10127            {
10128                if($TargetShortName eq $CompleteSignature{$LibVersion}{$Candidate}{"ShortName"}) {
10129                    return $Candidate;
10130                }
10131            }
10132        }
10133    }
10134    return "";
10135}
10136
10137sub registerVTable($)
10138{
10139    my $LibVersion = $_[0];
10140    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
10141    {
10142        if($CompleteSignature{$LibVersion}{$Symbol}{"Virt"}
10143        or $CompleteSignature{$LibVersion}{$Symbol}{"PureVirt"})
10144        {
10145            my $ClassName = $TypeInfo{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"Class"}}{"Name"};
10146            next if(not $STDCXX_TESTING and $ClassName=~/\A(std::|__cxxabi)/);
10147            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}
10148            and $Symbol=~/D2E/)
10149            { # pure virtual D2-destructors are marked as "virt" in the dump
10150              # virtual D2-destructors are NOT marked as "virt" in the dump
10151              # both destructors are not presented in the v-table
10152                next;
10153            }
10154            my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
10155            $VirtualTable{$LibVersion}{$ClassName}{$MnglName} = 1;
10156        }
10157    }
10158}
10159
10160sub registerOverriding($)
10161{
10162    my $LibVersion = $_[0];
10163    my @Classes = keys(%{$VirtualTable{$LibVersion}});
10164    @Classes = sort {int($TName_Tid{$LibVersion}{$a})<=>int($TName_Tid{$LibVersion}{$b})} @Classes;
10165    foreach my $ClassName (@Classes)
10166    {
10167        foreach my $VirtFunc (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10168        {
10169            if($CompleteSignature{$LibVersion}{$VirtFunc}{"PureVirt"})
10170            { # pure virtuals
10171                next;
10172            }
10173            my $ClassId = $TName_Tid{$LibVersion}{$ClassName};
10174            if(my $Overridden = findMethod($VirtFunc, $ClassId, $LibVersion))
10175            {
10176                if($CompleteSignature{$LibVersion}{$Overridden}{"Virt"}
10177                or $CompleteSignature{$LibVersion}{$Overridden}{"PureVirt"})
10178                { # both overridden virtual methods
10179                  # and implemented pure virtual methods
10180                    $CompleteSignature{$LibVersion}{$VirtFunc}{"Override"} = $Overridden;
10181                    $OverriddenMethods{$LibVersion}{$Overridden}{$VirtFunc} = 1;
10182                    delete($VirtualTable{$LibVersion}{$ClassName}{$VirtFunc}); # remove from v-table model
10183                }
10184            }
10185        }
10186        if(not keys(%{$VirtualTable{$LibVersion}{$ClassName}})) {
10187            delete($VirtualTable{$LibVersion}{$ClassName});
10188        }
10189    }
10190}
10191
10192sub setVirtFuncPositions($)
10193{
10194    my $LibVersion = $_[0];
10195    foreach my $ClassName (keys(%{$VirtualTable{$LibVersion}}))
10196    {
10197        my ($Num, $Rel) = (1, 0);
10198
10199        if(my @Funcs = sort keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10200        {
10201            if($UsedDump{$LibVersion}{"DWARF"}) {
10202                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @Funcs;
10203            }
10204            else {
10205                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @Funcs;
10206            }
10207            foreach my $VirtFunc (@Funcs)
10208            {
10209                if($UsedDump{$LibVersion}{"DWARF"}) {
10210                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $CompleteSignature{$LibVersion}{$VirtFunc}{"VirtPos"};
10211                }
10212                else {
10213                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $Num++;
10214                }
10215
10216                # set relative positions
10217                if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc}
10218                and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc})
10219                { # relative position excluding added and removed virtual functions
10220                    if(not $CompleteSignature{1}{$VirtFunc}{"Override"}
10221                    and not $CompleteSignature{2}{$VirtFunc}{"Override"}) {
10222                        $CompleteSignature{$LibVersion}{$VirtFunc}{"RelPos"} = $Rel++;
10223                    }
10224                }
10225            }
10226        }
10227    }
10228    foreach my $ClassName (keys(%{$ClassNames{$LibVersion}}))
10229    {
10230        my $AbsNum = 1;
10231        foreach my $VirtFunc (getVTable_Model($TName_Tid{$LibVersion}{$ClassName}, $LibVersion)) {
10232            $VirtualTable_Model{$LibVersion}{$ClassName}{$VirtFunc} = $AbsNum++;
10233        }
10234    }
10235}
10236
10237sub get_sub_classes($$$)
10238{
10239    my ($ClassId, $LibVersion, $Recursive) = @_;
10240    return () if(not defined $Class_SubClasses{$LibVersion}{$ClassId});
10241    my @Subs = ();
10242    foreach my $SubId (keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
10243    {
10244        if($Recursive)
10245        {
10246            foreach my $SubSubId (get_sub_classes($SubId, $LibVersion, $Recursive)) {
10247                push(@Subs, $SubSubId);
10248            }
10249        }
10250        push(@Subs, $SubId);
10251    }
10252    return @Subs;
10253}
10254
10255sub get_base_classes($$$)
10256{
10257    my ($ClassId, $LibVersion, $Recursive) = @_;
10258    my %ClassType = get_Type($ClassId, $LibVersion);
10259    return () if(not defined $ClassType{"Base"});
10260    my @Bases = ();
10261    foreach my $BaseId (sort {int($ClassType{"Base"}{$a}{"pos"})<=>int($ClassType{"Base"}{$b}{"pos"})}
10262    keys(%{$ClassType{"Base"}}))
10263    {
10264        if($Recursive)
10265        {
10266            foreach my $SubBaseId (get_base_classes($BaseId, $LibVersion, $Recursive)) {
10267                push(@Bases, $SubBaseId);
10268            }
10269        }
10270        push(@Bases, $BaseId);
10271    }
10272    return @Bases;
10273}
10274
10275sub getVTable_Model($$)
10276{ # return an ordered list of v-table elements
10277    my ($ClassId, $LibVersion) = @_;
10278    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10279    my @Elements = ();
10280    foreach my $BaseId (@Bases, $ClassId)
10281    {
10282        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10283        {
10284            if(defined $VirtualTable{$LibVersion}{$BName})
10285            {
10286                my @VFuncs = keys(%{$VirtualTable{$LibVersion}{$BName}});
10287                if($UsedDump{$LibVersion}{"DWARF"}) {
10288                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @VFuncs;
10289                }
10290                else {
10291                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @VFuncs;
10292                }
10293                foreach my $VFunc (@VFuncs) {
10294                    push(@Elements, $VFunc);
10295                }
10296            }
10297        }
10298    }
10299    return @Elements;
10300}
10301
10302sub getVShift($$)
10303{
10304    my ($ClassId, $LibVersion) = @_;
10305    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10306    my $VShift = 0;
10307    foreach my $BaseId (@Bases)
10308    {
10309        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10310        {
10311            if(defined $VirtualTable{$LibVersion}{$BName}) {
10312                $VShift+=keys(%{$VirtualTable{$LibVersion}{$BName}});
10313            }
10314        }
10315    }
10316    return $VShift;
10317}
10318
10319sub getShift($$)
10320{
10321    my ($ClassId, $LibVersion) = @_;
10322    my @Bases = get_base_classes($ClassId, $LibVersion, 0);
10323    my $Shift = 0;
10324    foreach my $BaseId (@Bases)
10325    {
10326        if(my $Size = $TypeInfo{$LibVersion}{$BaseId}{"Size"})
10327        {
10328            if($Size!=1)
10329            { # not empty base class
10330                $Shift+=$Size;
10331            }
10332        }
10333    }
10334    return $Shift;
10335}
10336
10337sub getVTable_Size($$)
10338{ # number of v-table elements
10339    my ($ClassName, $LibVersion) = @_;
10340    my $Size = 0;
10341    # three approaches
10342    if(not $Size)
10343    { # real size
10344        if(my %VTable = getVTable_Real($ClassName, $LibVersion)) {
10345            $Size = keys(%VTable);
10346        }
10347    }
10348    if(not $Size)
10349    { # shared library symbol size
10350        if($Size = getSymbolSize($ClassVTable{$ClassName}, $LibVersion)) {
10351            $Size /= $WORD_SIZE{$LibVersion};
10352        }
10353    }
10354    if(not $Size)
10355    { # model size
10356        if(defined $VirtualTable_Model{$LibVersion}{$ClassName}) {
10357            $Size = keys(%{$VirtualTable_Model{$LibVersion}{$ClassName}}) + 2;
10358        }
10359    }
10360    return $Size;
10361}
10362
10363sub isCopyingClass($$)
10364{
10365    my ($TypeId, $LibVersion) = @_;
10366    return $TypeInfo{$LibVersion}{$TypeId}{"Copied"};
10367}
10368
10369sub isLeafClass($$)
10370{
10371    my ($ClassId, $LibVersion) = @_;
10372    return (not keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}));
10373}
10374
10375sub havePubFields($)
10376{ # check structured type for public fields
10377    return isAccessible($_[0], {}, 0, -1);
10378}
10379
10380sub isAccessible($$$$)
10381{ # check interval in structured type for public fields
10382    my ($TypePtr, $Skip, $Start, $End) = @_;
10383    return 0 if(not $TypePtr);
10384    if($End==-1) {
10385        $End = keys(%{$TypePtr->{"Memb"}})-1;
10386    }
10387    foreach my $MemPos (sort {int($a)<=>int($b)} keys(%{$TypePtr->{"Memb"}}))
10388    {
10389        if($Skip and $Skip->{$MemPos})
10390        { # skip removed/added fields
10391            next;
10392        }
10393        if(int($MemPos)>=$Start and int($MemPos)<=$End)
10394        {
10395            if(isPublic($TypePtr, $MemPos)) {
10396                return ($MemPos+1);
10397            }
10398        }
10399    }
10400    return 0;
10401}
10402
10403sub isReserved($)
10404{ # reserved fields == private
10405    my $MName = $_[0];
10406    if($MName=~/reserved|padding|f_spare/i) {
10407        return 1;
10408    }
10409    if($MName=~/\A[_]*(spare|pad|unused|dummy)[_\d]*\Z/i) {
10410        return 1;
10411    }
10412    if($MName=~/(pad\d+)/i) {
10413        return 1;
10414    }
10415    return 0;
10416}
10417
10418sub isPublic($$)
10419{
10420    my ($TypePtr, $FieldPos) = @_;
10421    return 0 if(not $TypePtr);
10422    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos});
10423    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"});
10424    if(not $TypePtr->{"Memb"}{$FieldPos}{"access"})
10425    { # by name in C language
10426      # FIXME: add other methods to detect private members
10427        my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"};
10428        if($MName=~/priv|abidata|parent_object/i)
10429        { # C-styled private data
10430            return 0;
10431        }
10432        if(lc($MName) eq "abi")
10433        { # ABI information/reserved field
10434            return 0;
10435        }
10436        if(isReserved($MName))
10437        { # reserved fields
10438            return 0;
10439        }
10440        return 1;
10441    }
10442    elsif($TypePtr->{"Memb"}{$FieldPos}{"access"} ne "private")
10443    { # by access in C++ language
10444        return 1;
10445    }
10446    return 0;
10447}
10448
10449sub getVTable_Real($$)
10450{
10451    my ($ClassName, $LibVersion) = @_;
10452    if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
10453    {
10454        my %Type = get_Type($ClassId, $LibVersion);
10455        if(defined $Type{"VTable"}) {
10456            return %{$Type{"VTable"}};
10457        }
10458    }
10459    return ();
10460}
10461
10462sub cmpVTables($)
10463{
10464    my $ClassName = $_[0];
10465    my $Res = cmpVTables_Real($ClassName, 1);
10466    if($Res==-1) {
10467        $Res = cmpVTables_Model($ClassName);
10468    }
10469    return $Res;
10470}
10471
10472sub cmpVTables_Model($)
10473{
10474    my $ClassName = $_[0];
10475    foreach my $Symbol (keys(%{$VirtualTable_Model{1}{$ClassName}}))
10476    {
10477        if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) {
10478            return 1;
10479        }
10480    }
10481    return 0;
10482}
10483
10484sub cmpVTables_Real($$)
10485{
10486    my ($ClassName, $Strong) = @_;
10487    if(defined $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}) {
10488        return $Cache{"cmpVTables_Real"}{$Strong}{$ClassName};
10489    }
10490    my %VTable_Old = getVTable_Real($ClassName, 1);
10491    my %VTable_New = getVTable_Real($ClassName, 2);
10492    if(not %VTable_Old or not %VTable_New)
10493    { # old ABI dumps
10494        return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1);
10495    }
10496    my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New));
10497    foreach my $Offset (sort {int($a)<=>int($b)} keys(%Indexes))
10498    {
10499        if(not defined $VTable_Old{$Offset})
10500        { # v-table v.1 < v-table v.2
10501            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = $Strong);
10502        }
10503        my $Entry1 = $VTable_Old{$Offset};
10504        if(not defined $VTable_New{$Offset})
10505        { # v-table v.1 > v-table v.2
10506            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = ($Strong or $Entry1!~/__cxa_pure_virtual/));
10507        }
10508        my $Entry2 = $VTable_New{$Offset};
10509
10510        $Entry1 = simpleVEntry($Entry1);
10511        $Entry2 = simpleVEntry($Entry2);
10512
10513        if($Entry1=~/ 0x/ or $Entry2=~/ 0x/)
10514        { # NOTE: problem with vtable-dumper
10515            next;
10516        }
10517
10518        if($Entry1 ne $Entry2)
10519        { # register as changed
10520            if($Entry1=~/::([^:]+)\Z/)
10521            {
10522                my $M1 = $1;
10523                if($Entry2=~/::([^:]+)\Z/)
10524                {
10525                    my $M2 = $1;
10526                    if($M1 eq $M2)
10527                    { # overridden
10528                        next;
10529                    }
10530                }
10531            }
10532            if(differentDumps("G"))
10533            {
10534                if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/)
10535                {
10536                    # GCC 4.6.1: -0x00000000000000010
10537                    # GCC 4.7.0: -16
10538                    next;
10539                }
10540            }
10541            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 1);
10542        }
10543    }
10544    return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 0);
10545}
10546
10547sub mergeVTables($)
10548{ # merging v-tables without diagnostics
10549    my $Level = $_[0];
10550    foreach my $ClassName (keys(%{$VirtualTable{1}}))
10551    {
10552        my $ClassId = $TName_Tid{1}{$ClassName};
10553        if(isPrivateABI($ClassId, 1)) {
10554            next;
10555        }
10556
10557        if($VTableChanged_M{$ClassName})
10558        { # already registered
10559            next;
10560        }
10561        if(cmpVTables_Real($ClassName, 0)==1)
10562        {
10563            my @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10564            foreach my $Symbol (@Affected)
10565            {
10566                %{$CompatProblems{$Level}{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=(
10567                    "Type_Name"=>$ClassName,
10568                    "Target"=>$ClassName);
10569            }
10570        }
10571    }
10572}
10573
10574sub mergeBases($)
10575{
10576    my $Level = $_[0];
10577    foreach my $ClassName (keys(%{$ClassNames{1}}))
10578    { # detect added and removed virtual functions
10579        my $ClassId = $TName_Tid{1}{$ClassName};
10580        next if(not $ClassId);
10581
10582        if(isPrivateABI($ClassId, 1)) {
10583            next;
10584        }
10585
10586        if(defined $VirtualTable{2}{$ClassName})
10587        {
10588            foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}}))
10589            {
10590                if($TName_Tid{1}{$ClassName}
10591                and not defined $VirtualTable{1}{$ClassName}{$Symbol})
10592                { # added to v-table
10593                    if(defined $CompleteSignature{1}{$Symbol}
10594                    and $CompleteSignature{1}{$Symbol}{"Virt"})
10595                    { # override some method in v.1
10596                        next;
10597                    }
10598                    $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10599                }
10600            }
10601        }
10602        if(defined $VirtualTable{1}{$ClassName})
10603        {
10604            foreach my $Symbol (keys(%{$VirtualTable{1}{$ClassName}}))
10605            {
10606                if($TName_Tid{2}{$ClassName}
10607                and not defined $VirtualTable{2}{$ClassName}{$Symbol})
10608                { # removed from v-table
10609                    if(defined $CompleteSignature{2}{$Symbol}
10610                    and $CompleteSignature{2}{$Symbol}{"Virt"})
10611                    { # override some method in v.2
10612                        next;
10613                    }
10614                    $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10615                }
10616            }
10617        }
10618        if($Level eq "Binary")
10619        { # Binary-level
10620            my %Class_Type = get_Type($ClassId, 1);
10621            foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}}))
10622            { # check replacements, including pure virtual methods
10623                my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc};
10624                foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$ClassName}}))
10625                {
10626                    my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc};
10627                    if($AddedPos==$RemovedPos)
10628                    {
10629                        $VirtualReplacement{$AddedVFunc} = $RemovedVFunc;
10630                        $VirtualReplacement{$RemovedVFunc} = $AddedVFunc;
10631                        last; # other methods will be reported as "added" or "removed"
10632                    }
10633                }
10634                if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc})
10635                {
10636                    if(lc($AddedVFunc) eq lc($RemovedVFunc))
10637                    { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0)
10638                        next;
10639                    }
10640                    my $ProblemType = "Virtual_Replacement";
10641                    my @Affected = ($RemovedVFunc);
10642                    if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
10643                    { # pure methods
10644                        if(not isUsedClass($ClassId, 1, $Level))
10645                        { # not a parameter of some exported method
10646                            next;
10647                        }
10648                        $ProblemType = "Pure_Virtual_Replacement";
10649
10650                        # affected all methods (both virtual and non-virtual ones)
10651                        @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10652                        push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}}));
10653                    }
10654                    $VTableChanged_M{$ClassName}=1;
10655                    foreach my $AffectedInt (@Affected)
10656                    {
10657                        if($CompleteSignature{1}{$AffectedInt}{"PureVirt"})
10658                        { # affected exported methods only
10659                            next;
10660                        }
10661                        if(not symbolFilter($AffectedInt, 1, "Affected", $Level)) {
10662                            next;
10663                        }
10664                        %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
10665                            "Type_Name"=>$Class_Type{"Name"},
10666                            "Target"=>get_Signature($AddedVFunc, 2),
10667                            "Old_Value"=>get_Signature($RemovedVFunc, 1));
10668                    }
10669                }
10670            }
10671        }
10672    }
10673    if(not checkDump(1, "2.0")
10674    or not checkDump(2, "2.0"))
10675    { # support for old ABI dumps
10676      # "Base" attribute introduced in ACC 1.22 (ABI dump 2.0 format)
10677        return;
10678    }
10679    foreach my $ClassName (sort keys(%{$ClassNames{1}}))
10680    {
10681        my $ClassId_Old = $TName_Tid{1}{$ClassName};
10682        next if(not $ClassId_Old);
10683
10684        if(isPrivateABI($ClassId_Old, 1)) {
10685            next;
10686        }
10687
10688        if(not isCreatable($ClassId_Old, 1))
10689        { # skip classes without public constructors (including auto-generated)
10690          # example: class has only a private exported or private inline constructor
10691            next;
10692        }
10693        if($ClassName=~/>/)
10694        { # skip affected template instances
10695            next;
10696        }
10697        my %Class_Old = get_Type($ClassId_Old, 1);
10698        my $ClassId_New = $TName_Tid{2}{$ClassName};
10699        if(not $ClassId_New) {
10700            next;
10701        }
10702        my %Class_New = get_Type($ClassId_New, 2);
10703        if($Class_New{"Type"}!~/Class|Struct/)
10704        { # became typedef
10705            if($Level eq "Binary") {
10706                next;
10707            }
10708            if($Level eq "Source")
10709            {
10710                %Class_New = get_PureType($ClassId_New, $TypeInfo{2});
10711                if($Class_New{"Type"}!~/Class|Struct/) {
10712                    next;
10713                }
10714                $ClassId_New = $Class_New{"Tid"};
10715            }
10716        }
10717
10718        if(not $Class_New{"Size"} or not $Class_Old{"Size"})
10719        { # incomplete info in the ABI dump
10720            next;
10721        }
10722
10723
10724        my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}});
10725        my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}});
10726
10727        my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncover_typedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old;
10728        my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncover_typedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New;
10729
10730        my ($BNum1, $BNum2) = (1, 1);
10731        my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old;
10732        my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New;
10733        my %ShortBase_Old = map {get_ShortClass($_, 1) => 1} @Bases_Old;
10734        my %ShortBase_New = map {get_ShortClass($_, 2) => 1} @Bases_New;
10735        my $Shift_Old = getShift($ClassId_Old, 1);
10736        my $Shift_New = getShift($ClassId_New, 2);
10737        my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New;
10738        my ($Added, $Removed) = (0, 0);
10739        my @StableBases_Old = ();
10740        foreach my $BaseId (@Bases_Old)
10741        {
10742            my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10743            if($BasePos_New{$Tr_Old{$BaseName}}) {
10744                push(@StableBases_Old, $BaseId);
10745            }
10746            elsif(not $ShortBase_New{$Tr_Old{$BaseName}}
10747            and not $ShortBase_New{get_ShortClass($BaseId, 1)})
10748            { # removed base
10749              # excluding namespace::SomeClass to SomeClass renaming
10750                my $ProblemKind = "Removed_Base_Class";
10751                if($Level eq "Binary")
10752                { # Binary-level
10753                    if($Shift_Old ne $Shift_New)
10754                    { # affected fields
10755                        if(havePubFields(\%Class_Old)) {
10756                            $ProblemKind .= "_And_Shift";
10757                        }
10758                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10759                            $ProblemKind .= "_And_Size";
10760                        }
10761                    }
10762                    if(keys(%{$VirtualTable_Model{1}{$BaseName}})
10763                    and cmpVTables($ClassName)==1)
10764                    { # affected v-table
10765                        $ProblemKind .= "_And_VTable";
10766                        $VTableChanged_M{$ClassName}=1;
10767                    }
10768                }
10769                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10770                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10771                {
10772                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10773                    {
10774                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10775                        if($ProblemKind=~/VTable/) {
10776                            $VTableChanged_M{$SubName}=1;
10777                        }
10778                    }
10779                }
10780                foreach my $Interface (@Affected)
10781                {
10782                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10783                        next;
10784                    }
10785                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10786                        "Type_Name"=>$ClassName,
10787                        "Target"=>$BaseName,
10788                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10789                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10790                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10791                }
10792                $Removed+=1;
10793            }
10794        }
10795        my @StableBases_New = ();
10796        foreach my $BaseId (@Bases_New)
10797        {
10798            my $BaseName = $TypeInfo{2}{$BaseId}{"Name"};
10799            if($BasePos_Old{$Tr_New{$BaseName}}) {
10800                push(@StableBases_New, $BaseId);
10801            }
10802            elsif(not $ShortBase_Old{$Tr_New{$BaseName}}
10803            and not $ShortBase_Old{get_ShortClass($BaseId, 2)})
10804            { # added base
10805              # excluding namespace::SomeClass to SomeClass renaming
10806                my $ProblemKind = "Added_Base_Class";
10807                if($Level eq "Binary")
10808                { # Binary-level
10809                    if($Shift_Old ne $Shift_New)
10810                    { # affected fields
10811                        if(havePubFields(\%Class_Old)) {
10812                            $ProblemKind .= "_And_Shift";
10813                        }
10814                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10815                            $ProblemKind .= "_And_Size";
10816                        }
10817                    }
10818                    if(keys(%{$VirtualTable_Model{2}{$BaseName}})
10819                    and cmpVTables($ClassName)==1)
10820                    { # affected v-table
10821                        $ProblemKind .= "_And_VTable";
10822                        $VTableChanged_M{$ClassName}=1;
10823                    }
10824                }
10825                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10826                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10827                {
10828                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10829                    {
10830                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10831                        if($ProblemKind=~/VTable/) {
10832                            $VTableChanged_M{$SubName}=1;
10833                        }
10834                    }
10835                }
10836                foreach my $Interface (@Affected)
10837                {
10838                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10839                        next;
10840                    }
10841                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10842                        "Type_Name"=>$ClassName,
10843                        "Target"=>$BaseName,
10844                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10845                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10846                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10847                }
10848                $Added+=1;
10849            }
10850        }
10851        if($Level eq "Binary")
10852        { # Binary-level
10853            ($BNum1, $BNum2) = (1, 1);
10854            my %BaseRelPos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @StableBases_Old;
10855            my %BaseRelPos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @StableBases_New;
10856            foreach my $BaseId (@Bases_Old)
10857            {
10858                my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10859                if(my $NewPos = $BaseRelPos_New{$Tr_Old{$BaseName}})
10860                {
10861                    my $BaseNewId = $BaseId_New{$Tr_Old{$BaseName}};
10862                    my $OldPos = $BaseRelPos_Old{$Tr_Old{$BaseName}};
10863                    if($NewPos!=$OldPos)
10864                    { # changed position of the base class
10865                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10866                        {
10867                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10868                                next;
10869                            }
10870                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=(
10871                                "Type_Name"=>$ClassName,
10872                                "Target"=>$BaseName,
10873                                "Old_Value"=>$OldPos-1,
10874                                "New_Value"=>$NewPos-1  );
10875                        }
10876                    }
10877                    if($Class_Old{"Base"}{$BaseId}{"virtual"}
10878                    and not $Class_New{"Base"}{$BaseNewId}{"virtual"})
10879                    { # became non-virtual base
10880                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10881                        {
10882                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10883                                next;
10884                            }
10885                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=(
10886                                "Type_Name"=>$ClassName,
10887                                "Target"=>$BaseName  );
10888                        }
10889                    }
10890                    elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"}
10891                    and $Class_New{"Base"}{$BaseNewId}{"virtual"})
10892                    { # became virtual base
10893                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10894                        {
10895                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10896                                next;
10897                            }
10898                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=(
10899                                "Type_Name"=>$ClassName,
10900                                "Target"=>$BaseName  );
10901                        }
10902                    }
10903                }
10904            }
10905            # detect size changes in base classes
10906            if($Shift_Old!=$Shift_New)
10907            { # size of allocable class
10908                foreach my $BaseId (@StableBases_Old)
10909                { # search for changed base
10910                    my %BaseType = get_Type($BaseId, 1);
10911                    my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"};
10912                    my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"};
10913                    if($Size_Old ne $Size_New
10914                    and $Size_Old and $Size_New)
10915                    {
10916                        my $ProblemType = undef;
10917                        if(isCopyingClass($BaseId, 1)) {
10918                            $ProblemType = "Size_Of_Copying_Class";
10919                        }
10920                        elsif($AllocableClass{1}{$BaseType{"Name"}})
10921                        {
10922                            if($Size_New>$Size_Old)
10923                            { # increased size
10924                                $ProblemType = "Size_Of_Allocable_Class_Increased";
10925                            }
10926                            else
10927                            { # decreased size
10928                                $ProblemType = "Size_Of_Allocable_Class_Decreased";
10929                                if(not havePubFields(\%Class_Old))
10930                                { # affected class has no public members
10931                                    next;
10932                                }
10933                            }
10934                        }
10935                        next if(not $ProblemType);
10936                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10937                        { # base class size changes affecting current class
10938                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10939                                next;
10940                            }
10941                            %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=(
10942                                "Type_Name"=>$BaseType{"Name"},
10943                                "Target"=>$BaseType{"Name"},
10944                                "Old_Size"=>$Size_Old*$BYTE_SIZE,
10945                                "New_Size"=>$Size_New*$BYTE_SIZE  );
10946                        }
10947                    }
10948                }
10949            }
10950            if(defined $VirtualTable_Model{1}{$ClassName}
10951            and cmpVTables_Real($ClassName, 1)==1
10952            and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}}))
10953            { # compare virtual tables size in base classes
10954                my $VShift_Old = getVShift($ClassId_Old, 1);
10955                my $VShift_New = getVShift($ClassId_New, 2);
10956                if($VShift_Old ne $VShift_New)
10957                { # changes in the base class or changes in the list of base classes
10958                    my @AllBases_Old = get_base_classes($ClassId_Old, 1, 1);
10959                    my @AllBases_New = get_base_classes($ClassId_New, 2, 1);
10960                    ($BNum1, $BNum2) = (1, 1);
10961                    my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New;
10962                    foreach my $BaseId (@AllBases_Old)
10963                    {
10964                        my %BaseType = get_Type($BaseId, 1);
10965                        if(not $StableBase{$Tr_Old{$BaseType{"Name"}}})
10966                        { # lost base
10967                            next;
10968                        }
10969                        my $VSize_Old = getVTable_Size($BaseType{"Name"}, 1);
10970                        my $VSize_New = getVTable_Size($BaseType{"Name"}, 2);
10971                        if($VSize_Old!=$VSize_New)
10972                        {
10973                            foreach my $Symbol (@VFunctions)
10974                            { # TODO: affected non-virtual methods?
10975                                if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol})
10976                                { # Removed_Virtual_Method, will be registered in mergeVirtualTables()
10977                                    next;
10978                                }
10979                                if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0)
10980                                { # skip interfaces that have not changed the absolute virtual position
10981                                    next;
10982                                }
10983                                if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
10984                                    next;
10985                                }
10986                                $VTableChanged_M{$BaseType{"Name"}} = 1;
10987                                $VTableChanged_M{$ClassName} = 1;
10988                                foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}}))
10989                                { # the reason of the layout change: added virtual functions
10990                                    next if($VirtualReplacement{$VirtFunc});
10991                                    my $ProblemType = "Added_Virtual_Method";
10992                                    if($CompleteSignature{2}{$VirtFunc}{"PureVirt"}) {
10993                                        $ProblemType = "Added_Pure_Virtual_Method";
10994                                    }
10995                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 2)}}=(
10996                                        "Type_Name"=>$BaseType{"Name"},
10997                                        "Target"=>get_Signature($VirtFunc, 2)  );
10998                                }
10999                                foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}}))
11000                                { # the reason of the layout change: removed virtual functions
11001                                    next if($VirtualReplacement{$VirtFunc});
11002                                    my $ProblemType = "Removed_Virtual_Method";
11003                                    if($CompleteSignature{1}{$VirtFunc}{"PureVirt"}) {
11004                                        $ProblemType = "Removed_Pure_Virtual_Method";
11005                                    }
11006                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 1)}}=(
11007                                        "Type_Name"=>$BaseType{"Name"},
11008                                        "Target"=>get_Signature($VirtFunc, 1)  );
11009                                }
11010                            }
11011                        }
11012                    }
11013                }
11014            }
11015        }
11016    }
11017}
11018
11019sub isCreatable($$)
11020{
11021    my ($ClassId, $LibVersion) = @_;
11022    if($AllocableClass{$LibVersion}{$TypeInfo{$LibVersion}{$ClassId}{"Name"}}
11023    or isCopyingClass($ClassId, $LibVersion)) {
11024        return 1;
11025    }
11026    if(keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
11027    { # Fix for incomplete data: if this class has
11028      # a base class then it should also has a constructor
11029        return 1;
11030    }
11031    if($ReturnedClass{$LibVersion}{$ClassId})
11032    { # returned by some method of this class
11033      # or any other class
11034        return 1;
11035    }
11036    return 0;
11037}
11038
11039sub isUsedClass($$$)
11040{
11041    my ($ClassId, $LibVersion, $Level) = @_;
11042    if(keys(%{$ParamClass{$LibVersion}{$ClassId}}))
11043    { # parameter of some exported method
11044        return 1;
11045    }
11046    my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
11047    if(keys(%{$ClassMethods{$Level}{$LibVersion}{$CName}}))
11048    { # method from target class
11049        return 1;
11050    }
11051    return 0;
11052}
11053
11054sub mergeVirtualTables($$)
11055{ # check for changes in the virtual table
11056    my ($Interface, $Level) = @_;
11057    # affected methods:
11058    #  - virtual
11059    #  - pure-virtual
11060    #  - non-virtual
11061    if($CompleteSignature{1}{$Interface}{"Data"})
11062    { # global data is not affected
11063        return;
11064    }
11065    my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"};
11066    if(not $Class_Id) {
11067        return;
11068    }
11069    my $CName = $TypeInfo{1}{$Class_Id}{"Name"};
11070    if(cmpVTables_Real($CName, 1)==0)
11071    { # no changes
11072        return;
11073    }
11074    $CheckedTypes{$Level}{$CName} = 1;
11075    if($Level eq "Binary")
11076    { # Binary-level
11077        if($CompleteSignature{1}{$Interface}{"PureVirt"}
11078        and not isUsedClass($Class_Id, 1, $Level))
11079        { # pure virtuals should not be affected
11080          # if there are no exported methods using this class
11081            return;
11082        }
11083    }
11084    foreach my $Func (keys(%{$VirtualTable{1}{$CName}}))
11085    {
11086        if(defined $VirtualTable{2}{$CName}{$Func}
11087        and defined $CompleteSignature{2}{$Func})
11088        {
11089            if(not $CompleteSignature{1}{$Func}{"PureVirt"}
11090            and $CompleteSignature{2}{$Func}{"PureVirt"})
11091            { # became pure virtual
11092                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$tr_name{$Func}}}=(
11093                    "Type_Name"=>$CName,
11094                    "Target"=>get_Signature_M($Func, 1)  );
11095                $VTableChanged_M{$CName} = 1;
11096            }
11097            elsif($CompleteSignature{1}{$Func}{"PureVirt"}
11098            and not $CompleteSignature{2}{$Func}{"PureVirt"})
11099            { # became non-pure virtual
11100                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$tr_name{$Func}}}=(
11101                    "Type_Name"=>$CName,
11102                    "Target"=>get_Signature_M($Func, 1)  );
11103                $VTableChanged_M{$CName} = 1;
11104            }
11105        }
11106    }
11107    if($Level eq "Binary")
11108    { # Binary-level
11109        # check virtual table structure
11110        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11111        {
11112            next if($Interface eq $AddedVFunc);
11113            next if($VirtualReplacement{$AddedVFunc});
11114            my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc};
11115            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11116            { # pure virtual methods affect all others (virtual and non-virtual)
11117                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11118                    "Type_Name"=>$CName,
11119                    "Target"=>get_Signature($AddedVFunc, 2)  );
11120                $VTableChanged_M{$CName} = 1;
11121            }
11122            elsif(not defined $VirtualTable{1}{$CName}
11123            or $VPos_Added>keys(%{$VirtualTable{1}{$CName}}))
11124            { # added virtual function at the end of v-table
11125                if(not keys(%{$VirtualTable_Model{1}{$CName}}))
11126                { # became polymorphous class, added v-table pointer
11127                    %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11128                        "Type_Name"=>$CName,
11129                        "Target"=>get_Signature($AddedVFunc, 2)  );
11130                    $VTableChanged_M{$CName} = 1;
11131                }
11132                else
11133                {
11134                    my $VSize_Old = getVTable_Size($CName, 1);
11135                    my $VSize_New = getVTable_Size($CName, 2);
11136                    next if($VSize_Old==$VSize_New); # exception: register as removed and added virtual method
11137                    if(isCopyingClass($Class_Id, 1))
11138                    { # class has no constructors and v-table will be copied by applications, this may affect all methods
11139                        my $ProblemType = "Added_Virtual_Method";
11140                        if(isLeafClass($Class_Id, 1)) {
11141                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class";
11142                        }
11143                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11144                            "Type_Name"=>$CName,
11145                            "Target"=>get_Signature($AddedVFunc, 2)  );
11146                        $VTableChanged_M{$CName} = 1;
11147                    }
11148                    else
11149                    {
11150                        my $ProblemType = "Added_Virtual_Method";
11151                        if(isLeafClass($Class_Id, 1)) {
11152                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class";
11153                        }
11154                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11155                            "Type_Name"=>$CName,
11156                            "Target"=>get_Signature($AddedVFunc, 2)  );
11157                        $VTableChanged_M{$CName} = 1;
11158                    }
11159                }
11160            }
11161            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11162            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11163            {
11164                if(defined $VirtualTable{1}{$CName}
11165                and defined $VirtualTable{2}{$CName})
11166                {
11167                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11168                    my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11169
11170                    if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New)
11171                    {
11172                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11173                        foreach my $ASymbol (@Affected)
11174                        {
11175                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11176                            {
11177                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11178                                    next;
11179                                }
11180                            }
11181                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11182                            %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11183                                "Type_Name"=>$CName,
11184                                "Target"=>get_Signature($AddedVFunc, 2)  );
11185                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11186                        }
11187                    }
11188                }
11189            }
11190            else {
11191                # safe
11192            }
11193        }
11194        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11195        {
11196            next if($VirtualReplacement{$RemovedVFunc});
11197            if($RemovedVFunc eq $Interface
11198            and $CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11199            { # This case is for removed virtual methods
11200              # implemented in both versions of a library
11201                next;
11202            }
11203            if(not keys(%{$VirtualTable_Model{2}{$CName}}))
11204            { # became non-polymorphous class, removed v-table pointer
11205                %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11206                    "Type_Name"=>$CName,
11207                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11208                $VTableChanged_M{$CName} = 1;
11209            }
11210            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11211            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11212            {
11213                if(defined $VirtualTable{1}{$CName} and defined $VirtualTable{2}{$CName})
11214                {
11215                    if(not defined $VirtualTable{1}{$CName}{$Interface}) {
11216                        next;
11217                    }
11218                    my $VPos_New = -1;
11219                    if(defined $VirtualTable{2}{$CName}{$Interface})
11220                    {
11221                        $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11222                    }
11223                    else
11224                    {
11225                        if($Interface ne $RemovedVFunc) {
11226                            next;
11227                        }
11228                    }
11229                    my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc};
11230                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11231                    if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New)
11232                    {
11233                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11234                        foreach my $ASymbol (@Affected)
11235                        {
11236                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11237                            {
11238                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11239                                    next;
11240                                }
11241                            }
11242                            my $ProblemType = "Removed_Virtual_Method";
11243                            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) {
11244                                $ProblemType = "Removed_Pure_Virtual_Method";
11245                            }
11246                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11247                            %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$RemovedVFunc}}}=(
11248                                "Type_Name"=>$CName,
11249                                "Target"=>get_Signature($RemovedVFunc, 1)  );
11250                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11251                        }
11252                    }
11253                }
11254            }
11255        }
11256    }
11257    else
11258    { # Source-level
11259        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11260        {
11261            next if($Interface eq $AddedVFunc);
11262            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11263            {
11264                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11265                    "Type_Name"=>$CName,
11266                    "Target"=>get_Signature($AddedVFunc, 2)  );
11267            }
11268        }
11269        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11270        {
11271            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11272            {
11273                %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11274                    "Type_Name"=>$CName,
11275                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11276            }
11277        }
11278    }
11279}
11280
11281sub find_MemberPair_Pos_byName($$)
11282{
11283    my ($Member_Name, $Pair_Type) = @_;
11284    $Member_Name=~s/\A[_]+|[_]+\Z//g;
11285    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11286    {
11287        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos})
11288        {
11289            my $Name = $Pair_Type->{"Memb"}{$MemberPair_Pos}{"name"};
11290            $Name=~s/\A[_]+|[_]+\Z//g;
11291            if($Name eq $Member_Name) {
11292                return $MemberPair_Pos;
11293            }
11294        }
11295    }
11296    return "lost";
11297}
11298
11299sub find_MemberPair_Pos_byVal($$)
11300{
11301    my ($Member_Value, $Pair_Type) = @_;
11302    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11303    {
11304        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}
11305        and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) {
11306            return $MemberPair_Pos;
11307        }
11308    }
11309    return "lost";
11310}
11311
11312sub isRecurType($$$)
11313{
11314    foreach (@{$_[2]})
11315    {
11316        if( $_->{"T1"} eq $_[0]
11317        and $_->{"T2"} eq $_[1] )
11318        {
11319            return 1;
11320        }
11321    }
11322    return 0;
11323}
11324
11325sub pushType($$$)
11326{
11327    my %IDs = (
11328        "T1" => $_[0],
11329        "T2" => $_[1]
11330    );
11331    push(@{$_[2]}, \%IDs);
11332}
11333
11334sub isRenamed($$$$$)
11335{
11336    my ($MemPos, $Type1, $LVersion1, $Type2, $LVersion2) = @_;
11337    my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"};
11338    my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"};
11339    my %MemberType_Pure = get_PureType($MemberType_Id, $TypeInfo{$LVersion1});
11340    if(not defined $Type2->{"Memb"}{$MemPos}) {
11341        return "";
11342    }
11343    my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"};
11344    my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{$LVersion2});
11345
11346    my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"};
11347    my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:find_MemberPair_Pos_byName($Pair_Name, $Type1);
11348    if($MemberPair_Pos_Rev eq "lost")
11349    {
11350        if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"})
11351        { # base type match
11352            return $Pair_Name;
11353        }
11354        if($TypeInfo{$LVersion1}{$MemberType_Id}{"Name"} eq $TypeInfo{$LVersion2}{$PairType_Id}{"Name"})
11355        { # exact type match
11356            return $Pair_Name;
11357        }
11358        if($MemberType_Pure{"Size"} eq $PairType_Pure{"Size"})
11359        { # size match
11360            return $Pair_Name;
11361        }
11362        if(isReserved($Pair_Name))
11363        { # reserved fields
11364            return $Pair_Name;
11365        }
11366    }
11367    return "";
11368}
11369
11370sub isLastElem($$)
11371{
11372    my ($Pos, $TypeRef) = @_;
11373    my $Name = $TypeRef->{"Memb"}{$Pos}{"name"};
11374    if($Name=~/last|count|max|total/i)
11375    { # GST_LEVEL_COUNT, GST_RTSP_ELAST
11376        return 1;
11377    }
11378    elsif($Name=~/END|NLIMITS\Z/)
11379    { # __RLIMIT_NLIMITS
11380        return 1;
11381    }
11382    elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/
11383    and $Pos+1==keys(%{$TypeRef->{"Memb"}}))
11384    { # NImageFormats, NColorRoles
11385        return 1;
11386    }
11387    return 0;
11388}
11389
11390sub nonComparable($$)
11391{
11392    my ($T1, $T2) = @_;
11393
11394    my $N1 = $T1->{"Name"};
11395    my $N2 = $T2->{"Name"};
11396
11397    $N1=~s/\A(struct|union|enum) //;
11398    $N2=~s/\A(struct|union|enum) //;
11399
11400    if($N1 ne $N2
11401    and not isAnon($N1)
11402    and not isAnon($N2))
11403    { # different names
11404        if($T1->{"Type"} ne "Pointer"
11405        or $T2->{"Type"} ne "Pointer")
11406        { # compare base types
11407            return 1;
11408        }
11409        if($N1!~/\Avoid\s*\*/
11410        and $N2=~/\Avoid\s*\*/)
11411        {
11412            return 1;
11413        }
11414    }
11415    elsif($T1->{"Type"} ne $T2->{"Type"})
11416    { # different types
11417        if($T1->{"Type"} eq "Class"
11418        and $T2->{"Type"} eq "Struct")
11419        { # "class" to "struct"
11420            return 0;
11421        }
11422        elsif($T2->{"Type"} eq "Class"
11423        and $T1->{"Type"} eq "Struct")
11424        { # "struct" to "class"
11425            return 0;
11426        }
11427        else
11428        { # "class" to "enum"
11429          # "union" to "class"
11430          #  ...
11431            return 1;
11432        }
11433    }
11434    return 0;
11435}
11436
11437sub isOpaque($)
11438{
11439    my $T = $_[0];
11440    if(not defined $T->{"Memb"})
11441    {
11442        return 1;
11443    }
11444    return 0;
11445}
11446
11447sub removeVPtr($)
11448{ # support for old ABI dumps
11449    my $TPtr = $_[0];
11450    my @Pos = sort {int($a)<=>int($b)} keys(%{$TPtr->{"Memb"}});
11451    if($#Pos>=1)
11452    {
11453        foreach my $Pos (0 .. $#Pos-1)
11454        {
11455            %{$TPtr->{"Memb"}{$Pos}} = %{$TPtr->{"Memb"}{$Pos+1}};
11456        }
11457        delete($TPtr->{"Memb"}{$#Pos});
11458    }
11459}
11460
11461sub isPrivateABI($$)
11462{
11463    my ($TypeId, $LibVersion) = @_;
11464
11465    if($CheckPrivateABI) {
11466        return 0;
11467    }
11468
11469    if(defined $TypeInfo{$LibVersion}{$TypeId}{"PrivateABI"}) {
11470        return 1;
11471    }
11472
11473    return 0;
11474}
11475
11476sub mergeTypes($$$)
11477{
11478    my ($Type1_Id, $Type2_Id, $Level) = @_;
11479    return {} if(not $Type1_Id or not $Type2_Id);
11480
11481    if(defined $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id})
11482    { # already merged
11483        return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id};
11484    }
11485
11486    my %Type1 = get_Type($Type1_Id, 1);
11487    my %Type2 = get_Type($Type2_Id, 2);
11488    if(not $Type1{"Name"} or not $Type2{"Name"}) {
11489        return {};
11490    }
11491
11492    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
11493    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
11494
11495    if(defined $UsedDump{1}{"DWARF"})
11496    {
11497        if($Type1_Pure{"Name"} eq "__unknown__"
11498        or $Type2_Pure{"Name"} eq "__unknown__")
11499        { # Error ABI dump
11500            return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
11501        }
11502    }
11503
11504    if(isPrivateABI($Type1_Id, 1)) {
11505        return {};
11506    }
11507
11508    $CheckedTypes{$Level}{$Type1{"Name"}} = 1;
11509    $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1;
11510
11511    my %SubProblems = ();
11512
11513    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
11514    {
11515        if($Type1_Pure{"Type"}=~/Struct|Union/
11516        and $Type2_Pure{"Type"}=~/Struct|Union/)
11517        {
11518            if(isOpaque(\%Type2_Pure) and not isOpaque(\%Type1_Pure))
11519            {
11520                if(not defined $UsedDump{1}{"DWARF"}
11521                and not defined $UsedDump{2}{"DWARF"})
11522                {
11523                    %{$SubProblems{"Type_Became_Opaque"}{$Type1_Pure{"Name"}}}=(
11524                        "Target"=>$Type1_Pure{"Name"},
11525                        "Type_Name"=>$Type1_Pure{"Name"}  );
11526                }
11527
11528                return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11529            }
11530        }
11531    }
11532
11533    if(not $Type1_Pure{"Size"}
11534    or not $Type2_Pure{"Size"})
11535    { # including a case when "class Class { ... };" changed to "class Class;"
11536        if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"}
11537        or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1)
11538        { # NOTE: template instances have no size
11539            return {};
11540        }
11541    }
11542    if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes))
11543    { # skip recursive declarations
11544        return {};
11545    }
11546    return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"});
11547    return {} if($SkipTypes{1}{$Type1_Pure{"Name"}});
11548    return {} if($SkipTypes{1}{$Type1{"Name"}});
11549
11550    if(not isTargetType($Type1_Pure{"Tid"}, 1)) {
11551        return {};
11552    }
11553
11554    if($Type1_Pure{"Type"}=~/Class|Struct/ and $Type2_Pure{"Type"}=~/Class|Struct/)
11555    { # support for old ABI dumps
11556      # _vptr field added in 3.0
11557        if(not checkDump(1, "3.0") and checkDump(2, "3.0"))
11558        {
11559            if(defined $Type2_Pure{"Memb"}
11560            and $Type2_Pure{"Memb"}{0}{"name"} eq "_vptr")
11561            {
11562                if(keys(%{$Type2_Pure{"Memb"}})==1) {
11563                    delete($Type2_Pure{"Memb"}{0});
11564                }
11565                else {
11566                    removeVPtr(\%Type2_Pure);
11567                }
11568            }
11569        }
11570        if(checkDump(1, "3.0") and not checkDump(2, "3.0"))
11571        {
11572            if(defined $Type1_Pure{"Memb"}
11573            and $Type1_Pure{"Memb"}{0}{"name"} eq "_vptr")
11574            {
11575                if(keys(%{$Type1_Pure{"Memb"}})==1) {
11576                    delete($Type1_Pure{"Memb"}{0});
11577                }
11578                else {
11579                    removeVPtr(\%Type1_Pure);
11580                }
11581            }
11582        }
11583    }
11584
11585    my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
11586    my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
11587
11588    if(%Typedef_1 and %Typedef_2
11589    and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef"
11590    and $Typedef_1{"Name"} eq $Typedef_2{"Name"})
11591    {
11592        my %Base_1 = get_OneStep_BaseType($Typedef_1{"Tid"}, $TypeInfo{1});
11593        my %Base_2 = get_OneStep_BaseType($Typedef_2{"Tid"}, $TypeInfo{2});
11594        if($Base_1{"Name"} ne $Base_2{"Name"})
11595        {
11596            if(differentDumps("G")
11597            or differentDumps("V"))
11598            { # different GCC versions or different dumps
11599                $Base_1{"Name"} = uncover_typedefs($Base_1{"Name"}, 1);
11600                $Base_2{"Name"} = uncover_typedefs($Base_2{"Name"}, 2);
11601                # std::__va_list and __va_list
11602                $Base_1{"Name"}=~s/\A(\w+::)+//;
11603                $Base_2{"Name"}=~s/\A(\w+::)+//;
11604                $Base_1{"Name"} = formatName($Base_1{"Name"}, "T");
11605                $Base_2{"Name"} = formatName($Base_2{"Name"}, "T");
11606            }
11607        }
11608        if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/
11609        and $Base_1{"Name"} ne $Base_2{"Name"})
11610        {
11611            if($Level eq "Binary"
11612            and $Type1{"Size"} and $Type2{"Size"}
11613            and $Type1{"Size"} ne $Type2{"Size"})
11614            {
11615                %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=(
11616                    "Target"=>$Typedef_1{"Name"},
11617                    "Type_Name"=>$Typedef_1{"Name"},
11618                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11619                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE  );
11620            }
11621            my %Base1_Pure = get_PureType($Base_1{"Tid"}, $TypeInfo{1});
11622            my %Base2_Pure = get_PureType($Base_2{"Tid"}, $TypeInfo{2});
11623
11624            if(defined $UsedDump{1}{"DWARF"})
11625            {
11626                if($Base1_Pure{"Name"}=~/\b__unknown__\b/
11627                or $Base2_Pure{"Name"}=~/\b__unknown__\b/)
11628                { # Error ABI dump
11629                    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
11630                }
11631            }
11632
11633            if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"}))
11634            {
11635                if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level))
11636                {
11637                    %{$SubProblems{"Typedef_BaseType_Format"}{$Typedef_1{"Name"}}}=(
11638                        "Target"=>$Typedef_1{"Name"},
11639                        "Type_Name"=>$Typedef_1{"Name"},
11640                        "Old_Value"=>$Base_1{"Name"},
11641                        "New_Value"=>$Base_2{"Name"}  );
11642                }
11643                else
11644                {
11645                    %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=(
11646                        "Target"=>$Typedef_1{"Name"},
11647                        "Type_Name"=>$Typedef_1{"Name"},
11648                        "Old_Value"=>$Base_1{"Name"},
11649                        "New_Value"=>$Base_2{"Name"}  );
11650                }
11651            }
11652        }
11653    }
11654    if(nonComparable(\%Type1_Pure, \%Type2_Pure))
11655    { # different types (reported in detectTypeChange(...))
11656        my $TT1 = $Type1_Pure{"Type"};
11657        my $TT2 = $Type2_Pure{"Type"};
11658
11659        if($TT1 ne $TT2
11660        and $TT1!~/Intrinsic|Pointer|Ref|Typedef/)
11661        { # different type of the type
11662            my $Short1 = $Type1_Pure{"Name"};
11663            my $Short2 = $Type2_Pure{"Name"};
11664
11665            $Short1=~s/\A\Q$TT1\E //ig;
11666            $Short2=~s/\A\Q$TT2\E //ig;
11667
11668            if($Short1 eq $Short2)
11669            {
11670                %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=(
11671                    "Target"=>$Type1_Pure{"Name"},
11672                    "Type_Name"=>$Type1_Pure{"Name"},
11673                    "Old_Value"=>lc($Type1_Pure{"Type"}),
11674                    "New_Value"=>lc($Type2_Pure{"Type"})  );
11675            }
11676        }
11677        return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11678    }
11679    pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes);
11680    if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
11681    or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"})))
11682    and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11683    { # checking size
11684        if($Level eq "Binary"
11685        and $Type1_Pure{"Size"} and $Type2_Pure{"Size"}
11686        and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11687        {
11688            my $ProblemKind = "DataType_Size";
11689            if($Type1_Pure{"Type"} eq "Class"
11690            and keys(%{$ClassMethods{$Level}{1}{$Type1_Pure{"Name"}}}))
11691            {
11692                if(isCopyingClass($Type1_Pure{"Tid"}, 1)) {
11693                    $ProblemKind = "Size_Of_Copying_Class";
11694                }
11695                elsif($AllocableClass{1}{$Type1_Pure{"Name"}})
11696                {
11697                    if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) {
11698                        $ProblemKind = "Size_Of_Allocable_Class_Increased";
11699                    }
11700                    else
11701                    {
11702                        # descreased size of allocable class
11703                        # it has no special effects
11704                    }
11705                }
11706            }
11707            %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=(
11708                "Target"=>$Type1_Pure{"Name"},
11709                "Type_Name"=>$Type1_Pure{"Name"},
11710                "Old_Size"=>$Type1_Pure{"Size"}*$BYTE_SIZE,
11711                "New_Size"=>$Type2_Pure{"Size"}*$BYTE_SIZE);
11712        }
11713    }
11714    if(defined $Type1_Pure{"BaseType"}
11715    and defined $Type2_Pure{"BaseType"})
11716    { # checking base types
11717        my $Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}, $Type2_Pure{"BaseType"}, $Level);
11718        foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
11719        {
11720            foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) {
11721                $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
11722            }
11723        }
11724    }
11725    my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = ();
11726    my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}});
11727    my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}});
11728    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11729    { # detect removed and renamed fields
11730        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11731        next if(not $Member_Name);
11732        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);
11733        if($MemberPair_Pos eq "lost")
11734        {
11735            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11736            {
11737                if(isUnnamed($Member_Name))
11738                { # support for old-version dumps
11739                  # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11740                    if(not checkDump(2, "2.1")) {
11741                        next;
11742                    }
11743                }
11744                if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2))
11745                { # renamed
11746                    $RenamedField{$Member_Pos} = $RenamedTo;
11747                    $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11748                }
11749                else
11750                { # removed
11751                    $RemovedField{$Member_Pos} = 1;
11752                }
11753            }
11754            elsif($Type1_Pure{"Type"} eq "Enum")
11755            {
11756                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
11757                next if($Member_Value1 eq "");
11758                $MemberPair_Pos = find_MemberPair_Pos_byVal($Member_Value1, \%Type2_Pure);
11759                if($MemberPair_Pos ne "lost")
11760                { # renamed
11761                    my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"};
11762                    my $MemberPair_Pos_Rev = find_MemberPair_Pos_byName($RenamedTo, \%Type1_Pure);
11763                    if($MemberPair_Pos_Rev eq "lost")
11764                    {
11765                        $RenamedField{$Member_Pos} = $RenamedTo;
11766                        $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11767                    }
11768                    else {
11769                        $RemovedField{$Member_Pos} = 1;
11770                    }
11771                }
11772                else
11773                { # removed
11774                    $RemovedField{$Member_Pos} = 1;
11775                }
11776            }
11777        }
11778        else
11779        { # related
11780            $RelatedField{$Member_Pos} = $MemberPair_Pos;
11781            $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos;
11782        }
11783    }
11784    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11785    { # detect added fields
11786        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11787        next if(not $Member_Name);
11788        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);
11789        if($MemberPair_Pos eq "lost")
11790        {
11791            if(isUnnamed($Member_Name))
11792            { # support for old-version dumps
11793            # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11794                if(not checkDump(1, "2.1")) {
11795                    next;
11796                }
11797            }
11798            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/)
11799            {
11800                if(not $RenamedField_Rev{$Member_Pos})
11801                { # added
11802                    $AddedField{$Member_Pos}=1;
11803                }
11804            }
11805        }
11806    }
11807    if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
11808    { # detect moved fields
11809        my (%RelPos, %RelPosName, %AbsPos) = ();
11810        my $Pos = 0;
11811        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11812        { # relative positions in 1st version
11813            my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11814            next if(not $Member_Name);
11815            if(not $RemovedField{$Member_Pos})
11816            { # old type without removed fields
11817                $RelPos{1}{$Member_Name} = $Pos;
11818                $RelPosName{1}{$Pos} = $Member_Name;
11819                $AbsPos{1}{$Pos++} = $Member_Pos;
11820            }
11821        }
11822        $Pos = 0;
11823        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11824        { # relative positions in 2nd version
11825            my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11826            next if(not $Member_Name);
11827            if(not $AddedField{$Member_Pos})
11828            { # new type without added fields
11829                $RelPos{2}{$Member_Name} = $Pos;
11830                $RelPosName{2}{$Pos} = $Member_Name;
11831                $AbsPos{2}{$Pos++} = $Member_Pos;
11832            }
11833        }
11834        foreach my $Member_Name (keys(%{$RelPos{1}}))
11835        {
11836            my $RPos1 = $RelPos{1}{$Member_Name};
11837            my $AbsPos1 = $NameToPosA{$Member_Name};
11838            my $Member_Name2 = $Member_Name;
11839            if(my $RenamedTo = $RenamedField{$AbsPos1})
11840            { # renamed
11841                $Member_Name2 = $RenamedTo;
11842            }
11843            my $RPos2 = $RelPos{2}{$Member_Name2};
11844            if($RPos2 ne "" and $RPos1 ne $RPos2)
11845            { # different relative positions
11846                my $AbsPos2 = $NameToPosB{$Member_Name2};
11847                if($AbsPos1 ne $AbsPos2)
11848                { # different absolute positions
11849                    my $ProblemType = "Moved_Field";
11850                    if(not isPublic(\%Type1_Pure, $AbsPos1))
11851                    { # may change layout and size of type
11852                        if($Level eq "Source") {
11853                            next;
11854                        }
11855                        $ProblemType = "Moved_Private_Field";
11856                    }
11857                    if($Level eq "Binary"
11858                    and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11859                    { # affected size
11860                        my $MemSize1 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$AbsPos1}{"type"}}{"Size"};
11861                        my $MovedAbsPos = $AbsPos{1}{$RPos2};
11862                        my $MemSize2 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}}{"Size"};
11863                        if($MemSize1 ne $MemSize2) {
11864                            $ProblemType .= "_And_Size";
11865                        }
11866                    }
11867                    if($ProblemType eq "Moved_Private_Field") {
11868                        next;
11869                    }
11870                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
11871                        "Target"=>$Member_Name,
11872                        "Type_Name"=>$Type1_Pure{"Name"},
11873                        "Old_Value"=>$RPos1,
11874                        "New_Value"=>$RPos2 );
11875                }
11876            }
11877        }
11878    }
11879    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11880    { # check older fields, public and private
11881        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11882        next if(not $Member_Name);
11883        next if($Member_Name eq "_vptr");
11884        if(my $RenamedTo = $RenamedField{$Member_Pos})
11885        { # renamed
11886            if(defined $Constants{2}{$Member_Name})
11887            {
11888                if($Constants{2}{$Member_Name}{"Value"} eq $RenamedTo)
11889                { # define OLD NEW
11890                    next; # Safe
11891                }
11892            }
11893
11894            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11895            {
11896                if(isPublic(\%Type1_Pure, $Member_Pos))
11897                {
11898                    %{$SubProblems{"Renamed_Field"}{$Member_Name}}=(
11899                        "Target"=>$Member_Name,
11900                        "Type_Name"=>$Type1_Pure{"Name"},
11901                        "Old_Value"=>$Member_Name,
11902                        "New_Value"=>$RenamedTo  );
11903                }
11904                elsif(isReserved($Member_Name))
11905                {
11906                    %{$SubProblems{"Used_Reserved_Field"}{$Member_Name}}=(
11907                        "Target"=>$Member_Name,
11908                        "Type_Name"=>$Type1_Pure{"Name"},
11909                        "Old_Value"=>$Member_Name,
11910                        "New_Value"=>$RenamedTo  );
11911                }
11912            }
11913            elsif($Type1_Pure{"Type"} eq "Enum")
11914            {
11915                %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=(
11916                    "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"},
11917                    "Type_Name"=>$Type1_Pure{"Name"},
11918                    "Old_Value"=>$Member_Name,
11919                    "New_Value"=>$RenamedTo  );
11920            }
11921        }
11922        elsif($RemovedField{$Member_Pos})
11923        { # removed
11924            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
11925            {
11926                my $ProblemType = "Removed_Field";
11927                if(not isPublic(\%Type1_Pure, $Member_Pos)
11928                or isUnnamed($Member_Name))
11929                {
11930                    if($Level eq "Source") {
11931                        next;
11932                    }
11933                    $ProblemType = "Removed_Private_Field";
11934                }
11935                if($Level eq "Binary"
11936                and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
11937                {
11938                    if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
11939                    { # affected fields
11940                        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}))
11941                        { # changed offset
11942                            $ProblemType .= "_And_Layout";
11943                        }
11944                    }
11945                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11946                    { # affected size
11947                        $ProblemType .= "_And_Size";
11948                    }
11949                }
11950                if($ProblemType eq "Removed_Private_Field") {
11951                    next;
11952                }
11953                %{$SubProblems{$ProblemType}{$Member_Name}}=(
11954                    "Target"=>$Member_Name,
11955                    "Type_Name"=>$Type1_Pure{"Name"}  );
11956            }
11957            elsif($Type2_Pure{"Type"} eq "Union")
11958            {
11959                if($Level eq "Binary"
11960                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11961                {
11962                    %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=(
11963                        "Target"=>$Member_Name,
11964                        "Type_Name"=>$Type1_Pure{"Name"}  );
11965                }
11966                else
11967                {
11968                    %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=(
11969                        "Target"=>$Member_Name,
11970                        "Type_Name"=>$Type1_Pure{"Name"}  );
11971                }
11972            }
11973            elsif($Type1_Pure{"Type"} eq "Enum")
11974            {
11975                %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=(
11976                    "Target"=>$Member_Name,
11977                    "Type_Name"=>$Type1_Pure{"Name"},
11978                    "Old_Value"=>$Member_Name  );
11979            }
11980        }
11981        else
11982        { # changed
11983            my $MemberPair_Pos = $RelatedField{$Member_Pos};
11984            if($Type1_Pure{"Type"} eq "Enum")
11985            {
11986                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
11987                next if($Member_Value1 eq "");
11988                my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"};
11989                next if($Member_Value2 eq "");
11990                if($Member_Value1 ne $Member_Value2)
11991                {
11992                    my $ProblemType = "Enum_Member_Value";
11993                    if(isLastElem($Member_Pos, \%Type1_Pure)) {
11994                        $ProblemType = "Enum_Last_Member_Value";
11995                    }
11996                    if($SkipConstants{1}{$Member_Name}) {
11997                        $ProblemType = "Enum_Private_Member_Value";
11998                    }
11999                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
12000                        "Target"=>$Member_Name,
12001                        "Type_Name"=>$Type1_Pure{"Name"},
12002                        "Old_Value"=>$Member_Value1,
12003                        "New_Value"=>$Member_Value2  );
12004                }
12005            }
12006            elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
12007            {
12008                my $Access1 = $Type1_Pure{"Memb"}{$Member_Pos}{"access"};
12009                my $Access2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"access"};
12010
12011                if($Access1 ne "private"
12012                and $Access2 eq "private")
12013                {
12014                    %{$SubProblems{"Field_Became_Private"}{$Member_Name}}=(
12015                        "Target"=>$Member_Name,
12016                        "Type_Name"=>$Type1_Pure{"Name"});
12017                }
12018                elsif($Access1 ne "protected"
12019                and $Access1 ne "private"
12020                and $Access2 eq "protected")
12021                {
12022                    %{$SubProblems{"Field_Became_Protected"}{$Member_Name}}=(
12023                        "Target"=>$Member_Name,
12024                        "Type_Name"=>$Type1_Pure{"Name"});
12025                }
12026
12027                my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"};
12028                my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"};
12029                my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE_SIZE;
12030                if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) {
12031                    $SizeV1 = $BSize1;
12032                }
12033                my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE;
12034                if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) {
12035                    $SizeV2 = $BSize2;
12036                }
12037                my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"};
12038                my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"};
12039                if($Level eq "Binary"
12040                and $SizeV1 and $SizeV2
12041                and $SizeV1 ne $SizeV2)
12042                {
12043                    if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name))
12044                    or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}))
12045                    { # field size change (including anon-structures and unions)
12046                      # - same types
12047                      # - unnamed types
12048                      # - bitfields
12049                        my $ProblemType = "Field_Size";
12050                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12051                        or isUnnamed($Member_Name))
12052                        { # should not be accessed by applications, goes to "Low Severity"
12053                          # example: "abidata" members in GStreamer types
12054                            $ProblemType = "Private_".$ProblemType;
12055                        }
12056                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12057                        { # check an effect
12058                            if($Type2_Pure{"Type"} ne "Union"
12059                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12060                            { # public fields after the current
12061                                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}))
12062                                { # changed offset
12063                                    $ProblemType .= "_And_Layout";
12064                                }
12065                            }
12066                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12067                                $ProblemType .= "_And_Type_Size";
12068                            }
12069                        }
12070                        if($ProblemType eq "Private_Field_Size")
12071                        { # private field size with no effect
12072                        }
12073                        if($ProblemType eq "Field_Size")
12074                        {
12075                            if($Type1_Pure{"Type"}=~/Union|Struct/ and $SizeV1<$SizeV2)
12076                            { # Low severity
12077                                $ProblemType = "Struct_Field_Size_Increased";
12078                            }
12079                        }
12080                        if($ProblemType)
12081                        { # register a problem
12082                            %{$SubProblems{$ProblemType}{$Member_Name}}=(
12083                                "Target"=>$Member_Name,
12084                                "Type_Name"=>$Type1_Pure{"Name"},
12085                                "Old_Size"=>$SizeV1,
12086                                "New_Size"=>$SizeV2);
12087                        }
12088                    }
12089                }
12090                if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}
12091                or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})
12092                { # do NOT check bitfield type changes
12093                    next;
12094                }
12095                if(checkDump(1, "2.13") and checkDump(2, "2.13"))
12096                {
12097                    if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12098                    and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12099                    {
12100                        %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=(
12101                            "Target"=>$Member_Name,
12102                            "Type_Name"=>$Type1_Pure{"Name"});
12103                    }
12104                    elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12105                    and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12106                    {
12107                        %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=(
12108                            "Target"=>$Member_Name,
12109                            "Type_Name"=>$Type1_Pure{"Name"});
12110                    }
12111                }
12112                my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level);
12113                foreach my $ProblemType (keys(%Sub_SubChanges))
12114                {
12115                    my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12116                    my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12117
12118                    # quals
12119                    if($ProblemType eq "Field_Type"
12120                    or $ProblemType eq "Field_Type_And_Size"
12121                    or $ProblemType eq "Field_Type_Format")
12122                    {
12123                        if(checkDump(1, "2.6") and checkDump(2, "2.6"))
12124                        {
12125                            if(addedQual($Old_Value, $New_Value, "volatile")) {
12126                                %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12127                            }
12128                            elsif(removedQual($Old_Value, $New_Value, "volatile")) {
12129                                %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12130                            }
12131                        }
12132                        if(my $RA = addedQual($Old_Value, $New_Value, "const"))
12133                        {
12134                            if($RA==2) {
12135                                %{$Sub_SubChanges{"Field_Added_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12136                            }
12137                            else {
12138                                %{$Sub_SubChanges{"Field_Became_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12139                            }
12140                        }
12141                        elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
12142                        {
12143                            if($RR==2) {
12144                                %{$Sub_SubChanges{"Field_Removed_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12145                            }
12146                            else {
12147                                %{$Sub_SubChanges{"Field_Became_Non_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12148                            }
12149                        }
12150                    }
12151                }
12152
12153                if($Level eq "Source")
12154                {
12155                    foreach my $ProblemType (keys(%Sub_SubChanges))
12156                    {
12157                        my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12158                        my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12159
12160                        if($ProblemType eq "Field_Type")
12161                        {
12162                            if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
12163                                delete($Sub_SubChanges{$ProblemType});
12164                            }
12165                        }
12166                    }
12167                }
12168
12169                foreach my $ProblemType (keys(%Sub_SubChanges))
12170                {
12171                    my $ProblemType_Init = $ProblemType;
12172                    if($ProblemType eq "Field_Type_And_Size")
12173                    { # Binary
12174                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12175                        or isUnnamed($Member_Name)) {
12176                            $ProblemType = "Private_".$ProblemType;
12177                        }
12178                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12179                        { # check an effect
12180                            if($Type2_Pure{"Type"} ne "Union"
12181                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12182                            { # public fields after the current
12183                                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}))
12184                                { # changed offset
12185                                    $ProblemType .= "_And_Layout";
12186                                }
12187                            }
12188                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12189                                $ProblemType .= "_And_Type_Size";
12190                            }
12191                        }
12192                    }
12193                    else
12194                    {
12195                        # TODO: Private_Field_Type rule?
12196
12197                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12198                        or isUnnamed($Member_Name)) {
12199                            next;
12200                        }
12201                    }
12202                    if($ProblemType eq "Private_Field_Type_And_Size")
12203                    { # private field change with no effect
12204                    }
12205                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
12206                        "Target"=>$Member_Name,
12207                        "Type_Name"=>$Type1_Pure{"Name"});
12208
12209                    foreach my $Attr (keys(%{$Sub_SubChanges{$ProblemType_Init}}))
12210                    { # other properties
12211                        $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubChanges{$ProblemType_Init}{$Attr};
12212                    }
12213                }
12214                if(not isPublic(\%Type1_Pure, $Member_Pos))
12215                { # do NOT check internal type changes
12216                    next;
12217                }
12218                if($MemberType1_Id and $MemberType2_Id)
12219                { # checking member type changes
12220                    my $Sub_SubProblems = mergeTypes($MemberType1_Id, $MemberType2_Id, $Level);
12221
12222                    my %DupProblems = ();
12223
12224                    foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
12225                    {
12226                        foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
12227                        {
12228                            if(not defined $AllAffected)
12229                            {
12230                                if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
12231                                    next;
12232                                }
12233                            }
12234
12235                            my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name;
12236                            $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
12237
12238                            if(not defined $AllAffected)
12239                            {
12240                                $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
12241                            }
12242                        }
12243                    }
12244
12245                    %DupProblems = ();
12246                }
12247            }
12248        }
12249    }
12250    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
12251    { # checking added members, public and private
12252        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
12253        next if(not $Member_Name);
12254        next if($Member_Name eq "_vptr");
12255        if($AddedField{$Member_Pos})
12256        { # added
12257            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
12258            {
12259                my $ProblemType = "Added_Field";
12260                if(not isPublic(\%Type2_Pure, $Member_Pos)
12261                or isUnnamed($Member_Name))
12262                {
12263                    if($Level eq "Source") {
12264                        next;
12265                    }
12266                    $ProblemType = "Added_Private_Field";
12267                }
12268                if($Level eq "Binary"
12269                and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12270                {
12271                    if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1))
12272                    { # public fields after the current
12273                        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}))
12274                        { # changed offset
12275                            $ProblemType .= "_And_Layout";
12276                        }
12277                    }
12278                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12279                        $ProblemType .= "_And_Size";
12280                    }
12281                }
12282                if($ProblemType eq "Added_Private_Field")
12283                { # skip added private fields
12284                    next;
12285                }
12286                %{$SubProblems{$ProblemType}{$Member_Name}}=(
12287                    "Target"=>$Member_Name,
12288                    "Type_Name"=>$Type1_Pure{"Name"});
12289            }
12290            elsif($Type2_Pure{"Type"} eq "Union")
12291            {
12292                if($Level eq "Binary"
12293                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
12294                {
12295                    %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=(
12296                        "Target"=>$Member_Name,
12297                        "Type_Name"=>$Type1_Pure{"Name"});
12298                }
12299                else
12300                {
12301                    %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=(
12302                        "Target"=>$Member_Name,
12303                        "Type_Name"=>$Type1_Pure{"Name"});
12304                }
12305            }
12306            elsif($Type2_Pure{"Type"} eq "Enum")
12307            {
12308                my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"};
12309                next if($Member_Value eq "");
12310                %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=(
12311                    "Target"=>$Member_Name,
12312                    "Type_Name"=>$Type2_Pure{"Name"},
12313                    "New_Value"=>$Member_Value);
12314            }
12315        }
12316    }
12317
12318    if($Type1_Pure{"Type"} eq "FuncPtr")
12319    {
12320        foreach my $PPos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Param"}}))
12321        {
12322            if(not defined $Type2_Pure{"Param"}{$PPos}) {
12323                next;
12324            }
12325
12326            my $PT1 = $Type1_Pure{"Param"}{$PPos}{"type"};
12327            my $PT2 = $Type2_Pure{"Param"}{$PPos}{"type"};
12328
12329            my $PName = "p".$PPos;
12330
12331            my $FP_SubProblems = mergeTypes($PT1, $PT2, $Level);
12332            my %DupProblems = ();
12333
12334            foreach my $FP_SubProblemType (keys(%{$FP_SubProblems}))
12335            {
12336                foreach my $FP_SubLocation (keys(%{$FP_SubProblems->{$FP_SubProblemType}}))
12337                {
12338                    if(not defined $AllAffected)
12339                    {
12340                        if(defined $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}}) {
12341                            next;
12342                        }
12343                    }
12344
12345                    my $NewLocation = ($FP_SubLocation)?$PName."->".$FP_SubLocation:$PName;
12346                    $SubProblems{$FP_SubProblemType}{$NewLocation} = $FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation};
12347
12348                    if(not defined $AllAffected)
12349                    {
12350                        $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}} = 1;
12351                    }
12352                }
12353            }
12354
12355            %DupProblems = ();
12356        }
12357    }
12358
12359    pop(@RecurTypes);
12360    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
12361}
12362
12363sub isUnnamed($) {
12364    return $_[0]=~/\Aunnamed\d+\Z/;
12365}
12366
12367sub get_ShortClass($$)
12368{
12369    my ($TypeId, $LibVersion) = @_;
12370    my $TypeName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
12371    if($TypeInfo{$LibVersion}{$TypeId}{"Type"}!~/Intrinsic|Class|Struct|Union|Enum/) {
12372        $TypeName = uncover_typedefs($TypeName, $LibVersion);
12373    }
12374    if(my $NameSpace = $TypeInfo{$LibVersion}{$TypeId}{"NameSpace"}) {
12375        $TypeName=~s/\A(struct |)\Q$NameSpace\E\:\://g;
12376    }
12377    return $TypeName;
12378}
12379
12380sub goToFirst($$$)
12381{
12382    my ($TypeId, $LibVersion, $Type_Type) = @_;
12383    return () if(not $TypeId);
12384    if(defined $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}) {
12385        return %{$Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}};
12386    }
12387    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12388    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12389    return () if(not $Type{"Type"});
12390    if($Type{"Type"} ne $Type_Type)
12391    {
12392        return () if(not $Type{"BaseType"});
12393        %Type = goToFirst($Type{"BaseType"}, $LibVersion, $Type_Type);
12394    }
12395    $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type} = \%Type;
12396    return %Type;
12397}
12398
12399my %TypeSpecAttributes = (
12400    "Const" => 1,
12401    "Volatile" => 1,
12402    "ConstVolatile" => 1,
12403    "Restrict" => 1,
12404    "Typedef" => 1
12405);
12406
12407sub get_PureType($$)
12408{
12409    my ($TypeId, $Info) = @_;
12410    if(not $TypeId or not $Info
12411    or not $Info->{$TypeId}) {
12412        return ();
12413    }
12414    if(defined $Cache{"get_PureType"}{$TypeId}{$Info}) {
12415        return %{$Cache{"get_PureType"}{$TypeId}{$Info}};
12416    }
12417    my %Type = %{$Info->{$TypeId}};
12418    return %Type if(not $Type{"BaseType"});
12419    if($TypeSpecAttributes{$Type{"Type"}}) {
12420        %Type = get_PureType($Type{"BaseType"}, $Info);
12421    }
12422    $Cache{"get_PureType"}{$TypeId}{$Info} = \%Type;
12423    return %Type;
12424}
12425
12426sub get_PLevel($$)
12427{
12428    my ($TypeId, $LibVersion) = @_;
12429    return 0 if(not $TypeId);
12430    if(defined $Cache{"get_PLevel"}{$TypeId}{$LibVersion}) {
12431        return $Cache{"get_PLevel"}{$TypeId}{$LibVersion};
12432    }
12433    return 0 if(not $TypeInfo{$LibVersion}{$TypeId});
12434    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12435    return 1 if($Type{"Type"}=~/FuncPtr|FieldPtr/);
12436    my $PLevel = 0;
12437    if($Type{"Type"} =~/Pointer|Ref|FuncPtr|FieldPtr/) {
12438        $PLevel += 1;
12439    }
12440    return $PLevel if(not $Type{"BaseType"});
12441    $PLevel += get_PLevel($Type{"BaseType"}, $LibVersion);
12442    $Cache{"get_PLevel"}{$TypeId}{$LibVersion} = $PLevel;
12443    return $PLevel;
12444}
12445
12446sub get_BaseType($$)
12447{
12448    my ($TypeId, $LibVersion) = @_;
12449    return () if(not $TypeId);
12450    if(defined $Cache{"get_BaseType"}{$TypeId}{$LibVersion}) {
12451        return %{$Cache{"get_BaseType"}{$TypeId}{$LibVersion}};
12452    }
12453    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12454    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12455    return %Type if(not $Type{"BaseType"});
12456    %Type = get_BaseType($Type{"BaseType"}, $LibVersion);
12457    $Cache{"get_BaseType"}{$TypeId}{$LibVersion} = \%Type;
12458    return %Type;
12459}
12460
12461sub get_BaseTypeQual($$)
12462{
12463    my ($TypeId, $LibVersion) = @_;
12464    return "" if(not $TypeId);
12465    return "" if(not $TypeInfo{$LibVersion}{$TypeId});
12466    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12467    return "" if(not $Type{"BaseType"});
12468    my $Qual = "";
12469    if($Type{"Type"} eq "Pointer") {
12470        $Qual .= "*";
12471    }
12472    elsif($Type{"Type"} eq "Ref") {
12473        $Qual .= "&";
12474    }
12475    elsif($Type{"Type"} eq "ConstVolatile") {
12476        $Qual .= "const volatile";
12477    }
12478    elsif($Type{"Type"} eq "Const"
12479    or $Type{"Type"} eq "Volatile"
12480    or $Type{"Type"} eq "Restrict") {
12481        $Qual .= lc($Type{"Type"});
12482    }
12483    my $BQual = get_BaseTypeQual($Type{"BaseType"}, $LibVersion);
12484    return $BQual.$Qual;
12485}
12486
12487sub get_OneStep_BaseType($$)
12488{
12489    my ($TypeId, $Info) = @_;
12490    if(not $TypeId or not $Info
12491    or not $Info->{$TypeId}) {
12492        return ();
12493    }
12494    my %Type = %{$Info->{$TypeId}};
12495    return %Type if(not $Type{"BaseType"});
12496    if(my $BTid = $Type{"BaseType"})
12497    {
12498        if($Info->{$BTid}) {
12499            return %{$Info->{$BTid}};
12500        }
12501        else { # something is going wrong
12502            return ();
12503        }
12504    }
12505    else {
12506        return %Type;
12507    }
12508}
12509
12510sub get_Type($$)
12511{
12512    my ($TypeId, $LibVersion) = @_;
12513    return () if(not $TypeId);
12514    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12515    return %{$TypeInfo{$LibVersion}{$TypeId}};
12516}
12517
12518sub isPrivateData($)
12519{ # non-public global data
12520    my $Symbol = $_[0];
12521    return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/);
12522}
12523
12524sub isInLineInst($$) {
12525    return (isTemplateInstance(@_) and not isTemplateSpec(@_));
12526}
12527
12528sub isTemplateInstance($$)
12529{
12530    my ($SInfo, $LibVersion) = @_;
12531
12532    if(my $ClassId = $SInfo->{"Class"})
12533    {
12534        if(my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"})
12535        {
12536            if(index($ClassName,"<")!=-1) {
12537                return 1;
12538            }
12539        }
12540    }
12541    if(my $ShortName = $SInfo->{"ShortName"})
12542    {
12543        if(index($ShortName,"<")!=-1
12544        and index($ShortName,">")!=-1) {
12545            return 1;
12546        }
12547    }
12548
12549    return 0;
12550}
12551
12552sub isTemplateSpec($$)
12553{
12554    my ($SInfo, $LibVersion) = @_;
12555    if(my $ClassId = $SInfo->{"Class"})
12556    {
12557        if($TypeInfo{$LibVersion}{$ClassId}{"Spec"})
12558        { # class specialization
12559            return 1;
12560        }
12561        elsif($SInfo->{"Spec"})
12562        { # method specialization
12563            return 1;
12564        }
12565    }
12566    return 0;
12567}
12568
12569sub symbolFilter($$$$)
12570{ # some special cases when the symbol cannot be imported
12571    my ($Symbol, $LibVersion, $Type, $Level) = @_;
12572
12573    if(isPrivateData($Symbol))
12574    { # non-public global data
12575        return 0;
12576    }
12577
12578    if(defined $SkipInternalSymbols)
12579    {
12580        return 0 if($Symbol=~/($SkipInternalSymbols)/);
12581    }
12582
12583    if($Symbol=~/\A_Z/)
12584    {
12585        if($Symbol=~/[CD][3-4]E/) {
12586            return 0;
12587        }
12588    }
12589
12590    if($CheckHeadersOnly and not checkDump($LibVersion, "2.7"))
12591    { # support for old ABI dumps in --headers-only mode
12592        foreach my $Pos (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
12593        {
12594            if(my $Pid = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"})
12595            {
12596                my $PType = $TypeInfo{$LibVersion}{$Pid}{"Type"};
12597                if(not $PType or $PType eq "Unknown") {
12598                    return 0;
12599                }
12600            }
12601        }
12602    }
12603    if($Type=~/Affected/)
12604    {
12605        my $Header = $CompleteSignature{$LibVersion}{$Symbol}{"Header"};
12606
12607        if($SkipSymbols{$LibVersion}{$Symbol})
12608        { # user defined symbols to ignore
12609            return 0;
12610        }
12611
12612        if($SymbolsListPath and not $SymbolsList{$Symbol})
12613        { # user defined symbols
12614            if(not $TargetHeadersPath or not $Header
12615            or not is_target_header($Header, 1))
12616            { # -symbols-list | -headers-list
12617                return 0;
12618            }
12619        }
12620
12621        if($AppPath and not $SymbolsList_App{$Symbol})
12622        { # user defined symbols (in application)
12623            return 0;
12624        }
12625
12626        my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"};
12627
12628        if($ClassId)
12629        {
12630            if(not isTargetType($ClassId, $LibVersion)) {
12631                return 0;
12632            }
12633        }
12634
12635        my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
12636        if(not $NameSpace and $ClassId)
12637        { # class methods have no "NameSpace" attribute
12638            $NameSpace = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"};
12639        }
12640        if($NameSpace)
12641        { # user defined namespaces to ignore
12642            if($SkipNameSpaces{$LibVersion}{$NameSpace}) {
12643                return 0;
12644            }
12645            foreach my $NS (keys(%{$SkipNameSpaces{$LibVersion}}))
12646            { # nested namespaces
12647                if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) {
12648                    return 0;
12649                }
12650            }
12651        }
12652        if($Header)
12653        {
12654            if(my $Skip = skipHeader($Header, $LibVersion))
12655            { # --skip-headers or <skip_headers> (not <skip_including>)
12656                if($Skip==1) {
12657                    return 0;
12658                }
12659            }
12660        }
12661        if($TypesListPath and $ClassId)
12662        { # user defined types
12663            my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
12664
12665            if(not $TypesList{$CName})
12666            {
12667                if(my $NS = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"})
12668                {
12669                    $CName=~s/\A\Q$NS\E\:\://g;
12670                }
12671
12672                if(not $TypesList{$CName})
12673                {
12674                    my $Found = 0;
12675
12676                    while($CName=~s/\:\:.+?\Z//)
12677                    {
12678                        if($TypesList{$CName})
12679                        {
12680                            $Found = 1;
12681                            last;
12682                        }
12683                    }
12684
12685                    if(not $Found) {
12686                        return 0;
12687                    }
12688                }
12689            }
12690        }
12691
12692        if(not selectSymbol($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $Level, $LibVersion))
12693        { # non-target symbols
12694            return 0;
12695        }
12696        if($Level eq "Binary")
12697        {
12698            if($CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
12699            or isInLineInst($CompleteSignature{$LibVersion}{$Symbol}, $LibVersion))
12700            {
12701                if($ClassId and $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
12702                { # inline virtual methods
12703                    if($Type=~/InlineVirt/) {
12704                        return 1;
12705                    }
12706                    my $Allocable = (not isCopyingClass($ClassId, $LibVersion));
12707                    if(not $Allocable)
12708                    { # check bases
12709                        foreach my $DCId (get_sub_classes($ClassId, $LibVersion, 1))
12710                        {
12711                            if(not isCopyingClass($DCId, $LibVersion))
12712                            { # exists a derived class without default c-tor
12713                                $Allocable=1;
12714                                last;
12715                            }
12716                        }
12717                    }
12718                    if(not $Allocable) {
12719                        return 0;
12720                    }
12721                }
12722                else
12723                { # inline non-virtual methods
12724                    return 0;
12725                }
12726            }
12727        }
12728    }
12729    return 1;
12730}
12731
12732sub detectAdded($)
12733{
12734    my $Level = $_[0];
12735    foreach my $Symbol (keys(%{$Symbol_Library{2}}))
12736    {
12737        if(link_symbol($Symbol, 1, "+Deps"))
12738        { # linker can find a new symbol
12739          # in the old-version library
12740          # So, it's not a new symbol
12741            next;
12742        }
12743        if(my $VSym = $SymVer{2}{$Symbol}
12744        and index($Symbol,"\@")==-1) {
12745            next;
12746        }
12747        $AddedInt{$Level}{$Symbol} = 1;
12748    }
12749}
12750
12751sub detectRemoved($)
12752{
12753    my $Level = $_[0];
12754    foreach my $Symbol (keys(%{$Symbol_Library{1}}))
12755    {
12756        if(link_symbol($Symbol, 2, "+Deps"))
12757        { # linker can find an old symbol
12758          # in the new-version library
12759            next;
12760        }
12761        if(my $VSym = $SymVer{1}{$Symbol}
12762        and index($Symbol,"\@")==-1) {
12763            next;
12764        }
12765        $RemovedInt{$Level}{$Symbol} = 1;
12766    }
12767}
12768
12769sub mergeLibs($)
12770{
12771    my $Level = $_[0];
12772    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
12773    { # checking added symbols
12774        next if($CompleteSignature{2}{$Symbol}{"Private"});
12775        next if(not $CompleteSignature{2}{$Symbol}{"Header"});
12776        next if(not symbolFilter($Symbol, 2, "Affected + InlineVirt", $Level));
12777        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
12778    }
12779    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
12780    { # checking removed symbols
12781        next if($CompleteSignature{1}{$Symbol}{"Private"});
12782        next if(not $CompleteSignature{1}{$Symbol}{"Header"});
12783        if(index($Symbol, "_ZTV")==0)
12784        { # skip v-tables for templates, that should not be imported by applications
12785            next if($tr_name{$Symbol}=~/</);
12786            if(my $CName = $VTableClass{$Symbol})
12787            {
12788                if(not keys(%{$ClassMethods{$Level}{1}{$CName}}))
12789                { # vtables for "private" classes
12790                  # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol
12791                    next;
12792                }
12793            }
12794
12795            if($SkipSymbols{1}{$Symbol})
12796            { # user defined symbols to ignore
12797                next;
12798            }
12799        }
12800        else {
12801            next if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level));
12802        }
12803        if($CompleteSignature{1}{$Symbol}{"PureVirt"})
12804        { # symbols for pure virtual methods cannot be called by clients
12805            next;
12806        }
12807        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
12808    }
12809}
12810
12811sub checkDump($$)
12812{
12813    my ($LibVersion, $V) = @_;
12814    if(defined $Cache{"checkDump"}{$LibVersion}{$V}) {
12815        return $Cache{"checkDump"}{$LibVersion}{$V};
12816    }
12817    return ($Cache{"checkDump"}{$LibVersion}{$V} = (not $UsedDump{$LibVersion}{"V"} or cmpVersions($UsedDump{$LibVersion}{"V"}, $V)>=0));
12818}
12819
12820sub detectAdded_H($)
12821{
12822    my $Level = $_[0];
12823    foreach my $Symbol (sort keys(%{$CompleteSignature{2}}))
12824    {
12825        if($Level eq "Source")
12826        { # remove symbol version
12827            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12828            $Symbol=$SN;
12829
12830            if($CompleteSignature{2}{$Symbol}{"Artificial"})
12831            { # skip artificial constructors
12832                next;
12833            }
12834        }
12835        if(not $CompleteSignature{2}{$Symbol}{"Header"}
12836        or not $CompleteSignature{2}{$Symbol}{"MnglName"}) {
12837            next;
12838        }
12839        if($ExtendedSymbols{$Symbol}) {
12840            next;
12841        }
12842        if(not defined $CompleteSignature{1}{$Symbol}
12843        or not $CompleteSignature{1}{$Symbol}{"MnglName"})
12844        {
12845            if($UsedDump{2}{"SrcBin"})
12846            {
12847                if($UsedDump{1}{"BinOnly"} or not checkDump(1, "2.11"))
12848                { # support for old and different (!) ABI dumps
12849                    if(not $CompleteSignature{2}{$Symbol}{"Virt"}
12850                    and not $CompleteSignature{2}{$Symbol}{"PureVirt"})
12851                    {
12852                        if($CheckHeadersOnly)
12853                        {
12854                            if(my $Lang = $CompleteSignature{2}{$Symbol}{"Lang"})
12855                            {
12856                                if($Lang eq "C")
12857                                { # support for old ABI dumps: missed extern "C" functions
12858                                    next;
12859                                }
12860                            }
12861                        }
12862                        else
12863                        {
12864                            if(not link_symbol($Symbol, 2, "-Deps"))
12865                            { # skip added inline symbols and const global data
12866                                next;
12867                            }
12868                        }
12869                    }
12870                }
12871            }
12872            $AddedInt{$Level}{$Symbol} = 1;
12873        }
12874    }
12875}
12876
12877sub detectRemoved_H($)
12878{
12879    my $Level = $_[0];
12880    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
12881    {
12882        if($Level eq "Source")
12883        { # remove symbol version
12884            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12885            $Symbol=$SN;
12886        }
12887        if(not $CompleteSignature{1}{$Symbol}{"Header"}
12888        or not $CompleteSignature{1}{$Symbol}{"MnglName"}) {
12889            next;
12890        }
12891        if($ExtendedSymbols{$Symbol}) {
12892            next;
12893        }
12894        if(not defined $CompleteSignature{2}{$Symbol}
12895        or not $CompleteSignature{2}{$Symbol}{"MnglName"})
12896        {
12897            if($UsedDump{1}{"SrcBin"})
12898            {
12899                if($UsedDump{2}{"BinOnly"} or not checkDump(2, "2.11"))
12900                { # support for old and different (!) ABI dumps
12901                    if(not $CompleteSignature{1}{$Symbol}{"Virt"}
12902                    and not $CompleteSignature{1}{$Symbol}{"PureVirt"})
12903                    {
12904                        if($CheckHeadersOnly)
12905                        { # skip all removed symbols
12906                            if(my $Lang = $CompleteSignature{1}{$Symbol}{"Lang"})
12907                            {
12908                                if($Lang eq "C")
12909                                { # support for old ABI dumps: missed extern "C" functions
12910                                    next;
12911                                }
12912                            }
12913                        }
12914                        else
12915                        {
12916                            if(not link_symbol($Symbol, 1, "-Deps"))
12917                            { # skip removed inline symbols
12918                                next;
12919                            }
12920                        }
12921                    }
12922                }
12923            }
12924            if(not checkDump(1, "2.15"))
12925            {
12926                if($Symbol=~/_IT_E\Z/)
12927                { # _ZN28QExplicitlySharedDataPointerI22QSslCertificatePrivateEC1IT_EERKS_IT_E
12928                    next;
12929                }
12930            }
12931            if(not $CompleteSignature{1}{$Symbol}{"Class"})
12932            {
12933                if(my $Short = $CompleteSignature{1}{$Symbol}{"ShortName"})
12934                {
12935                    if(defined $Constants{2}{$Short})
12936                    {
12937                        my $Val = $Constants{2}{$Short}{"Value"};
12938                        if(defined $Func_ShortName{2}{$Val})
12939                        { # old name defined to new
12940                            next;
12941                        }
12942                    }
12943                }
12944
12945            }
12946            $RemovedInt{$Level}{$Symbol} = 1;
12947            if($Level eq "Source")
12948            { # search for a source-compatible equivalent
12949                setAlternative($Symbol, $Level);
12950            }
12951        }
12952    }
12953}
12954
12955sub mergeHeaders($)
12956{
12957    my $Level = $_[0];
12958    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
12959    { # checking added symbols
12960        next if($CompleteSignature{2}{$Symbol}{"PureVirt"});
12961        next if($CompleteSignature{2}{$Symbol}{"Private"});
12962        next if(not symbolFilter($Symbol, 2, "Affected", $Level));
12963        if($Level eq "Binary")
12964        {
12965            if($CompleteSignature{2}{$Symbol}{"InLine"})
12966            {
12967                if(not $CompleteSignature{2}{$Symbol}{"Virt"})
12968                { # skip inline non-virtual functions
12969                    next;
12970                }
12971            }
12972        }
12973        else
12974        { # Source
12975            if($SourceAlternative_B{$Symbol}) {
12976                next;
12977            }
12978        }
12979        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
12980    }
12981    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
12982    { # checking removed symbols
12983        next if($CompleteSignature{1}{$Symbol}{"PureVirt"});
12984        next if($CompleteSignature{1}{$Symbol}{"Private"});
12985        next if(not symbolFilter($Symbol, 1, "Affected", $Level));
12986        if($Level eq "Binary")
12987        {
12988            if($CompleteSignature{1}{$Symbol}{"InLine"})
12989            {
12990                if(not $CompleteSignature{1}{$Symbol}{"Virt"})
12991                { # skip inline non-virtual functions
12992                    next;
12993                }
12994            }
12995        }
12996        else
12997        { # Source
12998            if(my $Alt = $SourceAlternative{$Symbol})
12999            {
13000                if(defined $CompleteSignature{1}{$Alt}
13001                and $CompleteSignature{1}{$Symbol}{"Const"})
13002                {
13003                    my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
13004                    %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=(
13005                        "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"},
13006                        "Target"=>get_Signature($Alt, 1));
13007                }
13008                else
13009                { # do NOT show removed symbol
13010                    next;
13011                }
13012            }
13013        }
13014        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
13015    }
13016}
13017
13018sub addParamNames($)
13019{
13020    my $LibraryVersion = $_[0];
13021    return if(not keys(%AddIntParams));
13022    my $SecondVersion = $LibraryVersion==1?2:1;
13023    foreach my $Interface (sort keys(%{$CompleteSignature{$LibraryVersion}}))
13024    {
13025        next if(not keys(%{$AddIntParams{$Interface}}));
13026        foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibraryVersion}{$Interface}{"Param"}}))
13027        { # add absent parameter names
13028            my $ParamName = $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"};
13029            if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddIntParams{$Interface}{$ParamPos})
13030            { # names from the external file
13031                if(defined $CompleteSignature{$SecondVersion}{$Interface}
13032                and defined $CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos})
13033                {
13034                    if($CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) {
13035                        $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
13036                    }
13037                }
13038                else {
13039                    $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
13040                }
13041            }
13042        }
13043    }
13044}
13045
13046sub detectChangedTypedefs()
13047{ # detect changed typedefs to show
13048  # correct function signatures
13049    foreach my $Typedef (keys(%{$Typedef_BaseName{1}}))
13050    {
13051        next if(not $Typedef);
13052        my $BName1 = $Typedef_BaseName{1}{$Typedef};
13053        if(not $BName1 or isAnon($BName1)) {
13054            next;
13055        }
13056        my $BName2 = $Typedef_BaseName{2}{$Typedef};
13057        if(not $BName2 or isAnon($BName2)) {
13058            next;
13059        }
13060        if($BName1 ne $BName2) {
13061            $ChangedTypedef{$Typedef} = 1;
13062        }
13063    }
13064}
13065
13066sub get_symbol_suffix($$)
13067{
13068    my ($Symbol, $Full) = @_;
13069    my ($SN, $SO, $SV) = separate_symbol($Symbol);
13070    $Symbol=$SN; # remove version
13071    my $Signature = $tr_name{$Symbol};
13072    my $Suffix = substr($Signature, find_center($Signature, "("));
13073    if(not $Full) {
13074        $Suffix=~s/(\))\s*(const volatile|volatile const|const|volatile)\Z/$1/g;
13075    }
13076    return $Suffix;
13077}
13078
13079sub get_symbol_prefix($$)
13080{
13081    my ($Symbol, $LibVersion) = @_;
13082    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
13083    if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
13084    { # methods
13085        $ShortName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}."::".$ShortName;
13086    }
13087    return $ShortName;
13088}
13089
13090sub setAlternative($)
13091{
13092    my $Symbol = $_[0];
13093    my $PSymbol = $Symbol;
13094    if(not defined $CompleteSignature{2}{$PSymbol}
13095    or (not $CompleteSignature{2}{$PSymbol}{"MnglName"}
13096    and not $CompleteSignature{2}{$PSymbol}{"ShortName"}))
13097    { # search for a pair
13098        if(my $ShortName = $CompleteSignature{1}{$PSymbol}{"ShortName"})
13099        {
13100            if($CompleteSignature{1}{$PSymbol}{"Data"})
13101            {
13102                if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/
13103                or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/)
13104                {
13105                    if(defined $CompleteSignature{2}{$PSymbol}
13106                    and $CompleteSignature{2}{$PSymbol}{"MnglName"})
13107                    {
13108                        $SourceAlternative{$Symbol} = $PSymbol;
13109                        $SourceAlternative_B{$PSymbol} = $Symbol;
13110                        if(not defined $CompleteSignature{1}{$PSymbol}
13111                        or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
13112                            $SourceReplacement{$Symbol} = $PSymbol;
13113                        }
13114                    }
13115                }
13116            }
13117            else
13118            {
13119                foreach my $Sp ("KV", "VK", "K", "V")
13120                {
13121                    if($PSymbol=~s/\A_ZN$Sp/_ZN/
13122                    or $PSymbol=~s/\A_ZN/_ZN$Sp/)
13123                    {
13124                        if(defined $CompleteSignature{2}{$PSymbol}
13125                        and $CompleteSignature{2}{$PSymbol}{"MnglName"})
13126                        {
13127                            $SourceAlternative{$Symbol} = $PSymbol;
13128                            $SourceAlternative_B{$PSymbol} = $Symbol;
13129                            if(not defined $CompleteSignature{1}{$PSymbol}
13130                            or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
13131                                $SourceReplacement{$Symbol} = $PSymbol;
13132                            }
13133                        }
13134                    }
13135                    $PSymbol = $Symbol;
13136                }
13137            }
13138        }
13139    }
13140    return "";
13141}
13142
13143sub getSymKind($$)
13144{
13145    my ($Symbol, $LibVersion) = @_;
13146    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"})
13147    {
13148        return "Global_Data";
13149    }
13150    elsif($CompleteSignature{$LibVersion}{$Symbol}{"Class"})
13151    {
13152        return "Method";
13153    }
13154    return "Function";
13155}
13156
13157sub mergeSymbols($)
13158{
13159    my $Level = $_[0];
13160    my %SubProblems = ();
13161
13162    mergeBases($Level);
13163
13164    my %AddedOverloads = ();
13165    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
13166    { # check all added exported symbols
13167        if(not $CompleteSignature{2}{$Symbol}{"Header"}) {
13168            next;
13169        }
13170        if(defined $CompleteSignature{1}{$Symbol}
13171        and $CompleteSignature{1}{$Symbol}{"Header"})
13172        { # double-check added symbol
13173            next;
13174        }
13175        if(not symbolFilter($Symbol, 2, "Affected", $Level)) {
13176            next;
13177        }
13178        if($Symbol=~/\A(_Z|\?)/)
13179        { # C++
13180            $AddedOverloads{get_symbol_prefix($Symbol, 2)}{get_symbol_suffix($Symbol, 1)} = $Symbol;
13181        }
13182        if(my $OverriddenMethod = $CompleteSignature{2}{$Symbol}{"Override"})
13183        { # register virtual overridings
13184            my $Cid = $CompleteSignature{2}{$Symbol}{"Class"};
13185            my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"};
13186            if(defined $CompleteSignature{1}{$OverriddenMethod} and $CompleteSignature{1}{$OverriddenMethod}{"Virt"}
13187            and not $CompleteSignature{1}{$OverriddenMethod}{"Private"})
13188            {
13189                if($TName_Tid{1}{$AffectedClass_Name})
13190                { # class should exist in previous version
13191                    if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1))
13192                    { # old v-table is NOT copied by old applications
13193                        %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$tr_name{$Symbol}}}=(
13194                            "Type_Name"=>$AffectedClass_Name,
13195                            "Target"=>get_Signature($Symbol, 2),
13196                            "Old_Value"=>get_Signature($OverriddenMethod, 2),
13197                            "New_Value"=>get_Signature($Symbol, 2));
13198                    }
13199                }
13200            }
13201        }
13202    }
13203    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
13204    { # check all removed exported symbols
13205        if(not $CompleteSignature{1}{$Symbol}{"Header"}) {
13206            next;
13207        }
13208        if(defined $CompleteSignature{2}{$Symbol}
13209        and $CompleteSignature{2}{$Symbol}{"Header"})
13210        { # double-check removed symbol
13211            next;
13212        }
13213        if($CompleteSignature{1}{$Symbol}{"Private"})
13214        { # skip private methods
13215            next;
13216        }
13217        if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
13218            next;
13219        }
13220        $CheckedSymbols{$Level}{$Symbol} = 1;
13221        if(my $OverriddenMethod = $CompleteSignature{1}{$Symbol}{"Override"})
13222        { # register virtual overridings
13223            my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
13224            my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"};
13225            if(defined $CompleteSignature{2}{$OverriddenMethod}
13226            and $CompleteSignature{2}{$OverriddenMethod}{"Virt"})
13227            {
13228                if($TName_Tid{2}{$AffectedClass_Name})
13229                { # class should exist in newer version
13230                    if(not isCopyingClass($CompleteSignature{1}{$Symbol}{"Class"}, 1))
13231                    { # old v-table is NOT copied by old applications
13232                        %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$tr_name{$OverriddenMethod}}}=(
13233                            "Type_Name"=>$AffectedClass_Name,
13234                            "Target"=>get_Signature($OverriddenMethod, 1),
13235                            "Old_Value"=>get_Signature($Symbol, 1),
13236                            "New_Value"=>get_Signature($OverriddenMethod, 1));
13237                    }
13238                }
13239            }
13240        }
13241        if($Level eq "Binary"
13242        and $OStarget eq "windows")
13243        { # register the reason of symbol name change
13244            if(my $NewSym = $mangled_name{2}{$tr_name{$Symbol}})
13245            {
13246                if($AddedInt{$Level}{$NewSym})
13247                {
13248                    if($CompleteSignature{1}{$Symbol}{"Static"} ne $CompleteSignature{2}{$NewSym}{"Static"})
13249                    {
13250                        if($CompleteSignature{2}{$NewSym}{"Static"})
13251                        {
13252                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$tr_name{$Symbol}}}=(
13253                                "Target"=>$tr_name{$Symbol},
13254                                "Old_Value"=>$Symbol,
13255                                "New_Value"=>$NewSym  );
13256                        }
13257                        else
13258                        {
13259                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$tr_name{$Symbol}}}=(
13260                                "Target"=>$tr_name{$Symbol},
13261                                "Old_Value"=>$Symbol,
13262                                "New_Value"=>$NewSym  );
13263                        }
13264                    }
13265                    if($CompleteSignature{1}{$Symbol}{"Virt"} ne $CompleteSignature{2}{$NewSym}{"Virt"})
13266                    {
13267                        if($CompleteSignature{2}{$NewSym}{"Virt"})
13268                        {
13269                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$tr_name{$Symbol}}}=(
13270                                "Target"=>$tr_name{$Symbol},
13271                                "Old_Value"=>$Symbol,
13272                                "New_Value"=>$NewSym  );
13273                        }
13274                        else
13275                        {
13276                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$tr_name{$Symbol}}}=(
13277                                "Target"=>$tr_name{$Symbol},
13278                                "Old_Value"=>$Symbol,
13279                                "New_Value"=>$NewSym  );
13280                        }
13281                    }
13282                    my $RTId1 = $CompleteSignature{1}{$Symbol}{"Return"};
13283                    my $RTId2 = $CompleteSignature{2}{$NewSym}{"Return"};
13284                    my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"};
13285                    my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"};
13286                    if($RTName1 ne $RTName2)
13287                    {
13288                        my $ProblemType = "Symbol_Changed_Return";
13289                        if($CompleteSignature{1}{$Symbol}{"Data"}) {
13290                            $ProblemType = "Global_Data_Symbol_Changed_Type";
13291                        }
13292                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$tr_name{$Symbol}}}=(
13293                            "Target"=>$tr_name{$Symbol},
13294                            "Old_Type"=>$RTName1,
13295                            "New_Type"=>$RTName2,
13296                            "Old_Value"=>$Symbol,
13297                            "New_Value"=>$NewSym  );
13298                    }
13299                }
13300            }
13301        }
13302        if($Symbol=~/\A(_Z|\?)/)
13303        { # C++
13304            my $Prefix = get_symbol_prefix($Symbol, 1);
13305            if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}})
13306            and not $AddedOverloads{$Prefix}{get_symbol_suffix($Symbol, 1)})
13307            { # changed signature: params, "const"-qualifier
13308                my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]};
13309                if($CompleteSignature{1}{$Symbol}{"Constructor"})
13310                {
13311                    if($Symbol=~/(C[1-2][EI])/)
13312                    {
13313                        my $CtorType = $1;
13314                        $NewSym=~s/(C[1-2][EI])/$CtorType/g;
13315                    }
13316                }
13317                elsif($CompleteSignature{1}{$Symbol}{"Destructor"})
13318                {
13319                    if($Symbol=~/(D[0-2][EI])/)
13320                    {
13321                        my $DtorType = $1;
13322                        $NewSym=~s/(D[0-2][EI])/$DtorType/g;
13323                    }
13324                }
13325                my $NS1 = $CompleteSignature{1}{$Symbol}{"NameSpace"};
13326                my $NS2 = $CompleteSignature{2}{$NewSym}{"NameSpace"};
13327                if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2))
13328                { # from the same class and namespace
13329                    if($CompleteSignature{1}{$Symbol}{"Const"}
13330                    and not $CompleteSignature{2}{$NewSym}{"Const"})
13331                    { # "const" to non-"const"
13332                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$tr_name{$Symbol}}}=(
13333                            "Type_Name"=>$TypeInfo{1}{$CompleteSignature{1}{$Symbol}{"Class"}}{"Name"},
13334                            "Target"=>$tr_name{$Symbol},
13335                            "New_Signature"=>get_Signature($NewSym, 2),
13336                            "Old_Value"=>$Symbol,
13337                            "New_Value"=>$NewSym  );
13338                    }
13339                    elsif(not $CompleteSignature{1}{$Symbol}{"Const"}
13340                    and $CompleteSignature{2}{$NewSym}{"Const"})
13341                    { # non-"const" to "const"
13342                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$tr_name{$Symbol}}}=(
13343                            "Target"=>$tr_name{$Symbol},
13344                            "New_Signature"=>get_Signature($NewSym, 2),
13345                            "Old_Value"=>$Symbol,
13346                            "New_Value"=>$NewSym  );
13347                    }
13348                    if($CompleteSignature{1}{$Symbol}{"Volatile"}
13349                    and not $CompleteSignature{2}{$NewSym}{"Volatile"})
13350                    { # "volatile" to non-"volatile"
13351
13352                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$tr_name{$Symbol}}}=(
13353                            "Target"=>$tr_name{$Symbol},
13354                            "New_Signature"=>get_Signature($NewSym, 2),
13355                            "Old_Value"=>$Symbol,
13356                            "New_Value"=>$NewSym  );
13357                    }
13358                    elsif(not $CompleteSignature{1}{$Symbol}{"Volatile"}
13359                    and $CompleteSignature{2}{$NewSym}{"Volatile"})
13360                    { # non-"volatile" to "volatile"
13361                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$tr_name{$Symbol}}}=(
13362                            "Target"=>$tr_name{$Symbol},
13363                            "New_Signature"=>get_Signature($NewSym, 2),
13364                            "Old_Value"=>$Symbol,
13365                            "New_Value"=>$NewSym  );
13366                    }
13367                    if(get_symbol_suffix($Symbol, 0) ne get_symbol_suffix($NewSym, 0))
13368                    { # params list
13369                        %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$tr_name{$Symbol}}}=(
13370                            "Target"=>$tr_name{$Symbol},
13371                            "New_Signature"=>get_Signature($NewSym, 2),
13372                            "Old_Value"=>$Symbol,
13373                            "New_Value"=>$NewSym  );
13374                    }
13375                }
13376            }
13377        }
13378    }
13379    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
13380    { # checking symbols
13381        $CurrentSymbol = $Symbol;
13382
13383        my ($SN, $SS, $SV) = separate_symbol($Symbol);
13384        if($Level eq "Source")
13385        { # remove symbol version
13386            $Symbol=$SN;
13387        }
13388        else
13389        { # Binary
13390            if(not $SV)
13391            { # symbol without version
13392                if(my $VSym = $SymVer{1}{$Symbol})
13393                { # the symbol is linked with versioned symbol
13394                    if($CompleteSignature{2}{$VSym}{"MnglName"})
13395                    { # show report for symbol@ver only
13396                        next;
13397                    }
13398                    elsif(not link_symbol($VSym, 2, "-Deps"))
13399                    { # changed version: sym@v1 to sym@v2
13400                      # do NOT show report for symbol
13401                        next;
13402                    }
13403                }
13404            }
13405        }
13406        my $PSymbol = $Symbol;
13407        if($Level eq "Source"
13408        and my $S = $SourceReplacement{$Symbol})
13409        { # take a source-compatible replacement function
13410            $PSymbol = $S;
13411        }
13412        if($CompleteSignature{1}{$Symbol}{"Private"})
13413        { # private symbols
13414            next;
13415        }
13416        if(not defined $CompleteSignature{1}{$Symbol}
13417        or not defined $CompleteSignature{2}{$PSymbol})
13418        { # no info
13419            next;
13420        }
13421        if(not $CompleteSignature{1}{$Symbol}{"MnglName"}
13422        or not $CompleteSignature{2}{$PSymbol}{"MnglName"})
13423        { # no mangled name
13424            next;
13425        }
13426        if(not $CompleteSignature{1}{$Symbol}{"Header"}
13427        or not $CompleteSignature{2}{$PSymbol}{"Header"})
13428        { # without a header
13429            next;
13430        }
13431
13432        if(not $CompleteSignature{1}{$Symbol}{"PureVirt"}
13433        and $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13434        { # became pure
13435            next;
13436        }
13437        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13438        and not $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13439        { # became non-pure
13440            next;
13441        }
13442
13443        if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level))
13444        { # exported, target, inline virtual and pure virtual
13445            next;
13446        }
13447        if(not symbolFilter($PSymbol, 2, "Affected + InlineVirt", $Level))
13448        { # exported, target, inline virtual and pure virtual
13449            next;
13450        }
13451
13452        if(checkDump(1, "2.13") and checkDump(2, "2.13"))
13453        {
13454            if($CompleteSignature{1}{$Symbol}{"Data"}
13455            and $CompleteSignature{2}{$PSymbol}{"Data"})
13456            {
13457                my $Value1 = $CompleteSignature{1}{$Symbol}{"Value"};
13458                my $Value2 = $CompleteSignature{2}{$PSymbol}{"Value"};
13459                if(defined $Value1)
13460                {
13461                    $Value1 = showVal($Value1, $CompleteSignature{1}{$Symbol}{"Return"}, 1);
13462                    if(defined $Value2)
13463                    {
13464                        $Value2 = showVal($Value2, $CompleteSignature{2}{$PSymbol}{"Return"}, 2);
13465                        if($Value1 ne $Value2)
13466                        {
13467                            %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=(
13468                                "Old_Value"=>$Value1,
13469                                "New_Value"=>$Value2,
13470                                "Target"=>get_Signature($Symbol, 1)  );
13471                        }
13472                    }
13473                }
13474            }
13475        }
13476
13477        if($CompleteSignature{2}{$PSymbol}{"Private"})
13478        {
13479            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=(
13480                "Target"=>get_Signature_M($PSymbol, 2)  );
13481        }
13482        elsif(not $CompleteSignature{1}{$Symbol}{"Protected"}
13483        and $CompleteSignature{2}{$PSymbol}{"Protected"})
13484        {
13485            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=(
13486                "Target"=>get_Signature_M($PSymbol, 2)  );
13487        }
13488        elsif($CompleteSignature{1}{$Symbol}{"Protected"}
13489        and not $CompleteSignature{2}{$PSymbol}{"Protected"})
13490        {
13491            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=(
13492                "Target"=>get_Signature_M($PSymbol, 2)  );
13493        }
13494
13495        # checking virtual table
13496        mergeVirtualTables($Symbol, $Level);
13497
13498        if($COMPILE_ERRORS)
13499        { # if some errors occurred at the compiling stage
13500          # then some false positives can be skipped here
13501            if(not $CompleteSignature{1}{$Symbol}{"Data"} and $CompleteSignature{2}{$PSymbol}{"Data"}
13502            and not $GlobalDataObject{2}{$Symbol})
13503            { # missed information about parameters in newer version
13504                next;
13505            }
13506            if($CompleteSignature{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol}
13507            and not $CompleteSignature{2}{$PSymbol}{"Data"})
13508            { # missed information about parameters in older version
13509                next;
13510            }
13511        }
13512        my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
13513        # checking attributes
13514        if($CompleteSignature{2}{$PSymbol}{"Static"}
13515        and not $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13516        {
13517            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=(
13518                "Target"=>get_Signature($Symbol, 1)
13519            );
13520        }
13521        elsif(not $CompleteSignature{2}{$PSymbol}{"Static"}
13522        and $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13523        {
13524            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=(
13525                "Target"=>get_Signature($Symbol, 1)
13526            );
13527        }
13528        if(($CompleteSignature{1}{$Symbol}{"Virt"} and $CompleteSignature{2}{$PSymbol}{"Virt"})
13529        or ($CompleteSignature{1}{$Symbol}{"PureVirt"} and $CompleteSignature{2}{$PSymbol}{"PureVirt"}))
13530        { # relative position of virtual and pure virtual methods
13531            if($Level eq "Binary")
13532            {
13533                if(defined $CompleteSignature{1}{$Symbol}{"RelPos"} and defined $CompleteSignature{2}{$PSymbol}{"RelPos"}
13534                and $CompleteSignature{1}{$Symbol}{"RelPos"}!=$CompleteSignature{2}{$PSymbol}{"RelPos"})
13535                { # top-level virtual methods only
13536                    my $Class_Id = $CompleteSignature{1}{$Symbol}{"Class"};
13537                    my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"};
13538                    if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name}
13539                    and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol})
13540                    { # check the absolute position of virtual method (including added and removed methods)
13541                        my %Class_Type = get_Type($Class_Id, 1);
13542                        my $ProblemType = "Virtual_Method_Position";
13543                        if($CompleteSignature{1}{$Symbol}{"PureVirt"}) {
13544                            $ProblemType = "Pure_Virtual_Method_Position";
13545                        }
13546                        if(isUsedClass($Class_Id, 1, $Level))
13547                        {
13548                            my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}}));
13549                            foreach my $ASymbol (@Affected)
13550                            {
13551                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
13552                                    next;
13553                                }
13554                                %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$MnglName}}}=(
13555                                    "Type_Name"=>$Class_Type{"Name"},
13556                                    "Old_Value"=>$CompleteSignature{1}{$Symbol}{"RelPos"},
13557                                    "New_Value"=>$CompleteSignature{2}{$PSymbol}{"RelPos"},
13558                                    "Target"=>get_Signature($Symbol, 1));
13559                            }
13560                            $VTableChanged_M{$Class_Type{"Name"}} = 1;
13561                        }
13562                    }
13563                }
13564            }
13565        }
13566        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13567        or $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13568        { # do NOT check type changes in pure virtuals
13569            next;
13570        }
13571        $CheckedSymbols{$Level}{$Symbol} = 1;
13572        if($Symbol=~/\A(_Z|\?)/
13573        or keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})==keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13574        { # C/C++: changes in parameters
13575            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13576            { # checking parameters
13577                mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1);
13578            }
13579        }
13580        else
13581        { # C: added/removed parameters
13582            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13583            { # checking added parameters
13584                my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13585                my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"};
13586                last if($PType2_Name eq "...");
13587                my $PName = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13588                my $PName_Old = (defined $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos})?$CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:"";
13589                my $ParamPos_Prev = "-1";
13590                if($PName=~/\Ap\d+\Z/i)
13591                { # added unnamed parameter ( pN )
13592                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1);
13593                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2);
13594                    if($#Positions1==-1 or $#Positions2>$#Positions1) {
13595                        $ParamPos_Prev = "lost";
13596                    }
13597                }
13598                else {
13599                    $ParamPos_Prev = find_ParamPair_Pos_byName($PName, $Symbol, 1);
13600                }
13601                if($ParamPos_Prev eq "lost")
13602                {
13603                    if($ParamPos>keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13604                    {
13605                        my $ProblemType = "Added_Parameter";
13606                        if($PName=~/\Ap\d+\Z/) {
13607                            $ProblemType = "Added_Unnamed_Parameter";
13608                        }
13609                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13610                            "Target"=>$PName,
13611                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13612                            "Param_Type"=>$PType2_Name,
13613                            "New_Signature"=>get_Signature($Symbol, 2)  );
13614                    }
13615                    else
13616                    {
13617                        my %ParamType_Pure = get_PureType($PType2_Id, $TypeInfo{2});
13618                        my $PairType_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13619                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{1});
13620                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"})
13621                        and find_ParamPair_Pos_byName($PName_Old, $Symbol, 2) eq "lost")
13622                        {
13623                            if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13624                            {
13625                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13626                                    "Target"=>$PName_Old,
13627                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13628                                    "Param_Type"=>$PType2_Name,
13629                                    "Old_Value"=>$PName_Old,
13630                                    "New_Value"=>$PName,
13631                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13632                            }
13633                        }
13634                        else
13635                        {
13636                            my $ProblemType = "Added_Middle_Parameter";
13637                            if($PName=~/\Ap\d+\Z/) {
13638                                $ProblemType = "Added_Middle_Unnamed_Parameter";
13639                            }
13640                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13641                                "Target"=>$PName,
13642                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13643                                "Param_Type"=>$PType2_Name,
13644                                "New_Signature"=>get_Signature($Symbol, 2)  );
13645                        }
13646                    }
13647                }
13648            }
13649            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13650            { # check relevant parameters
13651                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13652                my $ParamName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13653                # FIXME: find relevant parameter by name
13654                if(defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})
13655                {
13656                    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13657                    my $ParamName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13658                    if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"}
13659                    or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) {
13660                        mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0);
13661                    }
13662                }
13663            }
13664            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13665            { # checking removed parameters
13666                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13667                my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"};
13668                last if($PType1_Name eq "...");
13669                my $PName = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13670                my $PName_New = (defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})?$CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:"";
13671                my $ParamPos_New = "-1";
13672                if($PName=~/\Ap\d+\Z/i)
13673                { # removed unnamed parameter ( pN )
13674                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1);
13675                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2);
13676                    if($#Positions2==-1 or $#Positions2<$#Positions1) {
13677                        $ParamPos_New = "lost";
13678                    }
13679                }
13680                else {
13681                    $ParamPos_New = find_ParamPair_Pos_byName($PName, $Symbol, 2);
13682                }
13683                if($ParamPos_New eq "lost")
13684                {
13685                    if($ParamPos>keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})-1)
13686                    {
13687                        my $ProblemType = "Removed_Parameter";
13688                        if($PName=~/\Ap\d+\Z/) {
13689                            $ProblemType = "Removed_Unnamed_Parameter";
13690                        }
13691                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13692                            "Target"=>$PName,
13693                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13694                            "Param_Type"=>$PType1_Name,
13695                            "New_Signature"=>get_Signature($Symbol, 2)  );
13696                    }
13697                    elsif($ParamPos<keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13698                    {
13699                        my %ParamType_Pure = get_PureType($PType1_Id, $TypeInfo{1});
13700                        my $PairType_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13701                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{2});
13702                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType1_Name eq $TypeInfo{2}{$PairType_Id}{"Name"})
13703                        and find_ParamPair_Pos_byName($PName_New, $Symbol, 1) eq "lost")
13704                        {
13705                            if($PName_New!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13706                            {
13707                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13708                                    "Target"=>$PName,
13709                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13710                                    "Param_Type"=>$PType1_Name,
13711                                    "Old_Value"=>$PName,
13712                                    "New_Value"=>$PName_New,
13713                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13714                            }
13715                        }
13716                        else
13717                        {
13718                            my $ProblemType = "Removed_Middle_Parameter";
13719                            if($PName=~/\Ap\d+\Z/) {
13720                                $ProblemType = "Removed_Middle_Unnamed_Parameter";
13721                            }
13722                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13723                                "Target"=>$PName,
13724                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13725                                "Param_Type"=>$PType1_Name,
13726                                "New_Signature"=>get_Signature($Symbol, 2)  );
13727                        }
13728                    }
13729                }
13730            }
13731        }
13732        # checking return type
13733        my $ReturnType1_Id = $CompleteSignature{1}{$Symbol}{"Return"};
13734        my $ReturnType2_Id = $CompleteSignature{2}{$PSymbol}{"Return"};
13735        my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level);
13736
13737        foreach my $SubProblemType (keys(%RC_SubProblems))
13738        {
13739            my $New_Value = $RC_SubProblems{$SubProblemType}{"New_Value"};
13740            my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"};
13741            my %ProblemTypes = ();
13742
13743            if($CompleteSignature{1}{$Symbol}{"Data"})
13744            {
13745                if($SubProblemType eq "Return_Type_And_Size") {
13746                    $ProblemTypes{"Global_Data_Type_And_Size"} = 1;
13747                }
13748                elsif($SubProblemType eq "Return_Type_Format") {
13749                    $ProblemTypes{"Global_Data_Type_Format"} = 1;
13750                }
13751                else {
13752                    $ProblemTypes{"Global_Data_Type"} = 1;
13753                }
13754
13755                # quals
13756                if($SubProblemType eq "Return_Type"
13757                or $SubProblemType eq "Return_Type_And_Size"
13758                or $SubProblemType eq "Return_Type_Format")
13759                {
13760                    if(my $RR = removedQual($Old_Value, $New_Value, "const"))
13761                    { # const to non-const
13762                        if($RR==2) {
13763                            $ProblemTypes{"Global_Data_Removed_Const"} = 1;
13764                        }
13765                        else {
13766                            $ProblemTypes{"Global_Data_Became_Non_Const"} = 1;
13767                        }
13768                        $ProblemTypes{"Global_Data_Type"} = 1;
13769                    }
13770                    elsif(my $RA = addedQual($Old_Value, $New_Value, "const"))
13771                    { # non-const to const
13772                        if($RA==2) {
13773                            $ProblemTypes{"Global_Data_Added_Const"} = 1;
13774                        }
13775                        else {
13776                            $ProblemTypes{"Global_Data_Became_Const"} = 1;
13777                        }
13778                        $ProblemTypes{"Global_Data_Type"} = 1;
13779                    }
13780                }
13781            }
13782            else
13783            {
13784                # quals
13785                if($SubProblemType eq "Return_Type"
13786                or $SubProblemType eq "Return_Type_And_Size"
13787                or $SubProblemType eq "Return_Type_Format")
13788                {
13789                    if(checkDump(1, "2.6") and checkDump(2, "2.6"))
13790                    {
13791                        if(addedQual($Old_Value, $New_Value, "volatile"))
13792                        {
13793                            $ProblemTypes{"Return_Value_Became_Volatile"} = 1;
13794                            if($Level ne "Source"
13795                            or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13796                                $ProblemTypes{"Return_Type"} = 1;
13797                            }
13798                        }
13799                    }
13800                    if(my $RA = addedQual($Old_Value, $New_Value, "const"))
13801                    {
13802                        if($RA==2) {
13803                            $ProblemTypes{"Return_Type_Added_Const"} = 1;
13804                        }
13805                        else {
13806                            $ProblemTypes{"Return_Type_Became_Const"} = 1;
13807                        }
13808                        if($Level ne "Source"
13809                        or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13810                            $ProblemTypes{"Return_Type"} = 1;
13811                        }
13812                    }
13813                }
13814            }
13815            if($Level eq "Binary"
13816            and not $CompleteSignature{1}{$Symbol}{"Data"})
13817            {
13818                my ($Arch1, $Arch2) = (getArch(1), getArch(2));
13819                if($Arch1 eq "unknown" or $Arch2 eq "unknown")
13820                { # if one of the architectures is unknown
13821                    # then set other arhitecture to unknown too
13822                    ($Arch1, $Arch2) = ("unknown", "unknown");
13823                }
13824                my (%Conv1, %Conv2) = ();
13825                if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"})
13826                {
13827                    %Conv1 = callingConvention_R_Real($CompleteSignature{1}{$Symbol});
13828                    %Conv2 = callingConvention_R_Real($CompleteSignature{2}{$PSymbol});
13829                }
13830                else
13831                {
13832                    %Conv1 = callingConvention_R_Model($CompleteSignature{1}{$Symbol}, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
13833                    %Conv2 = callingConvention_R_Model($CompleteSignature{2}{$PSymbol}, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
13834                }
13835
13836                if($SubProblemType eq "Return_Type_Became_Void")
13837                {
13838                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13839                    { # parameters stack has been affected
13840                        if($Conv1{"Method"} eq "stack") {
13841                            $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1;
13842                        }
13843                        elsif($Conv1{"Hidden"}) {
13844                            $ProblemTypes{"Return_Type_Became_Void_And_Register"} = 1;
13845                        }
13846                    }
13847                }
13848                elsif($SubProblemType eq "Return_Type_From_Void")
13849                {
13850                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13851                    { # parameters stack has been affected
13852                        if($Conv2{"Method"} eq "stack") {
13853                            $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1;
13854                        }
13855                        elsif($Conv2{"Hidden"}) {
13856                            $ProblemTypes{"Return_Type_From_Void_And_Register"} = 1;
13857                        }
13858                    }
13859                }
13860                elsif($SubProblemType eq "Return_Type"
13861                or $SubProblemType eq "Return_Type_And_Size"
13862                or $SubProblemType eq "Return_Type_Format")
13863                {
13864                    if($Conv1{"Method"} ne $Conv2{"Method"})
13865                    {
13866                        if($Conv1{"Method"} eq "stack")
13867                        { # returns in a register instead of a hidden first parameter
13868                            $ProblemTypes{"Return_Type_From_Stack_To_Register"} = 1;
13869                        }
13870                        else {
13871                            $ProblemTypes{"Return_Type_From_Register_To_Stack"} = 1;
13872                        }
13873                    }
13874                    else
13875                    {
13876                        if($Conv1{"Method"} eq "reg")
13877                        {
13878                            if($Conv1{"Registers"} ne $Conv2{"Registers"})
13879                            {
13880                                if($Conv1{"Hidden"}) {
13881                                    $ProblemTypes{"Return_Type_And_Register_Was_Hidden_Parameter"} = 1;
13882                                }
13883                                elsif($Conv2{"Hidden"}) {
13884                                    $ProblemTypes{"Return_Type_And_Register_Became_Hidden_Parameter"} = 1;
13885                                }
13886                                else {
13887                                    $ProblemTypes{"Return_Type_And_Register"} = 1;
13888                                }
13889                            }
13890                        }
13891                    }
13892                }
13893            }
13894
13895            if(not keys(%ProblemTypes))
13896            { # default
13897                $ProblemTypes{$SubProblemType} = 1;
13898            }
13899
13900            foreach my $ProblemType (keys(%ProblemTypes))
13901            { # additional
13902                $CompatProblems{$Level}{$Symbol}{$ProblemType}{"retval"} = $RC_SubProblems{$SubProblemType};
13903            }
13904        }
13905        if($ReturnType1_Id and $ReturnType2_Id)
13906        {
13907            @RecurTypes = ();
13908            my $Sub_SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id, $Level);
13909
13910            my $AddProblems = {};
13911
13912            if($CompleteSignature{1}{$Symbol}{"Data"})
13913            {
13914                if($Level eq "Binary")
13915                {
13916                    if(get_PLevel($ReturnType1_Id, 1)==0)
13917                    {
13918                        if(defined $Sub_SubProblems->{"DataType_Size"})
13919                        { # add "Global_Data_Size" problem
13920
13921                            foreach my $Loc (keys(%{$Sub_SubProblems->{"DataType_Size"}}))
13922                            {
13923                                if(index($Loc,"->")==-1)
13924                                {
13925                                    if($Loc eq $Sub_SubProblems->{"DataType_Size"}{$Loc}{"Type_Name"})
13926                                    {
13927                                        $AddProblems->{"Global_Data_Size"}{$Loc} = $Sub_SubProblems->{"DataType_Size"}{$Loc}; # add a new problem
13928                                        last;
13929                                    }
13930                                }
13931                            }
13932                        }
13933                    }
13934                    if(not defined $AddProblems->{"Global_Data_Size"})
13935                    {
13936                        if(defined $GlobalDataObject{1}{$Symbol}
13937                        and defined $GlobalDataObject{2}{$Symbol})
13938                        {
13939                            my $Old_Size = $GlobalDataObject{1}{$Symbol};
13940                            my $New_Size = $GlobalDataObject{2}{$Symbol};
13941                            if($Old_Size!=$New_Size)
13942                            {
13943                                $AddProblems->{"Global_Data_Size"}{"retval"} = {
13944                                    "Old_Size"=>$Old_Size*$BYTE_SIZE,
13945                                    "New_Size"=>$New_Size*$BYTE_SIZE };
13946                            }
13947                        }
13948                    }
13949                }
13950            }
13951
13952            foreach my $SubProblemType (keys(%{$AddProblems}))
13953            {
13954                foreach my $SubLocation (keys(%{$AddProblems->{$SubProblemType}}))
13955                {
13956                    my $NewLocation = "retval";
13957                    if($SubLocation and $SubLocation ne "retval") {
13958                        $NewLocation = "retval->".$SubLocation;
13959                    }
13960                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $AddProblems->{$SubProblemType}{$SubLocation};
13961                }
13962            }
13963
13964            foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
13965            {
13966                foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
13967                {
13968                    my $NewLocation = "retval";
13969                    if($SubLocation and $SubLocation ne "retval") {
13970                        $NewLocation = "retval->".$SubLocation;
13971                    }
13972                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
13973                }
13974            }
13975        }
13976
13977        # checking object type
13978        my $ObjTId1 = $CompleteSignature{1}{$Symbol}{"Class"};
13979        my $ObjTId2 = $CompleteSignature{2}{$PSymbol}{"Class"};
13980        if($ObjTId1 and $ObjTId2
13981        and not $CompleteSignature{1}{$Symbol}{"Static"})
13982        {
13983            my $ThisPtr1_Id = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1);
13984            my $ThisPtr2_Id = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2);
13985            if($ThisPtr1_Id and $ThisPtr2_Id)
13986            {
13987                @RecurTypes = ();
13988                my $Sub_SubProblems = mergeTypes($ThisPtr1_Id, $ThisPtr2_Id, $Level);
13989                foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
13990                {
13991                    foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
13992                    {
13993                        my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this";
13994                        $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
13995                    }
13996                }
13997            }
13998        }
13999    }
14000    if($Level eq "Binary") {
14001        mergeVTables($Level);
14002    }
14003    foreach my $Symbol (keys(%{$CompatProblems{$Level}})) {
14004        $CheckedSymbols{$Level}{$Symbol} = 1;
14005    }
14006}
14007
14008sub rmQuals($$)
14009{
14010    my ($Value, $Qual) = @_;
14011    if(not $Qual) {
14012        return $Value;
14013    }
14014    if($Qual eq "all")
14015    { # all quals
14016        $Qual = "const|volatile|restrict";
14017    }
14018    while($Value=~s/\b$Qual\b//) {
14019        $Value = formatName($Value, "T");
14020    }
14021    return $Value;
14022}
14023
14024sub cmpBTypes($$$$)
14025{
14026    my ($T1, $T2, $V1, $V2) = @_;
14027    $T1 = uncover_typedefs($T1, $V1);
14028    $T2 = uncover_typedefs($T2, $V2);
14029    return (rmQuals($T1, "all") eq rmQuals($T2, "all"));
14030}
14031
14032sub addedQual($$$)
14033{
14034    my ($Old_Value, $New_Value, $Qual) = @_;
14035    return removedQual_I($New_Value, $Old_Value, 2, 1, $Qual);
14036}
14037
14038sub removedQual($$$)
14039{
14040    my ($Old_Value, $New_Value, $Qual) = @_;
14041    return removedQual_I($Old_Value, $New_Value, 1, 2, $Qual);
14042}
14043
14044sub removedQual_I($$$$$)
14045{
14046    my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_;
14047    $Old_Value = uncover_typedefs($Old_Value, $V1);
14048    $New_Value = uncover_typedefs($New_Value, $V2);
14049    if($Old_Value eq $New_Value)
14050    { # equal types
14051        return 0;
14052    }
14053    if($Old_Value!~/\b$Qual\b/)
14054    { # without a qual
14055        return 0;
14056    }
14057    elsif($New_Value!~/\b$Qual\b/)
14058    { # became non-qual
14059        return 1;
14060    }
14061    else
14062    {
14063        my @BQ1 = getQualModel($Old_Value, $Qual);
14064        my @BQ2 = getQualModel($New_Value, $Qual);
14065        foreach (0 .. $#BQ1)
14066        { # removed qual
14067            if($BQ1[$_]==1
14068            and $BQ2[$_]!=1)
14069            {
14070                return 2;
14071            }
14072        }
14073    }
14074    return 0;
14075}
14076
14077sub getQualModel($$)
14078{
14079    my ($Value, $Qual) = @_;
14080    if(not $Qual) {
14081        return $Value;
14082    }
14083
14084    # cleaning
14085    while($Value=~/(\w+)/ and $1 ne $Qual) {
14086        $Value=~s/\b$1\b//g;
14087    }
14088    $Value=~s/[^\*\&\w]+//g;
14089
14090    # modeling
14091    # int*const*const == 011
14092    # int**const == 001
14093    my @Model = ();
14094    my @Elems = split(/[\*\&]/, $Value);
14095    if(not @Elems) {
14096        return (0);
14097    }
14098    foreach (@Elems)
14099    {
14100        if($_ eq $Qual) {
14101            push(@Model, 1);
14102        }
14103        else {
14104            push(@Model, 0);
14105        }
14106    }
14107
14108    return @Model;
14109}
14110
14111my %StringTypes = map {$_=>1} (
14112    "char*",
14113    "char const*"
14114);
14115
14116my %CharTypes = map {$_=>1} (
14117    "char",
14118    "char const"
14119);
14120
14121sub showVal($$$)
14122{
14123    my ($Value, $TypeId, $LibVersion) = @_;
14124    my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
14125    my $TName = uncover_typedefs($PureType{"Name"}, $LibVersion);
14126    if(substr($Value, 0, 2) eq "_Z")
14127    {
14128        if(my $Unmangled = $tr_name{$Value}) {
14129            return $Unmangled;
14130        }
14131    }
14132    elsif(defined $StringTypes{$TName} or $TName=~/string/i)
14133    { # strings
14134        return "\"$Value\"";
14135    }
14136    elsif(defined $CharTypes{$TName})
14137    { # characters
14138        return "\'$Value\'";
14139    }
14140    if($Value eq "")
14141    { # other
14142        return "\'\'";
14143    }
14144    return $Value;
14145}
14146
14147sub getRegs($$$)
14148{
14149    my ($LibVersion, $Symbol, $Pos) = @_;
14150
14151    if(defined $CompleteSignature{$LibVersion}{$Symbol}{"Reg"})
14152    {
14153        my %Regs = ();
14154        foreach my $Elem (sort keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}}))
14155        {
14156            if($Elem=~/\A$Pos([\.\+]|\Z)/) {
14157                $Regs{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}{$Elem}} = 1;
14158            }
14159        }
14160
14161        return join(", ", sort keys(%Regs));
14162    }
14163    elsif(defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}
14164    and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0}
14165    and not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0}{"offset"})
14166    {
14167        return "unknown";
14168    }
14169
14170    return undef;
14171}
14172
14173sub mergeParameters($$$$$$)
14174{
14175    my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $ChkRnmd) = @_;
14176    if(not $Symbol) {
14177        return;
14178    }
14179    my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"type"};
14180    my $PName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"name"};
14181    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"};
14182    my $PName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"};
14183    if(not $PType1_Id
14184    or not $PType2_Id) {
14185        return;
14186    }
14187
14188    if($Symbol=~/\A(_Z|\?)/)
14189    { # do not merge "this"
14190        if($PName1 eq "this" or $PName2 eq "this") {
14191            return;
14192        }
14193    }
14194
14195    my %Type1 = get_Type($PType1_Id, 1);
14196    my %Type2 = get_Type($PType2_Id, 2);
14197
14198    my %PureType1 = get_PureType($PType1_Id, $TypeInfo{1});
14199
14200    my %BaseType1 = get_BaseType($PType1_Id, 1);
14201    my %BaseType2 = get_BaseType($PType2_Id, 2);
14202
14203    my $Parameter_Location = ($PName1)?$PName1:showPos($ParamPos1)." Parameter";
14204
14205    if($Level eq "Binary")
14206    {
14207        if(checkDump(1, "2.6.1") and checkDump(2, "2.6.1"))
14208        { # "reg" attribute added in ACC 1.95.1 (dump 2.6.1 format)
14209            if($CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14210            and not $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14211            {
14212                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$Parameter_Location}}=(
14213                    "Target"=>$PName1,
14214                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14215            }
14216            elsif(not $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14217            and $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14218            {
14219                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$Parameter_Location}}=(
14220                    "Target"=>$PName1,
14221                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14222            }
14223        }
14224
14225        if(defined $UsedDump{1}{"DWARF"}
14226        and defined $UsedDump{2}{"DWARF"})
14227        {
14228            if(checkDump(1, "3.0") and checkDump(2, "3.0"))
14229            {
14230                my $Old_Regs = getRegs(1, $Symbol, $ParamPos1);
14231                my $New_Regs = getRegs(2, $PSymbol, $ParamPos2);
14232
14233                if($Old_Regs ne "unknown"
14234                and $New_Regs ne "unknown")
14235                {
14236                    if($Old_Regs and $New_Regs)
14237                    {
14238                        if($Old_Regs ne $New_Regs)
14239                        {
14240                            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$Parameter_Location}}=(
14241                                "Target"=>$PName1,
14242                                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14243                                "Old_Value"=>$Old_Regs,
14244                                "New_Value"=>$New_Regs  );
14245                        }
14246                    }
14247                    elsif($Old_Regs and not $New_Regs)
14248                    {
14249                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$Parameter_Location}}=(
14250                            "Target"=>$PName1,
14251                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14252                            "Old_Value"=>$Old_Regs  );
14253                    }
14254                    elsif(not $Old_Regs and $New_Regs)
14255                    {
14256                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$Parameter_Location}}=(
14257                            "Target"=>$PName1,
14258                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14259                            "New_Value"=>$New_Regs  );
14260                    }
14261                }
14262
14263                if((my $Old_Offset = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}) ne ""
14264                and (my $New_Offset = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}) ne "")
14265                {
14266                    if($Old_Offset ne $New_Offset)
14267                    {
14268                        my $Start1 = $CompleteSignature{1}{$Symbol}{"Param"}{0}{"offset"};
14269                        my $Start2 = $CompleteSignature{2}{$Symbol}{"Param"}{0}{"offset"};
14270
14271                        $Old_Offset = $Old_Offset - $Start1;
14272                        $New_Offset = $New_Offset - $Start2;
14273
14274                        if($Old_Offset ne $New_Offset)
14275                        {
14276                            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$Parameter_Location}}=(
14277                                "Target"=>$PName1,
14278                                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14279                                "Old_Value"=>$Old_Offset,
14280                                "New_Value"=>$New_Offset  );
14281                        }
14282                    }
14283                }
14284            }
14285        }
14286    }
14287    if(checkDump(1, "2.0") and checkDump(2, "2.0")
14288    and $UsedDump{1}{"V"} ne "3.1" and $UsedDump{2}{"V"} ne "3.1")
14289    { # "default" attribute added in ACC 1.22 (dump 2.0 format)
14290      # broken in 3.1, fixed in 3.2
14291        my $Value_Old = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"default"};
14292        my $Value_New = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"};
14293        if(not checkDump(1, "2.13")
14294        and checkDump(2, "2.13"))
14295        { # support for old ABI dumps
14296            if(defined $Value_Old and defined $Value_New)
14297            {
14298                if($PureType1{"Name"} eq "bool"
14299                and $Value_Old eq "false" and $Value_New eq "0")
14300                { # int class::method ( bool p = 0 );
14301                  # old ABI dumps: "false"
14302                  # new ABI dumps: "0"
14303                    $Value_Old = "0";
14304                }
14305            }
14306        }
14307        if(not checkDump(1, "2.18")
14308        and checkDump(2, "2.18"))
14309        { # support for old ABI dumps
14310            if(not defined $Value_Old
14311            and substr($Value_New, 0, 2) eq "_Z") {
14312                $Value_Old = $Value_New;
14313            }
14314        }
14315        if(defined $Value_Old)
14316        {
14317            $Value_Old = showVal($Value_Old, $PType1_Id, 1);
14318            if(defined $Value_New)
14319            {
14320                $Value_New = showVal($Value_New, $PType2_Id, 2);
14321                if($Value_Old ne $Value_New)
14322                { # FIXME: how to distinguish "0" and 0 (NULL)
14323                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$Parameter_Location}}=(
14324                        "Target"=>$PName1,
14325                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14326                        "Old_Value"=>$Value_Old,
14327                        "New_Value"=>$Value_New  );
14328                }
14329            }
14330            else
14331            {
14332                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$Parameter_Location}}=(
14333                    "Target"=>$PName1,
14334                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14335                    "Old_Value"=>$Value_Old  );
14336            }
14337        }
14338        elsif(defined $Value_New)
14339        {
14340            $Value_New = showVal($Value_New, $PType2_Id, 2);
14341            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$Parameter_Location}}=(
14342                "Target"=>$PName1,
14343                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14344                "New_Value"=>$Value_New  );
14345        }
14346    }
14347
14348    if($ChkRnmd)
14349    {
14350        if($PName1 and $PName2 and $PName1 ne $PName2
14351        and $PType1_Id!=-1 and $PType2_Id!=-1
14352        and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/)
14353        { # except unnamed "..." value list (Id=-1)
14354            %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=(
14355                "Target"=>$PName1,
14356                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14357                "Param_Type"=>$TypeInfo{1}{$PType1_Id}{"Name"},
14358                "Old_Value"=>$PName1,
14359                "New_Value"=>$PName2,
14360                "New_Signature"=>get_Signature($Symbol, 2)  );
14361        }
14362    }
14363
14364    # checking type change (replace)
14365    my %SubProblems = detectTypeChange($PType1_Id, $PType2_Id, "Parameter", $Level);
14366
14367    foreach my $SubProblemType (keys(%SubProblems))
14368    { # add new problems, remove false alarms
14369        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14370        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14371
14372        # quals
14373        if($SubProblemType eq "Parameter_Type"
14374        or $SubProblemType eq "Parameter_Type_And_Size"
14375        or $SubProblemType eq "Parameter_Type_Format")
14376        {
14377            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14378            {
14379                if(addedQual($Old_Value, $New_Value, "restrict")) {
14380                    %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}};
14381                }
14382                elsif(removedQual($Old_Value, $New_Value, "restrict")) {
14383                    %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}};
14384                }
14385            }
14386            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14387            {
14388                if(removedQual($Old_Value, $New_Value, "volatile")) {
14389                    %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}};
14390                }
14391            }
14392            if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"}
14393            and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14394            { # int to "int const"
14395                delete($SubProblems{$SubProblemType});
14396            }
14397            elsif($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"}
14398            and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14399            { # "int const" to int
14400                delete($SubProblems{$SubProblemType});
14401            }
14402            elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
14403            { # "const" to non-"const"
14404                if($RR==2) {
14405                    %{$SubProblems{"Parameter_Removed_Const"}} = %{$SubProblems{$SubProblemType}};
14406                }
14407                else {
14408                    %{$SubProblems{"Parameter_Became_Non_Const"}} = %{$SubProblems{$SubProblemType}};
14409                }
14410            }
14411        }
14412    }
14413
14414    if($Level eq "Source")
14415    {
14416        foreach my $SubProblemType (keys(%SubProblems))
14417        {
14418            my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14419            my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14420
14421            if($SubProblemType eq "Parameter_Type")
14422            {
14423                if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
14424                    delete($SubProblems{$SubProblemType});
14425                }
14426            }
14427        }
14428    }
14429
14430    foreach my $SubProblemType (keys(%SubProblems))
14431    { # modify/register problems
14432        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14433        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14434        my $New_Size = $SubProblems{$SubProblemType}{"New_Size"};
14435        my $Old_Size = $SubProblems{$SubProblemType}{"Old_Size"};
14436
14437        my $NewProblemType = $SubProblemType;
14438        if($Old_Value eq "..." and $New_Value ne "...")
14439        { # change from "..." to "int"
14440            if($ParamPos1==0)
14441            { # ISO C requires a named argument before "..."
14442                next;
14443            }
14444            $NewProblemType = "Parameter_Became_Non_VaList";
14445        }
14446        elsif($New_Value eq "..." and $Old_Value ne "...")
14447        { # change from "int" to "..."
14448            if($ParamPos2==0)
14449            { # ISO C requires a named argument before "..."
14450                next;
14451            }
14452            $NewProblemType = "Parameter_Became_VaList";
14453        }
14454        elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size"
14455        or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format"))
14456        {
14457            my ($Arch1, $Arch2) = (getArch(1), getArch(2));
14458            if($Arch1 eq "unknown"
14459            or $Arch2 eq "unknown")
14460            { # if one of the architectures is unknown
14461              # then set other arhitecture to unknown too
14462                ($Arch1, $Arch2) = ("unknown", "unknown");
14463            }
14464            my (%Conv1, %Conv2) = ();
14465            if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"})
14466            { # real
14467                %Conv1 = callingConvention_P_Real($CompleteSignature{1}{$Symbol}, $ParamPos1);
14468                %Conv2 = callingConvention_P_Real($CompleteSignature{2}{$Symbol}, $ParamPos2);
14469            }
14470            else
14471            { # model
14472                %Conv1 = callingConvention_P_Model($CompleteSignature{1}{$Symbol}, $ParamPos1, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
14473                %Conv2 = callingConvention_P_Model($CompleteSignature{2}{$Symbol}, $ParamPos2, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
14474            }
14475            if($Conv1{"Method"} eq $Conv2{"Method"})
14476            {
14477                if($Conv1{"Method"} eq "stack")
14478                {
14479                    if($Old_Size ne $New_Size) { # FIXME: isMemPadded, getOffset
14480                        $NewProblemType = "Parameter_Type_And_Stack";
14481                    }
14482                }
14483                elsif($Conv1{"Method"} eq "reg")
14484                {
14485                    if($Conv1{"Registers"} ne $Conv2{"Registers"}) {
14486                        $NewProblemType = "Parameter_Type_And_Register";
14487                    }
14488                }
14489            }
14490            elsif($Conv1{"Method"} ne "unknown"
14491            and $Conv2{"Method"} ne "unknown")
14492            {
14493                if($Conv1{"Method"} eq "stack") {
14494                    $NewProblemType = "Parameter_Type_From_Stack_To_Register";
14495                }
14496                elsif($Conv1{"Method"} eq "register") {
14497                    $NewProblemType = "Parameter_Type_From_Register_To_Stack";
14498                }
14499            }
14500            $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"};
14501            $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"};
14502        }
14503        %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}=(
14504            "Target"=>$PName1,
14505            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14506            "New_Signature"=>get_Signature($Symbol, 2) );
14507        @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
14508    }
14509
14510    @RecurTypes = ();
14511
14512    # checking type definition changes
14513    my $Sub_SubProblems = mergeTypes($PType1_Id, $PType2_Id, $Level);
14514    foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
14515    {
14516        foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
14517        {
14518            my $NewProblemType = $SubProblemType;
14519            if($SubProblemType eq "DataType_Size")
14520            {
14521                if($PureType1{"Type"}!~/\A(Pointer|Ref)\Z/ and $SubLocation!~/\-\>/)
14522                { # stack has been affected
14523                    $NewProblemType = "DataType_Size_And_Stack";
14524                }
14525            }
14526            my $NewLocation = ($SubLocation)?$Parameter_Location."->".$SubLocation:$Parameter_Location;
14527            $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
14528        }
14529    }
14530}
14531
14532sub find_ParamPair_Pos_byName($$$)
14533{
14534    my ($Name, $Symbol, $LibVersion) = @_;
14535    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14536    {
14537        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14538        if($CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name)
14539        {
14540            return $ParamPos;
14541        }
14542    }
14543    return "lost";
14544}
14545
14546sub find_ParamPair_Pos_byTypeAndPos($$$$$)
14547{
14548    my ($TypeName, $MediumPos, $Order, $Symbol, $LibVersion) = @_;
14549    my @Positions = ();
14550    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14551    {
14552        next if($Order eq "backward" and $ParamPos>$MediumPos);
14553        next if($Order eq "forward" and $ParamPos<$MediumPos);
14554        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14555        my $PTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"type"};
14556        if($TypeInfo{$LibVersion}{$PTypeId}{"Name"} eq $TypeName) {
14557            push(@Positions, $ParamPos);
14558        }
14559    }
14560    return @Positions;
14561}
14562
14563sub getTypeIdByName($$)
14564{
14565    my ($TypeName, $LibVersion) = @_;
14566    return $TName_Tid{$LibVersion}{formatName($TypeName, "T")};
14567}
14568
14569sub diffTypes($$$)
14570{
14571    if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) {
14572        return $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]};
14573    }
14574    if(isRecurType($_[0], $_[1], \@RecurTypes_Diff))
14575    { # skip recursive declarations
14576        return 0;
14577    }
14578
14579    pushType($_[0], $_[1], \@RecurTypes_Diff);
14580    my $Diff = diffTypes_I(@_);
14581    pop(@RecurTypes_Diff);
14582
14583    return ($Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]} = $Diff);
14584}
14585
14586sub diffTypes_I($$$)
14587{
14588    my ($Type1_Id, $Type2_Id, $Level) = @_;
14589
14590    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14591    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14592
14593    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
14594    { # equal types
14595        return 0;
14596    }
14597    if($Type1_Pure{"Name"} eq "void")
14598    { # from void* to something
14599        return 0;
14600    }
14601    if($Type1_Pure{"Name"}=~/\*/
14602    or $Type2_Pure{"Name"}=~/\*/)
14603    { # compared in detectTypeChange()
14604        return 0;
14605    }
14606
14607    my %FloatType = map {$_=>1} (
14608        "float",
14609        "double",
14610        "long double"
14611    );
14612
14613    my $T1 = $Type1_Pure{"Type"};
14614    my $T2 = $Type2_Pure{"Type"};
14615
14616    if($T1 eq "Struct"
14617    and $T2 eq "Class")
14618    { # compare as data structures
14619        $T2 = "Struct";
14620    }
14621
14622    if($T1 eq "Class"
14623    and $T2 eq "Struct")
14624    { # compare as data structures
14625        $T1 = "Struct";
14626    }
14627
14628    if($T1 ne $T2)
14629    { # different types
14630        if($T1 eq "Intrinsic"
14631        and $T2 eq "Enum")
14632        { # "int" to "enum"
14633            return 0;
14634        }
14635        elsif($T2 eq "Intrinsic"
14636        and $T1 eq "Enum")
14637        { # "enum" to "int"
14638            return 0;
14639        }
14640        else
14641        { # union to struct
14642          #  ...
14643            return 1;
14644        }
14645    }
14646    else
14647    {
14648        if($T1 eq "Intrinsic")
14649        {
14650            if($FloatType{$Type1_Pure{"Name"}}
14651            or $FloatType{$Type2_Pure{"Name"}})
14652            { # "float" to "double"
14653              # "float" to "int"
14654                if($Level eq "Source")
14655                { # Safe
14656                    return 0;
14657                }
14658                else {
14659                    return 1;
14660                }
14661            }
14662        }
14663        elsif($T1=~/Class|Struct|Union|Enum/)
14664        {
14665            my @Membs1 = keys(%{$Type1_Pure{"Memb"}});
14666            my @Membs2 = keys(%{$Type2_Pure{"Memb"}});
14667            if(not @Membs1
14668            or not @Membs2)
14669            { # private
14670                return 0;
14671            }
14672            if($#Membs1!=$#Membs2)
14673            { # different number of elements
14674                return 1;
14675            }
14676            if($T1 eq "Enum")
14677            {
14678                foreach my $Pos (@Membs1)
14679                { # compare elements by name and value
14680                    if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}
14681                    or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"})
14682                    { # different names
14683                        return 1;
14684                    }
14685                }
14686            }
14687            else
14688            {
14689                foreach my $Pos (@Membs1)
14690                {
14691                    if($Level eq "Source")
14692                    {
14693                        if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"})
14694                        { # different names
14695                            return 1;
14696                        }
14697                    }
14698
14699                    my %MT1 = %{$TypeInfo{1}{$Type1_Pure{"Memb"}{$Pos}{"type"}}};
14700                    my %MT2 = %{$TypeInfo{2}{$Type2_Pure{"Memb"}{$Pos}{"type"}}};
14701
14702                    if($MT1{"Name"} ne $MT2{"Name"}
14703                    or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"}))
14704                    {
14705                        my $PL1 = get_PLevel($MT1{"Tid"}, 1);
14706                        my $PL2 = get_PLevel($MT2{"Tid"}, 2);
14707
14708                        if($PL1 ne $PL2)
14709                        { # different pointer level
14710                            return 1;
14711                        }
14712
14713                        # compare base types
14714                        my %BT1 = get_BaseType($MT1{"Tid"}, 1);
14715                        my %BT2 = get_BaseType($MT2{"Tid"}, 2);
14716
14717                        if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level))
14718                        { # different types
14719                            return 1;
14720                        }
14721                    }
14722                }
14723            }
14724        }
14725        else
14726        {
14727            # TODO: arrays, etc.
14728        }
14729    }
14730    return 0;
14731}
14732
14733sub detectTypeChange($$$$)
14734{
14735    my ($Type1_Id, $Type2_Id, $Prefix, $Level) = @_;
14736    if(not $Type1_Id or not $Type2_Id) {
14737        return ();
14738    }
14739    my %LocalProblems = ();
14740    my %Type1 = get_Type($Type1_Id, 1);
14741    my %Type2 = get_Type($Type2_Id, 2);
14742    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14743    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14744
14745    my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type1_Pure{"Tid"}, $TypeInfo{1}):get_BaseType($Type1_Id, 1);
14746    my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type2_Pure{"Tid"}, $TypeInfo{2}):get_BaseType($Type2_Id, 2);
14747
14748    if(defined $UsedDump{1}{"DWARF"})
14749    {
14750        if($Type1_Pure{"Name"} eq "__unknown__"
14751        or $Type2_Pure{"Name"} eq "__unknown__"
14752        or $Type1_Base{"Name"} eq "__unknown__"
14753        or $Type2_Base{"Name"} eq "__unknown__")
14754        { # Error ABI dump
14755            return ();
14756        }
14757    }
14758
14759    my $Type1_PLevel = get_PLevel($Type1_Id, 1);
14760    my $Type2_PLevel = get_PLevel($Type2_Id, 2);
14761    return () if(not $Type1{"Name"} or not $Type2{"Name"});
14762    return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"});
14763    return () if($Type1_PLevel eq "" or $Type2_PLevel eq "");
14764    if($Type1_Base{"Name"} ne $Type2_Base{"Name"}
14765    and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel
14766    and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void")))
14767    { # base type change
14768        if($Type1{"Name"} eq $Type2{"Name"})
14769        {
14770            if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef")
14771            { # will be reported in mergeTypes() as typedef problem
14772                return ();
14773            }
14774            my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
14775            my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
14776            if(%Typedef_1 and %Typedef_2)
14777            {
14778                if($Typedef_1{"Name"} eq $Typedef_2{"Name"}
14779                and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef")
14780                { # const Typedef
14781                    return ();
14782                }
14783            }
14784        }
14785        if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/)
14786        {
14787            if($Level eq "Binary"
14788            and $Type1_Base{"Size"} and $Type2_Base{"Size"}
14789            and $Type1_Base{"Size"} ne $Type2_Base{"Size"})
14790            {
14791                %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=(
14792                    "Old_Value"=>$Type1_Base{"Name"},
14793                    "New_Value"=>$Type2_Base{"Name"},
14794                    "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14795                    "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14796            }
14797            else
14798            {
14799                if(diffTypes($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Level))
14800                { # format change
14801                    %{$LocalProblems{$Prefix."_BaseType_Format"}}=(
14802                        "Old_Value"=>$Type1_Base{"Name"},
14803                        "New_Value"=>$Type2_Base{"Name"},
14804                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14805                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14806                }
14807                elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
14808                {
14809                    %{$LocalProblems{$Prefix."_BaseType"}}=(
14810                        "Old_Value"=>$Type1_Base{"Name"},
14811                        "New_Value"=>$Type2_Base{"Name"},
14812                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14813                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14814                }
14815            }
14816        }
14817    }
14818    elsif($Type1{"Name"} ne $Type2{"Name"})
14819    { # type change
14820        if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/)
14821        {
14822            if($Prefix eq "Return"
14823            and $Type1_Pure{"Name"} eq "void")
14824            {
14825                %{$LocalProblems{"Return_Type_From_Void"}}=(
14826                    "New_Value"=>$Type2{"Name"},
14827                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14828            }
14829            elsif($Prefix eq "Return"
14830            and $Type2_Pure{"Name"} eq "void")
14831            {
14832                %{$LocalProblems{"Return_Type_Became_Void"}}=(
14833                    "Old_Value"=>$Type1{"Name"},
14834                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE);
14835            }
14836            else
14837            {
14838                if($Level eq "Binary"
14839                and $Type1{"Size"} and $Type2{"Size"}
14840                and $Type1{"Size"} ne $Type2{"Size"})
14841                {
14842                    %{$LocalProblems{$Prefix."_Type_And_Size"}}=(
14843                        "Old_Value"=>$Type1{"Name"},
14844                        "New_Value"=>$Type2{"Name"},
14845                        "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14846                        "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14847                }
14848                else
14849                {
14850                    if(diffTypes($Type1_Id, $Type2_Id, $Level))
14851                    { # format change
14852                        %{$LocalProblems{$Prefix."_Type_Format"}}=(
14853                            "Old_Value"=>$Type1{"Name"},
14854                            "New_Value"=>$Type2{"Name"},
14855                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14856                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14857                    }
14858                    elsif(tNameLock($Type1_Id, $Type2_Id))
14859                    { # FIXME: correct this condition
14860                        %{$LocalProblems{$Prefix."_Type"}}=(
14861                            "Old_Value"=>$Type1{"Name"},
14862                            "New_Value"=>$Type2{"Name"},
14863                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14864                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14865                    }
14866                }
14867            }
14868        }
14869    }
14870    if($Type1_PLevel!=$Type2_PLevel)
14871    {
14872        if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..."
14873        and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...")
14874        {
14875            if($Level eq "Source")
14876            {
14877                %{$LocalProblems{$Prefix."_PointerLevel"}}=(
14878                    "Old_Value"=>$Type1_PLevel,
14879                    "New_Value"=>$Type2_PLevel);
14880            }
14881            else
14882            {
14883                if($Type2_PLevel>$Type1_PLevel)
14884                {
14885                    %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=(
14886                        "Old_Value"=>$Type1_PLevel,
14887                        "New_Value"=>$Type2_PLevel);
14888                }
14889                else
14890                {
14891                    %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=(
14892                        "Old_Value"=>$Type1_PLevel,
14893                        "New_Value"=>$Type2_PLevel);
14894                }
14895            }
14896        }
14897    }
14898    if($Type1_Pure{"Type"} eq "Array"
14899    and $Type1_Pure{"BaseType"})
14900    { # base_type[N] -> base_type[N]
14901      # base_type: older_structure -> typedef to newer_structure
14902        my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix, $Level);
14903        foreach my $SubProblemType (keys(%SubProblems))
14904        {
14905            $SubProblemType=~s/_Type/_BaseType/g;
14906            next if(defined $LocalProblems{$SubProblemType});
14907            foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) {
14908                $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr};
14909            }
14910        }
14911    }
14912    return %LocalProblems;
14913}
14914
14915sub tNameLock($$)
14916{
14917    my ($Tid1, $Tid2) = @_;
14918    my $Changed = 0;
14919    if(differentDumps("G"))
14920    { # different GCC versions
14921        $Changed = 1;
14922    }
14923    elsif(differentDumps("V"))
14924    { # different versions of ABI dumps
14925        if(not checkDump(1, "2.20")
14926        or not checkDump(2, "2.20"))
14927        { # latest names update
14928          # 2.6: added restrict qualifier
14929          # 2.13: added missed typedefs to qualified types
14930          # 2.20: prefix for struct, union and enum types
14931            $Changed = 1;
14932        }
14933    }
14934
14935    my $TN1 = $TypeInfo{1}{$Tid1}{"Name"};
14936    my $TN2 = $TypeInfo{2}{$Tid2}{"Name"};
14937
14938    my $TT1 = $TypeInfo{1}{$Tid1}{"Type"};
14939    my $TT2 = $TypeInfo{2}{$Tid2}{"Type"};
14940
14941    if($Changed)
14942    { # different formats
14943        my %Base1 = get_Type($Tid1, 1);
14944        while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") {
14945            %Base1 = get_OneStep_BaseType($Base1{"Tid"}, $TypeInfo{1});
14946        }
14947        my %Base2 = get_Type($Tid2, 2);
14948        while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") {
14949            %Base2 = get_OneStep_BaseType($Base2{"Tid"}, $TypeInfo{2});
14950        }
14951        my $BName1 = uncover_typedefs($Base1{"Name"}, 1);
14952        my $BName2 = uncover_typedefs($Base2{"Name"}, 2);
14953        if($BName1 eq $BName2)
14954        { # equal base types
14955            return 0;
14956        }
14957
14958        if(not checkDump(1, "2.13")
14959        or not checkDump(2, "2.13"))
14960        { # broken array names in ABI dumps < 2.13
14961            if($TT1 eq "Array"
14962            and $TT2 eq "Array") {
14963                return 0;
14964            }
14965        }
14966
14967        if(not checkDump(1, "2.6")
14968        or not checkDump(2, "2.6"))
14969        { # added restrict attribute in 2.6
14970            if($TN1!~/\brestrict\b/
14971            and $TN2=~/\brestrict\b/) {
14972                return 0;
14973            }
14974        }
14975
14976        if(not checkDump(1, "2.20")
14977        or not checkDump(2, "2.20"))
14978        { # added type prefix in 2.20
14979            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/
14980            or $TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
14981                return 0;
14982            }
14983        }
14984    }
14985    else
14986    {
14987        # typedef struct {...} type_t
14988        # typedef struct type_t {...} type_t
14989        if(index($TN1, " ".$TN2)!=-1)
14990        {
14991            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/) {
14992                return 0;
14993            }
14994        }
14995        if(index($TN2, " ".$TN1)!=-1)
14996        {
14997            if($TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
14998                return 0;
14999            }
15000        }
15001
15002        if($TT1 eq "FuncPtr"
15003        and $TT2 eq "FuncPtr")
15004        {
15005            my $TN1_C = $TN1;
15006            my $TN2_C = $TN2;
15007
15008            $TN1_C=~s/\b(struct|union) //g;
15009            $TN2_C=~s/\b(struct|union) //g;
15010
15011            if($TN1_C eq $TN2_C) {
15012                return 0;
15013            }
15014        }
15015    }
15016
15017    my ($N1, $N2) = ($TN1, $TN2);
15018    $N1=~s/\b(struct|union) //g;
15019    $N2=~s/\b(struct|union) //g;
15020
15021    if($N1 eq $N2)
15022    { # QList<struct QUrl> and QList<QUrl>
15023        return 0;
15024    }
15025
15026    return 1;
15027}
15028
15029sub differentDumps($)
15030{
15031    my $Check = $_[0];
15032    if(defined $Cache{"differentDumps"}{$Check}) {
15033        return $Cache{"differentDumps"}{$Check};
15034    }
15035    if($UsedDump{1}{"V"} and $UsedDump{2}{"V"})
15036    {
15037        if($Check eq "G")
15038        {
15039            if(getGccVersion(1) ne getGccVersion(2))
15040            { # different GCC versions
15041                return ($Cache{"differentDumps"}{$Check}=1);
15042            }
15043        }
15044        if($Check eq "V")
15045        {
15046            if(cmpVersions(formatVersion($UsedDump{1}{"V"}, 2),
15047            formatVersion($UsedDump{2}{"V"}, 2))!=0)
15048            { # different dump versions (skip micro version)
15049                return ($Cache{"differentDumps"}{$Check}=1);
15050            }
15051        }
15052    }
15053    return ($Cache{"differentDumps"}{$Check}=0);
15054}
15055
15056sub formatVersion($$)
15057{ # cut off version digits
15058    my ($V, $Digits) = @_;
15059    my @Elems = split(/\./, $V);
15060    return join(".", splice(@Elems, 0, $Digits));
15061}
15062
15063sub htmlSpecChars($)
15064{
15065    my $Str = $_[0];
15066    if(not $Str) {
15067        return $Str;
15068    }
15069    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
15070    $Str=~s/</&lt;/g;
15071    $Str=~s/\-\>/&#45;&gt;/g; # &minus;
15072    $Str=~s/>/&gt;/g;
15073    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
15074    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
15075    $Str=~s/ /&#160;/g; # &nbsp;
15076    $Str=~s/\@SP\@/ /g;
15077    $Str=~s/\n/<br\/>/g;
15078    $Str=~s/\"/&quot;/g;
15079    $Str=~s/\'/&#39;/g;
15080    return $Str;
15081}
15082
15083sub xmlSpecChars($)
15084{
15085    my $Str = $_[0];
15086    if(not $Str) {
15087        return $Str;
15088    }
15089
15090    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
15091    $Str=~s/</&lt;/g;
15092    $Str=~s/>/&gt;/g;
15093
15094    $Str=~s/\"/&quot;/g;
15095    $Str=~s/\'/&#39;/g;
15096
15097    return $Str;
15098}
15099
15100sub xmlSpecChars_R($)
15101{
15102    my $Str = $_[0];
15103    if(not $Str) {
15104        return $Str;
15105    }
15106
15107    $Str=~s/&amp;/&/g;
15108    $Str=~s/&lt;/</g;
15109    $Str=~s/&gt;/>/g;
15110
15111    $Str=~s/&quot;/"/g;
15112    $Str=~s/&#39;/'/g;
15113
15114    return $Str;
15115}
15116
15117sub black_name($)
15118{
15119    my $Name = $_[0];
15120    return "<span class='iname_b'>".highLight_Signature($Name)."</span>";
15121}
15122
15123sub highLight_Signature($)
15124{
15125    my $Signature = $_[0];
15126    return highLight_Signature_PPos_Italic($Signature, "", 0, 0, 0);
15127}
15128
15129sub highLight_Signature_Italic_Color($)
15130{
15131    my $Signature = $_[0];
15132    return highLight_Signature_PPos_Italic($Signature, "", 1, 1, 1);
15133}
15134
15135sub separate_symbol($)
15136{
15137    my $Symbol = $_[0];
15138    my ($Name, $Spec, $Ver) = ($Symbol, "", "");
15139    if($Symbol=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) {
15140        ($Name, $Spec, $Ver) = ($1, $2, $3);
15141    }
15142    return ($Name, $Spec, $Ver);
15143}
15144
15145sub cut_f_attrs($)
15146{
15147    if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) {
15148        return $2;
15149    }
15150    return "";
15151}
15152
15153sub highLight_Signature_PPos_Italic($$$$$)
15154{
15155    my ($FullSignature, $Param_Pos, $ItalicParams, $ColorParams, $ShowReturn) = @_;
15156    $Param_Pos = "" if(not defined $Param_Pos);
15157    my ($Signature, $VersionSpec, $SymbolVersion) = separate_symbol($FullSignature);
15158    my $Return = "";
15159    if($ShowRetVal and $Signature=~s/([^:]):([^:].+?)\Z/$1/g) {
15160        $Return = $2;
15161    }
15162    my $SCenter = find_center($Signature, "(");
15163    if(not $SCenter)
15164    { # global data
15165        $Signature = htmlSpecChars($Signature);
15166        $Signature=~s!(\[data\])!<span class='attr'>$1</span>!g;
15167        $Signature .= (($SymbolVersion)?"<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>":"");
15168        if($Return and $ShowReturn) {
15169            $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15170        }
15171        return $Signature;
15172    }
15173    my ($Begin, $End) = (substr($Signature, 0, $SCenter), "");
15174    $Begin.=" " if($Begin!~/ \Z/);
15175    $End = cut_f_attrs($Signature);
15176    my @Parts = ();
15177    my ($Short, $Params) = split_Signature($Signature);
15178    my @SParts = separate_Params($Params, 1, 1);
15179    foreach my $Pos (0 .. $#SParts)
15180    {
15181        my $Part = $SParts[$Pos];
15182        $Part=~s/\A\s+|\s+\Z//g;
15183        my ($Part_Styled, $ParamName) = (htmlSpecChars($Part), "");
15184        if($Part=~/\([\*]+(\w+)\)/i) {
15185            $ParamName = $1;#func-ptr
15186        }
15187        elsif($Part=~/(\w+)[\,\)]*\Z/i) {
15188            $ParamName = $1;
15189        }
15190        if(not $ParamName)
15191        {
15192            push(@Parts, $Part_Styled);
15193            next;
15194        }
15195        if($ItalicParams and not $TName_Tid{1}{$Part}
15196        and not $TName_Tid{2}{$Part})
15197        {
15198            my $Style = "param";
15199            if($Param_Pos ne ""
15200            and $Pos==$Param_Pos) {
15201                $Style = "focus_p";
15202            }
15203            elsif($ColorParams) {
15204                $Style = "color_p";
15205            }
15206            $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1<span class=\'$Style\'>$ParamName</span>$2!ig;
15207        }
15208        $Part_Styled=~s/,(\w)/, $1/g;
15209        push(@Parts, $Part_Styled);
15210    }
15211    if(@Parts)
15212    {
15213        foreach my $Num (0 .. $#Parts)
15214        {
15215            if($Num==$#Parts)
15216            { # add ")" to the last parameter
15217                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]." )</span>";
15218            }
15219            elsif(length($Parts[$Num])<=45) {
15220                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]."</span>";
15221            }
15222        }
15223        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;".join(" ", @Parts)."</span>".$End;
15224    }
15225    else {
15226        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;)</span>".$End;
15227    }
15228    if($Return and $ShowReturn) {
15229        $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15230    }
15231    $Signature=~s!\[\]![&#160;]!g;
15232    $Signature=~s!operator=!operator&#160;=!g;
15233    $Signature=~s!(\[in-charge\]|\[not-in-charge\]|\[in-charge-deleting\]|\[static\])!<span class='attr'>$1</span>!g;
15234    if($SymbolVersion) {
15235        $Signature .= "<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>";
15236    }
15237    return $Signature;
15238}
15239
15240sub split_Signature($)
15241{
15242    my $Signature = $_[0];
15243    if(my $ShortName = substr($Signature, 0, find_center($Signature, "(")))
15244    {
15245        $Signature=~s/\A\Q$ShortName\E\(//g;
15246        cut_f_attrs($Signature);
15247        $Signature=~s/\)\Z//;
15248        return ($ShortName, $Signature);
15249    }
15250
15251    # error
15252    return ($Signature, "");
15253}
15254
15255sub separate_Params($$$)
15256{
15257    my ($Params, $Comma, $Sp) = @_;
15258    my @Parts = ();
15259    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15260    my $Part = 0;
15261    foreach my $Pos (0 .. length($Params) - 1)
15262    {
15263        my $S = substr($Params, $Pos, 1);
15264        if(defined $B{$S}) {
15265            $B{$S} += 1;
15266        }
15267        if($S eq "," and
15268        $B{"("}==$B{")"} and $B{"<"}==$B{">"})
15269        {
15270            if($Comma)
15271            { # include comma
15272                $Parts[$Part] .= $S;
15273            }
15274            $Part += 1;
15275        }
15276        else {
15277            $Parts[$Part] .= $S;
15278        }
15279    }
15280    if(not $Sp)
15281    { # remove spaces
15282        foreach (@Parts)
15283        {
15284            s/\A //g;
15285            s/ \Z//g;
15286        }
15287    }
15288    return @Parts;
15289}
15290
15291sub find_center($$)
15292{
15293    my ($Sign, $Target) = @_;
15294    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15295    my $Center = 0;
15296    if($Sign=~s/(operator([^\w\s\(\)]+|\(\)))//g)
15297    { # operators
15298        $Center+=length($1);
15299    }
15300    foreach my $Pos (0 .. length($Sign)-1)
15301    {
15302        my $S = substr($Sign, $Pos, 1);
15303        if($S eq $Target)
15304        {
15305            if($B{"("}==$B{")"}
15306            and $B{"<"}==$B{">"}) {
15307                return $Center;
15308            }
15309        }
15310        if(defined $B{$S}) {
15311            $B{$S}+=1;
15312        }
15313        $Center+=1;
15314    }
15315    return 0;
15316}
15317
15318sub appendFile($$)
15319{
15320    my ($Path, $Content) = @_;
15321    return if(not $Path);
15322    if(my $Dir = get_dirname($Path)) {
15323        mkpath($Dir);
15324    }
15325    open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n");
15326    print FILE $Content;
15327    close(FILE);
15328}
15329
15330sub writeFile($$)
15331{
15332    my ($Path, $Content) = @_;
15333    return if(not $Path);
15334    if(my $Dir = get_dirname($Path)) {
15335        mkpath($Dir);
15336    }
15337    open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n");
15338    print FILE $Content;
15339    close(FILE);
15340}
15341
15342sub readFile($)
15343{
15344    my $Path = $_[0];
15345    return "" if(not $Path or not -f $Path);
15346    open(FILE, $Path);
15347    local $/ = undef;
15348    my $Content = <FILE>;
15349    close(FILE);
15350    if($Path!~/\.(tu|class|abi)\Z/) {
15351        $Content=~s/\r/\n/g;
15352    }
15353    return $Content;
15354}
15355
15356sub get_filename($)
15357{ # much faster than basename() from File::Basename module
15358    if(defined $Cache{"get_filename"}{$_[0]}) {
15359        return $Cache{"get_filename"}{$_[0]};
15360    }
15361    if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
15362        return ($Cache{"get_filename"}{$_[0]}=$1);
15363    }
15364    return ($Cache{"get_filename"}{$_[0]}="");
15365}
15366
15367sub get_dirname($)
15368{ # much faster than dirname() from File::Basename module
15369    if(defined $Cache{"get_dirname"}{$_[0]}) {
15370        return $Cache{"get_dirname"}{$_[0]};
15371    }
15372    if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
15373        return ($Cache{"get_dirname"}{$_[0]}=$1);
15374    }
15375    return ($Cache{"get_dirname"}{$_[0]}="");
15376}
15377
15378sub separate_path($) {
15379    return (get_dirname($_[0]), get_filename($_[0]));
15380}
15381
15382sub esc($)
15383{
15384    my $Str = $_[0];
15385    $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g;
15386    return $Str;
15387}
15388
15389sub readLineNum($$)
15390{
15391    my ($Path, $Num) = @_;
15392    return "" if(not $Path or not -f $Path);
15393    open(FILE, $Path);
15394    foreach (1 ... $Num) {
15395        <FILE>;
15396    }
15397    my $Line = <FILE>;
15398    close(FILE);
15399    return $Line;
15400}
15401
15402sub readAttributes($$)
15403{
15404    my ($Path, $Num) = @_;
15405    return () if(not $Path or not -f $Path);
15406    my %Attributes = ();
15407    if(readLineNum($Path, $Num)=~/<!--\s+(.+)\s+-->/)
15408    {
15409        foreach my $AttrVal (split(/;/, $1))
15410        {
15411            if($AttrVal=~/(.+):(.+)/)
15412            {
15413                my ($Name, $Value) = ($1, $2);
15414                $Attributes{$Name} = $Value;
15415            }
15416        }
15417    }
15418    return \%Attributes;
15419}
15420
15421sub is_abs($) {
15422    return ($_[0]=~/\A(\/|\w+:[\/\\])/);
15423}
15424
15425sub get_abs_path($)
15426{ # abs_path() should NOT be called for absolute inputs
15427  # because it can change them
15428    my $Path = $_[0];
15429    if(not is_abs($Path)) {
15430        $Path = abs_path($Path);
15431    }
15432    return $Path;
15433}
15434
15435sub get_OSgroup()
15436{
15437    my $N = $Config{"osname"};
15438    if($N=~/macos|darwin|rhapsody/i) {
15439        return "macos";
15440    }
15441    elsif($N=~/freebsd|openbsd|netbsd/i) {
15442        return "bsd";
15443    }
15444    elsif($N=~/haiku|beos/i) {
15445        return "beos";
15446    }
15447    elsif($N=~/symbian|epoc/i) {
15448        return "symbian";
15449    }
15450    elsif($N=~/win/i) {
15451        return "windows";
15452    }
15453    else {
15454        return $N;
15455    }
15456}
15457
15458sub getGccVersion($)
15459{
15460    my $LibVersion = $_[0];
15461    if($GCC_VERSION{$LibVersion})
15462    { # dump version
15463        return $GCC_VERSION{$LibVersion};
15464    }
15465    elsif($UsedDump{$LibVersion}{"V"})
15466    { # old-version dumps
15467        return "unknown";
15468    }
15469    my $GccVersion = get_dumpversion($GCC_PATH); # host version
15470    if(not $GccVersion) {
15471        return "unknown";
15472    }
15473    return $GccVersion;
15474}
15475
15476sub showArch($)
15477{
15478    my $Arch = $_[0];
15479    if($Arch eq "arm"
15480    or $Arch eq "mips") {
15481        return uc($Arch);
15482    }
15483    return $Arch;
15484}
15485
15486sub getArch($)
15487{
15488    my $LibVersion = $_[0];
15489
15490    if($TargetArch) {
15491        return $TargetArch;
15492    }
15493    elsif($CPU_ARCH{$LibVersion})
15494    { # dump
15495        return $CPU_ARCH{$LibVersion};
15496    }
15497    elsif($UsedDump{$LibVersion}{"V"})
15498    { # old-version dumps
15499        return "unknown";
15500    }
15501
15502    return getArch_GCC($LibVersion);
15503}
15504
15505sub get_Report_Title($)
15506{
15507    my $Level = $_[0];
15508
15509    my $ArchInfo = " on <span style='color:Blue;'>".showArch(getArch(1))."</span>";
15510    if(getArch(1) ne getArch(2)
15511    or getArch(1) eq "unknown"
15512    or $Level eq "Source")
15513    { # don't show architecture in the header
15514        $ArchInfo="";
15515    }
15516    my $Title = "";
15517    if($Level eq "Source") {
15518        $Title .= "Source compatibility";
15519    }
15520    elsif($Level eq "Binary") {
15521        $Title .= "Binary compatibility";
15522    }
15523    else {
15524        $Title .= "API compatibility";
15525    }
15526
15527    my $V1 = $Descriptor{1}{"Version"};
15528    my $V2 = $Descriptor{2}{"Version"};
15529
15530    if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
15531    {
15532        my $M1 = $UsedDump{1}{"M"};
15533        my $M2 = $UsedDump{2}{"M"};
15534
15535        my $M1S = $M1;
15536        my $M2S = $M2;
15537
15538        $M1S=~s/(\.so|\.ko)\..+/$1/ig;
15539        $M2S=~s/(\.so|\.ko)\..+/$1/ig;
15540
15541        if($M1S eq $M2S
15542        and $V1 ne "X" and $V2 ne "Y")
15543        {
15544            $Title .= " report for the <span style='color:Blue;'>$M1S</span> $TargetComponent";
15545            $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
15546        }
15547        else
15548        {
15549            $Title .= " report between <span style='color:Blue;'>$M1</span> (<span style='color:Red;'>".$V1."</span>)";
15550            $Title .= " and <span style='color:Blue;'>$M2</span> (<span style='color:Red;'>".$V2."</span>) objects";
15551        }
15552    }
15553    else
15554    {
15555        $Title .= " report for the <span style='color:Blue;'>$TargetTitle</span> $TargetComponent";
15556        $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
15557    }
15558
15559    $Title .= $ArchInfo;
15560
15561    if($AppPath) {
15562        $Title .= " (relating to the portability of application <span style='color:Blue;'>".get_filename($AppPath)."</span>)";
15563    }
15564    $Title = "<h1>".$Title."</h1>\n";
15565    return $Title;
15566}
15567
15568sub get_CheckedHeaders($)
15569{
15570    my $LibVersion = $_[0];
15571
15572    my @Headers = ();
15573
15574    foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
15575    {
15576        my $File = get_filename($Path);
15577
15578        if(not is_target_header($File, $LibVersion)) {
15579            next;
15580        }
15581
15582        if(skipHeader($File, $LibVersion)) {
15583            next;
15584        }
15585
15586        push(@Headers, $Path);
15587    }
15588
15589    return @Headers;
15590}
15591
15592sub get_SourceInfo()
15593{
15594    my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
15595
15596    if(my @Headers = get_CheckedHeaders(1))
15597    {
15598        $CheckedHeaders = "<a name='Headers'></a><h2>Header Files (".($#Headers+1).")</h2><hr/>\n";
15599        $CheckedHeaders .= "<div class='h_list'>\n";
15600        foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
15601        {
15602            my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"};
15603            my $Name = get_filename($Identity);
15604            my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15605            $CheckedHeaders .= $Name.$Comment."<br/>\n";
15606        }
15607        $CheckedHeaders .= "</div>\n";
15608        $CheckedHeaders .= "<br/>$TOP_REF<br/>\n";
15609    }
15610
15611    if(my @Sources = keys(%{$Registered_Sources{1}}))
15612    {
15613        $CheckedSources = "<a name='Sources'></a><h2>Source Files (".($#Sources+1).")</h2><hr/>\n";
15614        $CheckedSources .= "<div class='h_list'>\n";
15615        foreach my $Header_Path (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
15616        {
15617            my $Identity = $Registered_Sources{1}{$Header_Path}{"Identity"};
15618            my $Name = get_filename($Identity);
15619            my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15620            $CheckedSources .= $Name.$Comment."<br/>\n";
15621        }
15622        $CheckedSources .= "</div>\n";
15623        $CheckedSources .= "<br/>$TOP_REF<br/>\n";
15624    }
15625
15626    if(not $CheckHeadersOnly)
15627    {
15628        $CheckedLibs = "<a name='Libs'></a><h2>".get_ObjTitle()." (".keys(%{$Library_Symbol{1}}).")</h2><hr/>\n";
15629        $CheckedLibs .= "<div class='lib_list'>\n";
15630        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}}))
15631        {
15632            # $Library .= " (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
15633            $CheckedLibs .= $Library."<br/>\n";
15634        }
15635        $CheckedLibs .= "</div>\n";
15636        $CheckedLibs .= "<br/>$TOP_REF<br/>\n";
15637    }
15638
15639    return $CheckedHeaders.$CheckedSources.$CheckedLibs;
15640}
15641
15642sub get_ObjTitle()
15643{
15644    if(defined $UsedDump{1}{"DWARF"}) {
15645        return "Objects";
15646    }
15647    else {
15648        return ucfirst($SLIB_TYPE)." Libraries";
15649    }
15650}
15651
15652sub get_TypeProblems_Count($$$)
15653{
15654    my ($TypeChanges, $TargetPriority, $Level) = @_;
15655    my $Type_Problems_Count = 0;
15656    foreach my $Type_Name (sort keys(%{$TypeChanges}))
15657    {
15658        my %Kinds_Target = ();
15659        foreach my $Kind (keys(%{$TypeChanges->{$Type_Name}}))
15660        {
15661            foreach my $Location (keys(%{$TypeChanges->{$Type_Name}{$Kind}}))
15662            {
15663                my $Target = $TypeChanges->{$Type_Name}{$Kind}{$Location}{"Target"};
15664                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15665                next if($Severity ne $TargetPriority);
15666                if($Kinds_Target{$Kind}{$Target}) {
15667                    next;
15668                }
15669
15670                if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target})
15671                {
15672                    if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
15673                    { # select a problem with the highest priority
15674                        next;
15675                    }
15676                }
15677                $Kinds_Target{$Kind}{$Target} = 1;
15678                $Type_Problems_Count += 1;
15679            }
15680        }
15681    }
15682    return $Type_Problems_Count;
15683}
15684
15685sub get_Summary($)
15686{
15687    my $Level = $_[0];
15688    my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
15689    $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);
15690    %{$RESULT{$Level}} = (
15691        "Problems"=>0,
15692        "Warnings"=>0,
15693        "Affected"=>0 );
15694    # check rules
15695    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15696    {
15697        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
15698        {
15699            if(not defined $CompatRules{$Level}{$Kind})
15700            { # unknown rule
15701                if(not $UnknownRules{$Level}{$Kind})
15702                { # only one warning
15703                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15704                    $UnknownRules{$Level}{$Kind}=1;
15705                }
15706                delete($CompatProblems{$Level}{$Interface}{$Kind});
15707            }
15708        }
15709    }
15710    foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
15711    {
15712        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
15713        {
15714            if(not defined $CompatRules{$Level}{$Kind})
15715            { # unknown rule
15716                if(not $UnknownRules{$Level}{$Kind})
15717                { # only one warning
15718                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15719                    $UnknownRules{$Level}{$Kind}=1;
15720                }
15721                delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
15722            }
15723        }
15724    }
15725    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15726    {
15727        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
15728        {
15729            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
15730            {
15731                foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
15732                {
15733                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15734                    if($Kind eq "Added_Symbol") {
15735                        $Added += 1;
15736                    }
15737                    elsif($Kind eq "Removed_Symbol")
15738                    {
15739                        $Removed += 1;
15740                        $TotalAffected{$Level}{$Interface} = $Severity;
15741                    }
15742                    else
15743                    {
15744                        if($Severity eq "Safe") {
15745                            $I_Other += 1;
15746                        }
15747                        elsif($Severity eq "High") {
15748                            $I_Problems_High += 1;
15749                        }
15750                        elsif($Severity eq "Medium") {
15751                            $I_Problems_Medium += 1;
15752                        }
15753                        elsif($Severity eq "Low") {
15754                            $I_Problems_Low += 1;
15755                        }
15756                        if(($Severity ne "Low" or $StrictCompat)
15757                        and $Severity ne "Safe") {
15758                            $TotalAffected{$Level}{$Interface} = $Severity;
15759                        }
15760                    }
15761                }
15762            }
15763        }
15764    }
15765    my %TypeChanges = ();
15766    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15767    {
15768        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
15769        {
15770            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
15771            {
15772                foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
15773                {
15774                    my $Type_Name = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
15775                    my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
15776                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15777                    my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target};
15778
15779                    if($MaxSeverity and $Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
15780                    { # select a problem with the highest priority
15781                        next;
15782                    }
15783
15784                    if(($Severity ne "Low" or $StrictCompat)
15785                    and $Severity ne "Safe")
15786                    {
15787                        if(defined $TotalAffected{$Level}{$Interface})
15788                        {
15789                            if($Severity_Val{$Severity}>$Severity_Val{$TotalAffected{$Level}{$Interface}}) {
15790                                $TotalAffected{$Level}{$Interface} = $Severity;
15791                            }
15792                        }
15793                        else {
15794                            $TotalAffected{$Level}{$Interface} = $Severity;
15795                        }
15796                    }
15797
15798                    $TypeChanges{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
15799
15800                    if($MaxSeverity)
15801                    {
15802                        if($Severity_Val{$Severity}>$Severity_Val{$MaxSeverity}) {
15803                            $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity;
15804                        }
15805                    }
15806                    else {
15807                        $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity;
15808                    }
15809                }
15810            }
15811        }
15812    }
15813
15814    $T_Problems_High = get_TypeProblems_Count(\%TypeChanges, "High", $Level);
15815    $T_Problems_Medium = get_TypeProblems_Count(\%TypeChanges, "Medium", $Level);
15816    $T_Problems_Low = get_TypeProblems_Count(\%TypeChanges, "Low", $Level);
15817    $T_Other = get_TypeProblems_Count(\%TypeChanges, "Safe", $Level);
15818
15819    %TypeChanges = (); # free memory
15820
15821    # changed and removed public symbols
15822    my $SCount = keys(%{$CheckedSymbols{$Level}});
15823    if($ExtendedCheck)
15824    { # don't count external_func_0 for constants
15825        $SCount-=1;
15826    }
15827    if($SCount)
15828    {
15829        my %Weight = (
15830            "High" => 100,
15831            "Medium" => 50,
15832            "Low" => 25
15833        );
15834        foreach (keys(%{$TotalAffected{$Level}})) {
15835            $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
15836        }
15837        $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
15838    }
15839    else {
15840        $RESULT{$Level}{"Affected"} = 0;
15841    }
15842
15843    $RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
15844    if($RESULT{$Level}{"Affected"}>=100) {
15845        $RESULT{$Level}{"Affected"} = 100;
15846    }
15847
15848    $RESULT{$Level}{"Problems"} += $Removed;
15849    $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
15850    $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
15851    if($StrictCompat) {
15852        $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
15853    }
15854    else {
15855        $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
15856    }
15857
15858    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
15859    {
15860        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
15861        {
15862            my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15863            if($Severity eq "Safe")
15864            {
15865                $C_Other+=1;
15866            }
15867            elsif($Severity eq "Low")
15868            {
15869                $C_Problems_Low+=1;
15870            }
15871        }
15872    }
15873
15874    if($C_Problems_Low)
15875    {
15876        if($StrictCompat) {
15877            $RESULT{$Level}{"Problems"} += $C_Problems_Low;
15878        }
15879        else {
15880            $RESULT{$Level}{"Warnings"} += $C_Problems_Low;
15881        }
15882    }
15883    if($RESULT{$Level}{"Problems"}
15884    and $RESULT{$Level}{"Affected"}) {
15885        $RESULT{$Level}{"Verdict"} = "incompatible";
15886    }
15887    else {
15888        $RESULT{$Level}{"Verdict"} = "compatible";
15889    }
15890
15891    my $TotalTypes = keys(%{$CheckedTypes{$Level}});
15892    if(not $TotalTypes)
15893    { # list all the types
15894        $TotalTypes = keys(%{$TName_Tid{1}});
15895    }
15896
15897    my ($Arch1, $Arch2) = (getArch(1), getArch(2));
15898    my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2));
15899
15900    my ($TestInfo, $TestResults, $Problem_Summary) = ();
15901
15902    if($ReportFormat eq "xml")
15903    { # XML
15904        # test info
15905        $TestInfo .= "  <library>$TargetLibraryName</library>\n";
15906        $TestInfo .= "  <version1>\n";
15907        $TestInfo .= "    <number>".$Descriptor{1}{"Version"}."</number>\n";
15908        $TestInfo .= "    <arch>$Arch1</arch>\n";
15909        $TestInfo .= "    <gcc>$GccV1</gcc>\n";
15910        $TestInfo .= "  </version1>\n";
15911
15912        $TestInfo .= "  <version2>\n";
15913        $TestInfo .= "    <number>".$Descriptor{2}{"Version"}."</number>\n";
15914        $TestInfo .= "    <arch>$Arch2</arch>\n";
15915        $TestInfo .= "    <gcc>$GccV2</gcc>\n";
15916        $TestInfo .= "  </version2>\n";
15917        $TestInfo = "<test_info>\n".$TestInfo."</test_info>\n\n";
15918
15919        # test results
15920        if(my @Headers = keys(%{$Registered_Headers{1}}))
15921        {
15922            $TestResults .= "  <headers>\n";
15923            foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
15924            {
15925                my $Identity = $Registered_Headers{1}{$Name}{"Identity"};
15926                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15927                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
15928            }
15929            $TestResults .= "  </headers>\n";
15930        }
15931
15932        if(my @Sources = keys(%{$Registered_Sources{1}}))
15933        {
15934            $TestResults .= "  <sources>\n";
15935            foreach my $Name (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
15936            {
15937                my $Identity = $Registered_Sources{1}{$Name}{"Identity"};
15938                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15939                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
15940            }
15941            $TestResults .= "  </sources>\n";
15942        }
15943
15944        $TestResults .= "  <libs>\n";
15945        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}}))
15946        {
15947            # $Library .= " (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
15948            $TestResults .= "    <name>$Library</name>\n";
15949        }
15950        $TestResults .= "  </libs>\n";
15951
15952        $TestResults .= "  <symbols>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))."</symbols>\n";
15953        $TestResults .= "  <types>".$TotalTypes."</types>\n";
15954
15955        $TestResults .= "  <verdict>".$RESULT{$Level}{"Verdict"}."</verdict>\n";
15956        $TestResults .= "  <affected>".$RESULT{$Level}{"Affected"}."</affected>\n";
15957        $TestResults = "<test_results>\n".$TestResults."</test_results>\n\n";
15958
15959        # problem summary
15960        $Problem_Summary .= "  <added_symbols>".$Added."</added_symbols>\n";
15961        $Problem_Summary .= "  <removed_symbols>".$Removed."</removed_symbols>\n";
15962
15963        $Problem_Summary .= "  <problems_with_types>\n";
15964        $Problem_Summary .= "    <high>$T_Problems_High</high>\n";
15965        $Problem_Summary .= "    <medium>$T_Problems_Medium</medium>\n";
15966        $Problem_Summary .= "    <low>$T_Problems_Low</low>\n";
15967        $Problem_Summary .= "    <safe>$T_Other</safe>\n";
15968        $Problem_Summary .= "  </problems_with_types>\n";
15969
15970        $Problem_Summary .= "  <problems_with_symbols>\n";
15971        $Problem_Summary .= "    <high>$I_Problems_High</high>\n";
15972        $Problem_Summary .= "    <medium>$I_Problems_Medium</medium>\n";
15973        $Problem_Summary .= "    <low>$I_Problems_Low</low>\n";
15974        $Problem_Summary .= "    <safe>$I_Other</safe>\n";
15975        $Problem_Summary .= "  </problems_with_symbols>\n";
15976
15977        $Problem_Summary .= "  <problems_with_constants>\n";
15978        $Problem_Summary .= "    <low>$C_Problems_Low</low>\n";
15979        $Problem_Summary .= "  </problems_with_constants>\n";
15980
15981        $Problem_Summary = "<problem_summary>\n".$Problem_Summary."</problem_summary>\n\n";
15982
15983        return ($TestInfo.$TestResults.$Problem_Summary, "");
15984    }
15985    else
15986    { # HTML
15987        # test info
15988        $TestInfo = "<h2>Test Info</h2><hr/>\n";
15989        $TestInfo .= "<table class='summary'>\n";
15990
15991        if($TargetComponent eq "library") {
15992            $TestInfo .= "<tr><th>Library Name</th><td>$TargetTitle</td></tr>\n";
15993        }
15994        else {
15995            $TestInfo .= "<tr><th>Module Name</th><td>$TargetTitle</td></tr>\n";
15996        }
15997
15998        my (@VInf1, @VInf2, $AddTestInfo) = ();
15999        if($Arch1 ne "unknown"
16000        and $Arch2 ne "unknown")
16001        { # CPU arch
16002            if($Arch1 eq $Arch2)
16003            { # go to the separate section
16004                $AddTestInfo .= "<tr><th>CPU Type</th><td>".showArch($Arch1)."</td></tr>\n";
16005            }
16006            else
16007            { # go to the version number
16008                push(@VInf1, showArch($Arch1));
16009                push(@VInf2, showArch($Arch2));
16010            }
16011        }
16012        if($GccV1 ne "unknown"
16013        and $GccV2 ne "unknown"
16014        and $OStarget ne "windows")
16015        { # GCC version
16016            if($GccV1 eq $GccV2)
16017            { # go to the separate section
16018                $AddTestInfo .= "<tr><th>GCC Version</th><td>$GccV1</td></tr>\n";
16019            }
16020            else
16021            { # go to the version number
16022                push(@VInf1, "gcc ".$GccV1);
16023                push(@VInf2, "gcc ".$GccV2);
16024            }
16025        }
16026        # show long version names with GCC version and CPU architecture name (if different)
16027        $TestInfo .= "<tr><th>Version #1</th><td>".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."</td></tr>\n";
16028        $TestInfo .= "<tr><th>Version #2</th><td>".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."</td></tr>\n";
16029        $TestInfo .= $AddTestInfo;
16030        #if($COMMON_LANGUAGE{1}) {
16031        #    $TestInfo .= "<tr><th>Language</th><td>".$COMMON_LANGUAGE{1}."</td></tr>\n";
16032        #}
16033        if($ExtendedCheck) {
16034            $TestInfo .= "<tr><th>Mode</th><td>Extended</td></tr>\n";
16035        }
16036        if($JoinReport)
16037        {
16038            if($Level eq "Binary") {
16039                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
16040            }
16041            if($Level eq "Source") {
16042                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
16043            }
16044        }
16045        $TestInfo .= "</table>\n";
16046
16047        # test results
16048        $TestResults = "<h2>Test Results</h2><hr/>\n";
16049        $TestResults .= "<table class='summary'>";
16050
16051        if(my @Headers = get_CheckedHeaders(1))
16052        {
16053            my $Headers_Link = "<a href='#Headers' style='color:Blue;'>".($#Headers + 1)."</a>";
16054            $TestResults .= "<tr><th>Total Header Files</th><td>".$Headers_Link."</td></tr>\n";
16055        }
16056
16057        if(my @Sources = keys(%{$Registered_Sources{1}}))
16058        {
16059            my $Src_Link = "<a href='#Sources' style='color:Blue;'>".($#Sources + 1)."</a>";
16060            $TestResults .= "<tr><th>Total Source Files</th><td>".$Src_Link."</td></tr>\n";
16061        }
16062
16063        if(not $ExtendedCheck)
16064        {
16065            my $Libs_Link = "0";
16066            $Libs_Link = "<a href='#Libs' style='color:Blue;'>".keys(%{$Library_Symbol{1}})."</a>" if(keys(%{$Library_Symbol{1}})>0);
16067            $TestResults .= "<tr><th>Total ".get_ObjTitle()."</th><td>".($CheckHeadersOnly?"0&#160;(not&#160;analyzed)":$Libs_Link)."</td></tr>\n";
16068        }
16069
16070        $TestResults .= "<tr><th>Total Symbols / Types</th><td>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))." / ".$TotalTypes."</td></tr>\n";
16071
16072        my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
16073        if($JoinReport) {
16074            $META_DATA = "kind:".lc($Level).";".$META_DATA;
16075        }
16076        $TestResults .= "<tr><th>Verdict</th>";
16077        if($RESULT{$Level}{"Verdict"} eq "incompatible") {
16078            $TestResults .= "<td><span style='color:Red;'><b>Incompatible<br/>(".$RESULT{$Level}{"Affected"}."%)</b></span></td>";
16079        }
16080        else {
16081            $TestResults .= "<td><span style='color:Green;'><b>Compatible</b></span></td>";
16082        }
16083        $TestResults .= "</tr>\n";
16084        $TestResults .= "</table>\n";
16085
16086        $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
16087        # problem summary
16088        $Problem_Summary = "<h2>Problem Summary</h2><hr/>\n";
16089        $Problem_Summary .= "<table class='summary'>";
16090        $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
16091
16092        my $Added_Link = "0";
16093        if($Added>0)
16094        {
16095            if($JoinReport) {
16096                $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
16097            }
16098            else {
16099                $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
16100            }
16101        }
16102        $META_DATA .= "added:$Added;";
16103        $Problem_Summary .= "<tr><th>Added Symbols</th><td>-</td><td".getStyle("I", "A", $Added).">$Added_Link</td></tr>\n";
16104
16105        my $Removed_Link = "0";
16106        if($Removed>0)
16107        {
16108            if($JoinReport) {
16109                $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
16110            }
16111            else {
16112                $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
16113            }
16114        }
16115        $META_DATA .= "removed:$Removed;";
16116        $Problem_Summary .= "<tr><th>Removed Symbols</th>";
16117        $Problem_Summary .= "<td>High</td><td".getStyle("I", "R", $Removed).">$Removed_Link</td></tr>\n";
16118
16119        my $TH_Link = "0";
16120        $TH_Link = "<a href='#".get_Anchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
16121        $META_DATA .= "type_problems_high:$T_Problems_High;";
16122        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
16123        $Problem_Summary .= "<td>High</td><td".getStyle("T", "H", $T_Problems_High).">$TH_Link</td></tr>\n";
16124
16125        my $TM_Link = "0";
16126        $TM_Link = "<a href='#".get_Anchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
16127        $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
16128        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "M", $T_Problems_Medium).">$TM_Link</td></tr>\n";
16129
16130        my $TL_Link = "0";
16131        $TL_Link = "<a href='#".get_Anchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
16132        $META_DATA .= "type_problems_low:$T_Problems_Low;";
16133        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "L", $T_Problems_Low).">$TL_Link</td></tr>\n";
16134
16135        my $IH_Link = "0";
16136        $IH_Link = "<a href='#".get_Anchor("Symbol", $Level, "High")."' style='color:Blue;'>$I_Problems_High</a>" if($I_Problems_High>0);
16137        $META_DATA .= "interface_problems_high:$I_Problems_High;";
16138        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Symbols</th>";
16139        $Problem_Summary .= "<td>High</td><td".getStyle("I", "H", $I_Problems_High).">$IH_Link</td></tr>\n";
16140
16141        my $IM_Link = "0";
16142        $IM_Link = "<a href='#".get_Anchor("Symbol", $Level, "Medium")."' style='color:Blue;'>$I_Problems_Medium</a>" if($I_Problems_Medium>0);
16143        $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
16144        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("I", "M", $I_Problems_Medium).">$IM_Link</td></tr>\n";
16145
16146        my $IL_Link = "0";
16147        $IL_Link = "<a href='#".get_Anchor("Symbol", $Level, "Low")."' style='color:Blue;'>$I_Problems_Low</a>" if($I_Problems_Low>0);
16148        $META_DATA .= "interface_problems_low:$I_Problems_Low;";
16149        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("I", "L", $I_Problems_Low).">$IL_Link</td></tr>\n";
16150
16151        my $ChangedConstants_Link = "0";
16152        if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
16153            $ChangedConstants_Link = "<a href='#".get_Anchor("Constant", $Level, "Low")."' style='color:Blue;'>$C_Problems_Low</a>";
16154        }
16155        $META_DATA .= "changed_constants:$C_Problems_Low;";
16156        $Problem_Summary .= "<tr><th>Problems with<br/>Constants</th><td>Low</td><td".getStyle("C", "L", $C_Problems_Low).">$ChangedConstants_Link</td></tr>\n";
16157
16158        # Safe Changes
16159        if($T_Other)
16160        {
16161            my $TS_Link = "<a href='#".get_Anchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
16162            $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "S", $T_Other).">$TS_Link</td></tr>\n";
16163            $META_DATA .= "type_changes_other:$T_Other;";
16164        }
16165
16166        if($I_Other)
16167        {
16168            my $IS_Link = "<a href='#".get_Anchor("Symbol", $Level, "Safe")."' style='color:Blue;'>$I_Other</a>";
16169            $Problem_Summary .= "<tr><th>Other Changes<br/>in Symbols</th><td>-</td><td".getStyle("I", "S", $I_Other).">$IS_Link</td></tr>\n";
16170            $META_DATA .= "interface_changes_other:$I_Other;";
16171        }
16172
16173        if($C_Other)
16174        {
16175            my $CS_Link = "<a href='#".get_Anchor("Constant", $Level, "Safe")."' style='color:Blue;'>$C_Other</a>";
16176            $Problem_Summary .= "<tr><th>Other Changes<br/>in Constants</th><td>-</td><td".getStyle("C", "S", $C_Other).">$CS_Link</td></tr>\n";
16177            $META_DATA .= "constant_changes_other:$C_Other;";
16178        }
16179
16180        $META_DATA .= "tool_version:$TOOL_VERSION";
16181        $Problem_Summary .= "</table>\n";
16182        # $TestInfo = getLegend().$TestInfo;
16183        return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
16184    }
16185}
16186
16187sub getStyle($$$)
16188{
16189    my ($Subj, $Act, $Num) = @_;
16190    my %Style = (
16191        "A"=>"new",
16192        "R"=>"failed",
16193        "S"=>"passed",
16194        "L"=>"warning",
16195        "M"=>"failed",
16196        "H"=>"failed"
16197    );
16198    if($Num>0) {
16199        return " class='".$Style{$Act}."'";
16200    }
16201    return "";
16202}
16203
16204sub show_number($)
16205{
16206    if($_[0])
16207    {
16208        my $Num = cut_off_number($_[0], 2, 0);
16209        if($Num eq "0")
16210        {
16211            foreach my $P (3 .. 7)
16212            {
16213                $Num = cut_off_number($_[0], $P, 1);
16214                if($Num ne "0") {
16215                    last;
16216                }
16217            }
16218        }
16219        if($Num eq "0") {
16220            $Num = $_[0];
16221        }
16222        return $Num;
16223    }
16224    return $_[0];
16225}
16226
16227sub cut_off_number($$$)
16228{
16229    my ($num, $digs_to_cut, $z) = @_;
16230    if($num!~/\./)
16231    {
16232        $num .= ".";
16233        foreach (1 .. $digs_to_cut-1) {
16234            $num .= "0";
16235        }
16236    }
16237    elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
16238    {
16239        foreach (1 .. $digs_to_cut - 1 - length($1)) {
16240            $num .= "0";
16241        }
16242    }
16243    elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
16244      $num=sprintf("%.".($digs_to_cut-1)."f", $num);
16245    }
16246    $num=~s/\.[0]+\Z//g;
16247    if($z) {
16248        $num=~s/(\.[1-9]+)[0]+\Z/$1/g;
16249    }
16250    return $num;
16251}
16252
16253sub get_Report_ChangedConstants($$)
16254{
16255    my ($TargetSeverity, $Level) = @_;
16256    my $CHANGED_CONSTANTS = "";
16257
16258    my %ReportMap = ();
16259    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
16260    {
16261        my $Header = $Constants{1}{$Constant}{"Header"};
16262        if(not $Header)
16263        { # added
16264            $Header = $Constants{2}{$Constant}{"Header"}
16265        }
16266
16267        foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
16268        {
16269            if(not defined $CompatRules{$Level}{$Kind}) {
16270                next;
16271            }
16272            if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
16273                next;
16274            }
16275            $ReportMap{$Header}{$Constant}{$Kind} = 1;
16276        }
16277    }
16278
16279    if($ReportFormat eq "xml")
16280    { # XML
16281        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16282        {
16283            $CHANGED_CONSTANTS .= "  <header name=\"$HeaderName\">\n";
16284            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16285            {
16286                $CHANGED_CONSTANTS .= "    <constant name=\"$Constant\">\n";
16287                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16288                {
16289                    my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16290                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16291                    my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
16292
16293                    $CHANGED_CONSTANTS .= "      <problem id=\"$Kind\">\n";
16294                    $CHANGED_CONSTANTS .= "        <change".getXmlParams($Change, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Change</change>\n";
16295                    $CHANGED_CONSTANTS .= "        <effect".getXmlParams($Effect, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Effect</effect>\n";
16296                    if($Overcome) {
16297                        $CHANGED_CONSTANTS .= "        <overcome".getXmlParams($Overcome, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Overcome</overcome>\n";
16298                    }
16299                    $CHANGED_CONSTANTS .= "      </problem>\n";
16300                }
16301                $CHANGED_CONSTANTS .= "    </constant>\n";
16302            }
16303            $CHANGED_CONSTANTS .= "    </header>\n";
16304        }
16305        $CHANGED_CONSTANTS = "<problems_with_constants severity=\"Low\">\n".$CHANGED_CONSTANTS."</problems_with_constants>\n\n";
16306    }
16307    else
16308    { # HTML
16309        my $Number = 0;
16310        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16311        {
16312            $CHANGED_CONSTANTS .= "<span class='h_name'>$HeaderName</span><br/>\n";
16313            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16314            {
16315                my $Report = "";
16316
16317                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16318                {
16319                    my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
16320                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16321                    $Report .= "<tr>\n<th>1</th>\n<td align='left' valign='top'>".$Change."</td>\n<td align='left' valign='top'>$Effect</td>\n</tr>\n";
16322                    $Number += 1;
16323                }
16324                if($Report)
16325                {
16326                    $Report = $ContentDivStart."<table class='ptable'>\n<tr>\n<th width='2%'></th>\n<th width='47%'>Change</th>\n<th>Effect</th>\n</tr>\n".$Report."</table>\n<br/>\n$ContentDivEnd\n";
16327                    $Report = $ContentSpanStart."<span class='extendable'>[+]</span> ".$Constant.$ContentSpanEnd."<br/>\n".$Report;
16328                    $Report = insertIDs($Report);
16329                }
16330                $CHANGED_CONSTANTS .= $Report;
16331            }
16332            $CHANGED_CONSTANTS .= "<br/>\n";
16333        }
16334        if($CHANGED_CONSTANTS)
16335        {
16336            my $Title = "Problems with Constants, $TargetSeverity Severity";
16337            if($TargetSeverity eq "Safe")
16338            { # Safe Changes
16339                $Title = "Other Changes in Constants";
16340            }
16341            $CHANGED_CONSTANTS = "<a name='".get_Anchor("Constant", $Level, $TargetSeverity)."'></a><h2>$Title ($Number)</h2><hr/>\n".$CHANGED_CONSTANTS.$TOP_REF."<br/>\n";
16342        }
16343    }
16344    return $CHANGED_CONSTANTS;
16345}
16346
16347sub getTitle($$$)
16348{
16349    my ($Header, $Library, $NameSpace) = @_;
16350    my $Title = "";
16351
16352    # if($Library and $Library!~/\.\w+\Z/) {
16353    #     $Library .= " (.$LIB_EXT)";
16354    # }
16355
16356    if($Header and $Library)
16357    {
16358        $Title .= "<span class='h_name'>$Header</span>";
16359        $Title .= ", <span class='lib_name'>$Library</span><br/>\n";
16360    }
16361    elsif($Library) {
16362        $Title .= "<span class='lib_name'>$Library</span><br/>\n";
16363    }
16364    elsif($Header) {
16365        $Title .= "<span class='h_name'>$Header</span><br/>\n";
16366    }
16367    if($NameSpace) {
16368        $Title .= "<span class='ns'>namespace <b>$NameSpace</b></span><br/>\n";
16369    }
16370    return $Title;
16371}
16372
16373sub get_Report_Added($)
16374{
16375    my $Level = $_[0];
16376    my $ADDED_INTERFACES = "";
16377    my %ReportMap = ();
16378    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
16379    {
16380        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
16381        {
16382            if($Kind eq "Added_Symbol")
16383            {
16384                my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"};
16385                my $DyLib = $Symbol_Library{2}{$Interface};
16386                if($Level eq "Source" and $ReportFormat eq "html")
16387                { # do not show library name in HTML report
16388                    $DyLib = "";
16389                }
16390                $ReportMap{$HeaderName}{$DyLib}{$Interface} = 1;
16391            }
16392        }
16393    }
16394    if($ReportFormat eq "xml")
16395    { # XML
16396        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16397        {
16398            $ADDED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16399            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16400            {
16401                $ADDED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16402                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16403                    $ADDED_INTERFACES .= "      <name>$Interface</name>\n";
16404                }
16405                $ADDED_INTERFACES .= "    </library>\n";
16406            }
16407            $ADDED_INTERFACES .= "  </header>\n";
16408        }
16409        $ADDED_INTERFACES = "<added_symbols>\n".$ADDED_INTERFACES."</added_symbols>\n\n";
16410    }
16411    else
16412    { # HTML
16413        my $Added_Number = 0;
16414        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16415        {
16416            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16417            {
16418                my %NameSpaceSymbols = ();
16419                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16420                    $NameSpaceSymbols{select_Symbol_NS($Interface, 2)}{$Interface} = 1;
16421                }
16422                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16423                {
16424                    $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16425                    my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpaceSymbols{$NameSpace}});
16426                    foreach my $Interface (@SortedInterfaces)
16427                    {
16428                        $Added_Number += 1;
16429                        my $Signature = get_Signature($Interface, 2);
16430                        if($NameSpace) {
16431                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16432                        }
16433                        if($Interface=~/\A(_Z|\?)/)
16434                        {
16435                            if($Signature) {
16436                                $ADDED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Interface</b>]</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
16437                            }
16438                            else {
16439                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16440                            }
16441                        }
16442                        else
16443                        {
16444                            if($Signature) {
16445                                $ADDED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16446                            }
16447                            else {
16448                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16449                            }
16450                        }
16451                    }
16452                    $ADDED_INTERFACES .= "<br/>\n";
16453                }
16454            }
16455        }
16456        if($ADDED_INTERFACES)
16457        {
16458            my $Anchor = "<a name='Added'></a>";
16459            if($JoinReport) {
16460                $Anchor = "<a name='".$Level."_Added'></a>";
16461            }
16462            $ADDED_INTERFACES = $Anchor."<h2>Added Symbols ($Added_Number)</h2><hr/>\n".$ADDED_INTERFACES.$TOP_REF."<br/>\n";
16463        }
16464    }
16465    return $ADDED_INTERFACES;
16466}
16467
16468sub get_Report_Removed($)
16469{
16470    my $Level = $_[0];
16471    my $REMOVED_INTERFACES = "";
16472    my %ReportMap = ();
16473    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16474    {
16475        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16476        {
16477            if($Kind eq "Removed_Symbol")
16478            {
16479                my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16480                my $DyLib = $Symbol_Library{1}{$Symbol};
16481                if($Level eq "Source" and $ReportFormat eq "html")
16482                { # do not show library name in HTML report
16483                    $DyLib = "";
16484                }
16485                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16486            }
16487        }
16488    }
16489    if($ReportFormat eq "xml")
16490    { # XML
16491        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16492        {
16493            $REMOVED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16494            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16495            {
16496                $REMOVED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16497                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16498                    $REMOVED_INTERFACES .= "      <name>$Symbol</name>\n";
16499                }
16500                $REMOVED_INTERFACES .= "    </library>\n";
16501            }
16502            $REMOVED_INTERFACES .= "  </header>\n";
16503        }
16504        $REMOVED_INTERFACES = "<removed_symbols>\n".$REMOVED_INTERFACES."</removed_symbols>\n\n";
16505    }
16506    else
16507    { # HTML
16508        my $Removed_Number = 0;
16509        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16510        {
16511            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16512            {
16513                my %NameSpaceSymbols = ();
16514                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16515                    $NameSpaceSymbols{select_Symbol_NS($Interface, 1)}{$Interface} = 1;
16516                }
16517                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16518                {
16519                    $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16520                    my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpaceSymbols{$NameSpace}});
16521                    foreach my $Symbol (@SortedInterfaces)
16522                    {
16523                        $Removed_Number += 1;
16524                        my $SubReport = "";
16525                        my $Signature = get_Signature($Symbol, 1);
16526                        if($NameSpace) {
16527                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16528                        }
16529                        if($Symbol=~/\A(_Z|\?)/)
16530                        {
16531                            if($Signature) {
16532                                $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
16533                            }
16534                            else {
16535                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16536                            }
16537                        }
16538                        else
16539                        {
16540                            if($Signature) {
16541                                $REMOVED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16542                            }
16543                            else {
16544                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16545                            }
16546                        }
16547                    }
16548                }
16549                $REMOVED_INTERFACES .= "<br/>\n";
16550            }
16551        }
16552        if($REMOVED_INTERFACES)
16553        {
16554            my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
16555            if($JoinReport) {
16556                $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
16557            }
16558            $REMOVED_INTERFACES = $Anchor."<h2>Removed Symbols ($Removed_Number)</h2><hr/>\n".$REMOVED_INTERFACES.$TOP_REF."<br/>\n";
16559        }
16560    }
16561    return $REMOVED_INTERFACES;
16562}
16563
16564sub getXmlParams($$)
16565{
16566    my ($Content, $Problem) = @_;
16567    return "" if(not $Content or not $Problem);
16568    my %XMLparams = ();
16569    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16570    {
16571        my $Macro = "\@".lc($Attr);
16572        if($Content=~/\Q$Macro\E/) {
16573            $XMLparams{lc($Attr)} = $Problem->{$Attr};
16574        }
16575    }
16576    my @PString = ();
16577    foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
16578        push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
16579    }
16580    if(@PString) {
16581        return " ".join(" ", @PString);
16582    }
16583    else {
16584        return "";
16585    }
16586}
16587
16588sub addMarkup($)
16589{
16590    my $Content = $_[0];
16591    # auto-markup
16592    $Content=~s/\n[ ]*//; # spaces
16593    $Content=~s!(\@\w+\s*\(\@\w+\))!<nowrap>$1</nowrap>!g; # @old_type (@old_size)
16594    $Content=~s!(... \(\w+\))!<nowrap><b>$1</b></nowrap>!g; # ... (va_list)
16595    $Content=~s!<nowrap>(.+?)</nowrap>!<span class='nowrap'>$1</span>!g;
16596    $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
16597    if($Content=~/\ANOTE:/)
16598    { # notes
16599        $Content=~s!(NOTE):!<b>$1</b>:!g;
16600    }
16601    else {
16602        $Content=~s!(NOTE):!<br/><b>$1</b>:!g;
16603    }
16604    $Content=~s! (out)-! <b>$1</b>-!g; # out-parameters
16605    my @Keywords = (
16606        "void",
16607        "const",
16608        "static",
16609        "restrict",
16610        "volatile",
16611        "register",
16612        "virtual"
16613    );
16614    my $MKeys = join("|", @Keywords);
16615    foreach (@Keywords) {
16616        $MKeys .= "|non-".$_;
16617    }
16618    $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
16619
16620    # Markdown
16621    $Content=~s!\*\*([\w\-]+)\*\*!<b>$1</b>!ig;
16622    $Content=~s!\*([\w\-]+)\*!<i>$1</i>!ig;
16623    return $Content;
16624}
16625
16626sub applyMacroses($$$$)
16627{
16628    my ($Level, $Kind, $Content, $Problem) = @_;
16629    return "" if(not $Content or not $Problem);
16630    $Problem->{"Word_Size"} = $WORD_SIZE{2};
16631    $Content = addMarkup($Content);
16632    # macros
16633    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16634    {
16635        my $Macro = "\@".lc($Attr);
16636        my $Value = $Problem->{$Attr};
16637        if(not defined $Value
16638        or $Value eq "") {
16639            next;
16640        }
16641
16642        if(index($Content, $Macro)==-1) {
16643            next;
16644        }
16645
16646        if($Kind!~/\A(Changed|Added|Removed)_Constant\Z/
16647        and $Kind!~/_Type_/
16648        and $Value=~/\s\(/ and $Value!~/['"]/)
16649        { # functions
16650            $Value=~s/\s*\[[\w\-]+\]//g; # remove quals
16651            $Value=~s/\s[a-z]\w*(\)|,)/$1/ig; # remove parameter names
16652            $Value = black_name($Value);
16653        }
16654        elsif($Value=~/\s/) {
16655            $Value = "<span class='value'>".htmlSpecChars($Value)."</span>";
16656        }
16657        elsif($Value=~/\A\d+\Z/
16658        and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
16659        { # bits to bytes
16660            if($Value % $BYTE_SIZE)
16661            { # bits
16662                if($Value==1) {
16663                    $Value = "<b>".$Value."</b> bit";
16664                }
16665                else {
16666                    $Value = "<b>".$Value."</b> bits";
16667                }
16668            }
16669            else
16670            { # bytes
16671                $Value /= $BYTE_SIZE;
16672                if($Value==1) {
16673                    $Value = "<b>".$Value."</b> byte";
16674                }
16675                else {
16676                    $Value = "<b>".$Value."</b> bytes";
16677                }
16678            }
16679        }
16680        else
16681        {
16682            $Value = "<b>".htmlSpecChars($Value)."</b>";
16683        }
16684        $Content=~s/\Q$Macro\E/$Value/g;
16685    }
16686
16687    if($Content=~/(\A|[^\@\w])\@\w/)
16688    {
16689        if(not $IncompleteRules{$Level}{$Kind})
16690        { # only one warning
16691            printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
16692            $IncompleteRules{$Level}{$Kind} = 1;
16693        }
16694    }
16695    return $Content;
16696}
16697
16698sub get_Report_SymbolProblems($$)
16699{
16700    my ($TargetSeverity, $Level) = @_;
16701    my $INTERFACE_PROBLEMS = "";
16702    my (%ReportMap, %SymbolChanges) = ();
16703
16704    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16705    {
16706        my ($SN, $SS, $SV) = separate_symbol($Symbol);
16707        if($SV and defined $CompatProblems{$Level}{$SN}) {
16708            next;
16709        }
16710        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16711        {
16712            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
16713            and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
16714            {
16715                my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16716                my $DyLib = $Symbol_Library{1}{$Symbol};
16717                if(not $DyLib and my $VSym = $SymVer{1}{$Symbol})
16718                { # Symbol with Version
16719                    $DyLib = $Symbol_Library{1}{$VSym};
16720                }
16721                if(not $DyLib)
16722                { # const global data
16723                    $DyLib = "";
16724                }
16725                if($Level eq "Source" and $ReportFormat eq "html")
16726                { # do not show library name in HTML report
16727                    $DyLib = "";
16728                }
16729                %{$SymbolChanges{$Symbol}{$Kind}} = %{$CompatProblems{$Level}{$Symbol}{$Kind}};
16730                foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16731                {
16732                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16733                    if($Severity ne $TargetSeverity) {
16734                        delete($SymbolChanges{$Symbol}{$Kind}{$Location});
16735                    }
16736                }
16737                if(not keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16738                {
16739                    delete($SymbolChanges{$Symbol}{$Kind});
16740                    next;
16741                }
16742                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16743            }
16744        }
16745        if(not keys(%{$SymbolChanges{$Symbol}})) {
16746            delete($SymbolChanges{$Symbol});
16747        }
16748    }
16749
16750    if($ReportFormat eq "xml")
16751    { # XML
16752        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16753        {
16754            $INTERFACE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
16755            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16756            {
16757                $INTERFACE_PROBLEMS .= "    <library name=\"$DyLib\">\n";
16758                my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
16759                foreach my $Symbol (@SortedInterfaces)
16760                {
16761                    $INTERFACE_PROBLEMS .= "      <symbol name=\"$Symbol\">\n";
16762                    foreach my $Kind (keys(%{$SymbolChanges{$Symbol}}))
16763                    {
16764                        foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16765                        {
16766                            my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
16767                            $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
16768
16769                            $INTERFACE_PROBLEMS .= "        <problem id=\"$Kind\">\n";
16770                            my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16771                            $INTERFACE_PROBLEMS .= "          <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
16772                            my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16773                            $INTERFACE_PROBLEMS .= "          <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
16774                            if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
16775                                $INTERFACE_PROBLEMS .= "          <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
16776                            }
16777                            $INTERFACE_PROBLEMS .= "        </problem>\n";
16778                        }
16779                    }
16780                    $INTERFACE_PROBLEMS .= "      </symbol>\n";
16781                }
16782                $INTERFACE_PROBLEMS .= "    </library>\n";
16783            }
16784            $INTERFACE_PROBLEMS .= "  </header>\n";
16785        }
16786        $INTERFACE_PROBLEMS = "<problems_with_symbols severity=\"$TargetSeverity\">\n".$INTERFACE_PROBLEMS."</problems_with_symbols>\n\n";
16787    }
16788    else
16789    { # HTML
16790        my $ProblemsNum = 0;
16791        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16792        {
16793            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16794            {
16795                my (%NameSpaceSymbols, %NewSignature) = ();
16796                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16797                    $NameSpaceSymbols{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
16798                }
16799                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16800                {
16801                    $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
16802                    my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} sort keys(%{$NameSpaceSymbols{$NameSpace}});
16803                    foreach my $Symbol (@SortedInterfaces)
16804                    {
16805                        my $Signature = get_Signature($Symbol, 1);
16806                        my $SYMBOL_REPORT = "";
16807                        my $ProblemNum = 1;
16808                        foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
16809                        {
16810                            foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16811                            {
16812                                my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
16813                                $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
16814                                if($Problem{"New_Signature"}) {
16815                                    $NewSignature{$Symbol} = $Problem{"New_Signature"};
16816                                }
16817                                if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
16818                                {
16819                                    my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
16820                                    $SYMBOL_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td align='left' valign='top'>".$Change."</td>\n<td align='left' valign='top'>".$Effect."</td>\n</tr>\n";
16821                                    $ProblemNum += 1;
16822                                    $ProblemsNum += 1;
16823                                }
16824                            }
16825                        }
16826                        $ProblemNum -= 1;
16827                        if($SYMBOL_REPORT)
16828                        {
16829                            $INTERFACE_PROBLEMS .= $ContentSpanStart."<span class='extendable'>[+]</span> ";
16830                            if($Signature) {
16831                                $INTERFACE_PROBLEMS .= highLight_Signature_Italic_Color($Signature);
16832                            }
16833                            else {
16834                                $INTERFACE_PROBLEMS .= $Symbol;
16835                            }
16836                            $INTERFACE_PROBLEMS .= " ($ProblemNum)".$ContentSpanEnd."<br/>\n";
16837                            $INTERFACE_PROBLEMS .= $ContentDivStart."\n";
16838                            if($NewSignature{$Symbol})
16839                            { # argument list changed to
16840                                $INTERFACE_PROBLEMS .= "\n<span class='new_sign_lbl'>changed to:</span>\n<br/>\n<span class='new_sign'>".highLight_Signature_Italic_Color($NewSignature{$Symbol})."</span><br/>\n";
16841                            }
16842                            if($Symbol=~/\A(_Z|\?)/) {
16843                                $INTERFACE_PROBLEMS .= "<span class='mangled'>&#160;&#160;&#160;&#160;[symbol: <b>$Symbol</b>]</span><br/>\n";
16844                            }
16845                            $INTERFACE_PROBLEMS .= "<table class='ptable'>\n<tr>\n<th width='2%'></th>\n<th width='47%'>Change</th>\n<th>Effect</th>\n</tr>\n$SYMBOL_REPORT</table>\n<br/>\n";
16846                            $INTERFACE_PROBLEMS .= $ContentDivEnd;
16847                            if($NameSpace) {
16848                                $INTERFACE_PROBLEMS=~s/\b\Q$NameSpace\E::\b//g;
16849                            }
16850                        }
16851                    }
16852                    $INTERFACE_PROBLEMS .= "<br/>\n";
16853                }
16854            }
16855        }
16856
16857        if($INTERFACE_PROBLEMS)
16858        {
16859            $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
16860            my $Title = "Problems with Symbols, $TargetSeverity Severity";
16861            if($TargetSeverity eq "Safe")
16862            { # Safe Changes
16863                $Title = "Other Changes in Symbols";
16864            }
16865            $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";
16866        }
16867    }
16868    return $INTERFACE_PROBLEMS;
16869}
16870
16871sub get_Report_TypeProblems($$)
16872{
16873    my ($TargetSeverity, $Level) = @_;
16874    my $TYPE_PROBLEMS = "";
16875    my (%ReportMap, %TypeChanges) = ();
16876
16877    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
16878    {
16879        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
16880        {
16881            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
16882            {
16883                foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
16884                {
16885                    my $TypeName = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
16886                    my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
16887                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16888
16889                    if($Severity eq "Safe"
16890                    and $TargetSeverity ne "Safe") {
16891                        next;
16892                    }
16893
16894                    if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$TypeName}{$Kind}{$Target})
16895                    {
16896                        if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
16897                        { # select a problem with the highest priority
16898                            next;
16899                        }
16900                    }
16901
16902                    $TypeChanges{$TypeName}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
16903                }
16904            }
16905        }
16906    }
16907
16908    my %Kinds_Locations = ();
16909    foreach my $TypeName (keys(%TypeChanges))
16910    {
16911        my %Kind_Target = ();
16912        foreach my $Kind (sort keys(%{$TypeChanges{$TypeName}}))
16913        {
16914            foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16915            {
16916                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16917                if($Severity ne $TargetSeverity)
16918                { # other priority
16919                    delete($TypeChanges{$TypeName}{$Kind}{$Location});
16920                    next;
16921                }
16922                $Kinds_Locations{$TypeName}{$Kind}{$Location} = 1;
16923                my $Target = $TypeChanges{$TypeName}{$Kind}{$Location}{"Target"};
16924                if($Kind_Target{$Kind}{$Target})
16925                { # duplicate target
16926                    delete($TypeChanges{$TypeName}{$Kind}{$Location});
16927                    next;
16928                }
16929                $Kind_Target{$Kind}{$Target} = 1;
16930                my $HeaderName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Header"};
16931                $ReportMap{$HeaderName}{$TypeName} = 1;
16932            }
16933            if(not keys(%{$TypeChanges{$TypeName}{$Kind}})) {
16934                delete($TypeChanges{$TypeName}{$Kind});
16935            }
16936        }
16937        if(not keys(%{$TypeChanges{$TypeName}})) {
16938            delete($TypeChanges{$TypeName});
16939        }
16940    }
16941
16942    my @Symbols = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$CompatProblems{$Level}});
16943    if($ReportFormat eq "xml")
16944    { # XML
16945        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16946        {
16947            $TYPE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
16948            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
16949            {
16950                $TYPE_PROBLEMS .= "    <type name=\"".xmlSpecChars($TypeName)."\">\n";
16951                foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
16952                {
16953                    foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16954                    {
16955                        my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
16956                        $TYPE_PROBLEMS .= "      <problem id=\"$Kind\">\n";
16957                        my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16958                        $TYPE_PROBLEMS .= "        <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
16959                        my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16960                        $TYPE_PROBLEMS .= "        <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
16961                        if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
16962                            $TYPE_PROBLEMS .= "        <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
16963                        }
16964                        $TYPE_PROBLEMS .= "      </problem>\n";
16965                    }
16966                }
16967                $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols);
16968                if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
16969                    $TYPE_PROBLEMS .= showVTables($TypeName);
16970                }
16971                $TYPE_PROBLEMS .= "    </type>\n";
16972            }
16973            $TYPE_PROBLEMS .= "  </header>\n";
16974        }
16975        $TYPE_PROBLEMS = "<problems_with_types severity=\"$TargetSeverity\">\n".$TYPE_PROBLEMS."</problems_with_types>\n\n";
16976    }
16977    else
16978    { # HTML
16979        my $ProblemsNum = 0;
16980        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16981        {
16982            my (%NameSpace_Type) = ();
16983            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
16984                $NameSpace_Type{select_Type_NS($TypeName, 1)}{$TypeName} = 1;
16985            }
16986            foreach my $NameSpace (sort keys(%NameSpace_Type))
16987            {
16988                $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
16989                my @SortedTypes = sort {lc(show_Type($a, 0, 1)) cmp lc(show_Type($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
16990                foreach my $TypeName (@SortedTypes)
16991                {
16992                    my $ProblemNum = 1;
16993                    my $TYPE_REPORT = "";
16994
16995                    foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
16996                    {
16997                        foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16998                        {
16999                            my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
17000                            if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
17001                            {
17002                                my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
17003                                $TYPE_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td align='left' valign='top'>".$Change."</td>\n<td align='left' valign='top'>$Effect</td>\n</tr>\n";
17004                                $ProblemNum += 1;
17005                                $ProblemsNum += 1;
17006                            }
17007                        }
17008                    }
17009                    $ProblemNum -= 1;
17010                    if($TYPE_REPORT)
17011                    {
17012                        my $Affected = getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols);
17013                        my $ShowVTables = "";
17014                        if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
17015                            $ShowVTables = showVTables($TypeName);
17016                        }
17017
17018                        $TYPE_PROBLEMS .= $ContentSpanStart."<span class='extendable'>[+]</span> ".show_Type($TypeName, 1, 1)." ($ProblemNum)".$ContentSpanEnd;
17019                        $TYPE_PROBLEMS .= "<br/>\n".$ContentDivStart."<table class='ptable'><tr>\n";
17020                        $TYPE_PROBLEMS .= "<th width='2%'></th><th width='47%'>Change</th>\n";
17021                        $TYPE_PROBLEMS .= "<th>Effect</th></tr>".$TYPE_REPORT."</table>\n";
17022                        $TYPE_PROBLEMS .= $ShowVTables.$Affected."<br/><br/>".$ContentDivEnd."\n";
17023                        if($NameSpace) {
17024                            $TYPE_PROBLEMS=~s/\b\Q$NameSpace\E::(\w|\~)/$1/g;
17025                        }
17026                    }
17027                }
17028                $TYPE_PROBLEMS .= "<br/>\n";
17029            }
17030        }
17031
17032        if($TYPE_PROBLEMS)
17033        {
17034            $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
17035            my $Title = "Problems with Data Types, $TargetSeverity Severity";
17036            if($TargetSeverity eq "Safe")
17037            { # Safe Changes
17038                $Title = "Other Changes in Data Types";
17039            }
17040            $TYPE_PROBLEMS = "<a name=\'".get_Anchor("Type", $Level, $TargetSeverity)."\'></a>\n<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS.$TOP_REF."<br/>\n";
17041        }
17042    }
17043    return $TYPE_PROBLEMS;
17044}
17045
17046sub show_Type($$$)
17047{
17048    my ($Name, $Html, $LibVersion) = @_;
17049    my $TType = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$Name}}{"Type"};
17050    $TType = lc($TType);
17051    if($TType=~/struct|union|enum/) {
17052        $Name=~s/\A\Q$TType\E //g;
17053    }
17054    if($Html) {
17055        $Name = "<span class='ttype'>".$TType."</span> ".htmlSpecChars($Name);
17056    }
17057    else {
17058        $Name = $TType." ".$Name;
17059    }
17060    return $Name;
17061}
17062
17063sub get_Anchor($$$)
17064{
17065    my ($Kind, $Level, $Severity) = @_;
17066    if($JoinReport)
17067    {
17068        if($Severity eq "Safe") {
17069            return "Other_".$Level."_Changes_In_".$Kind."s";
17070        }
17071        else {
17072            return $Kind."_".$Level."_Problems_".$Severity;
17073        }
17074    }
17075    else
17076    {
17077        if($Severity eq "Safe") {
17078            return "Other_Changes_In_".$Kind."s";
17079        }
17080        else {
17081            return $Kind."_Problems_".$Severity;
17082        }
17083    }
17084}
17085
17086sub showVTables($)
17087{
17088    my $TypeName = $_[0];
17089    my $TypeId1 = $TName_Tid{1}{$TypeName};
17090    my %Type1 = get_Type($TypeId1, 1);
17091    if(defined $Type1{"VTable"}
17092    and keys(%{$Type1{"VTable"}}))
17093    {
17094        my $TypeId2 = $TName_Tid{2}{$TypeName};
17095        my %Type2 = get_Type($TypeId2, 2);
17096        if(defined $Type2{"VTable"}
17097        and keys(%{$Type2{"VTable"}}))
17098        {
17099            my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
17100            my %Entries = ();
17101            foreach my $Index (sort {int($a)<=>int($b)} (keys(%Indexes)))
17102            {
17103                $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
17104                $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
17105            }
17106            my $VTABLES = "";
17107            if($ReportFormat eq "xml")
17108            { # XML
17109                $VTABLES .= "      <vtable>\n";
17110                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
17111                {
17112                    $VTABLES .= "        <entry offset=\"".$Index."\">\n";
17113                    $VTABLES .= "          <old>".xmlSpecChars($Entries{$Index}{"E1"})."</old>\n";
17114                    $VTABLES .= "          <new>".xmlSpecChars($Entries{$Index}{"E2"})."</new>\n";
17115                    $VTABLES .= "        </entry>\n";
17116                }
17117                $VTABLES .= "      </vtable>\n\n";
17118            }
17119            else
17120            { # HTML
17121                $VTABLES .= "<table class='vtable'>";
17122                $VTABLES .= "<tr><th>Offset</th>";
17123                $VTABLES .= "<th>Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries</th>";
17124                $VTABLES .= "<th>Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries</th></tr>";
17125                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
17126                {
17127                    my ($Color1, $Color2) = ("", "");
17128
17129                    my $E1 = $Entries{$Index}{"E1"};
17130                    my $E2 = $Entries{$Index}{"E2"};
17131
17132                    if($E1 ne $E2
17133                    and $E1!~/ 0x/
17134                    and $E2!~/ 0x/)
17135                    {
17136                        if($Entries{$Index}{"E1"})
17137                        {
17138                            $Color1 = " class='failed'";
17139                            $Color2 = " class='failed'";
17140                        }
17141                        else {
17142                            $Color2 = " class='warning'";
17143                        }
17144                    }
17145                    $VTABLES .= "<tr><th>".$Index."</th>\n";
17146                    $VTABLES .= "<td$Color1>".htmlSpecChars($Entries{$Index}{"E1"})."</td>\n";
17147                    $VTABLES .= "<td$Color2>".htmlSpecChars($Entries{$Index}{"E2"})."</td></tr>\n";
17148                }
17149                $VTABLES .= "</table><br/>\n";
17150                $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
17151                $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."<br/>\n".$VTABLES;
17152            }
17153            return $VTABLES;
17154        }
17155    }
17156    return "";
17157}
17158
17159sub simpleVEntry($)
17160{
17161    my $VEntry = $_[0];
17162    if(not defined $VEntry
17163    or $VEntry eq "") {
17164        return "";
17165    }
17166
17167    $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
17168    $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
17169    $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
17170    if($VEntry=~/\A_ZThn.+\Z/) {
17171        $VEntry = "non-virtual thunk";
17172    }
17173    $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
17174    # support for old GCC versions
17175    $VEntry=~s/\A0u\Z/(int (*)(...))0/;
17176    $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
17177    $VEntry=~s/\A&_Z\Z/& _Z/;
17178    $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
17179    return $VEntry;
17180}
17181
17182sub adjustParamPos($$$)
17183{
17184    my ($Pos, $Symbol, $LibVersion) = @_;
17185    if(defined $CompleteSignature{$LibVersion}{$Symbol})
17186    {
17187        if(not $CompleteSignature{$LibVersion}{$Symbol}{"Static"}
17188        and $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
17189        {
17190            return $Pos-1;
17191        }
17192
17193        return $Pos;
17194    }
17195
17196    return undef;
17197}
17198
17199sub getParamPos($$$)
17200{
17201    my ($Name, $Symbol, $LibVersion) = @_;
17202
17203    if(defined $CompleteSignature{$LibVersion}{$Symbol}
17204    and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"})
17205    {
17206        my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17207        foreach (keys(%{$Info->{"Param"}}))
17208        {
17209            if($Info->{"Param"}{$_}{"name"} eq $Name)
17210            {
17211                return $_;
17212            }
17213        }
17214    }
17215
17216    return undef;
17217}
17218
17219sub getParamName($)
17220{
17221    my $Loc = $_[0];
17222    $Loc=~s/\->.*//g;
17223    return $Loc;
17224}
17225
17226sub getAffectedSymbols($$$$)
17227{
17228    my ($Level, $Target_TypeName, $Kinds_Locations, $Syms) = @_;
17229
17230    my $LIMIT = 10;
17231    if(defined $AffectLimit) {
17232        $LIMIT = $AffectLimit;
17233    }
17234
17235    my @Kinds = sort keys(%{$Kinds_Locations});
17236    my %KLocs = ();
17237    foreach my $Kind (@Kinds)
17238    {
17239        my @Locs = sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} keys(%{$Kinds_Locations->{$Kind}});
17240        $KLocs{$Kind} = \@Locs;
17241    }
17242
17243    my %SymLocKind = ();
17244    foreach my $Symbol (@{$Syms})
17245    {
17246        if(index($Symbol, "_Z")==0
17247        and $Symbol=~/(C2|D2|D0)[EI]/)
17248        { # duplicated problems for C2 constructors, D2 and D0 destructors
17249            next;
17250        }
17251
17252        foreach my $Kind (@Kinds)
17253        {
17254            if(not defined $CompatProblems{$Level}{$Symbol}
17255            or not defined $CompatProblems{$Level}{$Symbol}{$Kind}) {
17256                next;
17257            }
17258
17259            foreach my $Loc (@{$KLocs{$Kind}})
17260            {
17261                if(not defined $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}) {
17262                    next;
17263                }
17264
17265                if(index($Symbol, "\@")!=-1
17266                or index($Symbol, "\$")!=-1)
17267                {
17268                    my ($SN, $SS, $SV) = separate_symbol($Symbol);
17269
17270                    if($Level eq "Source")
17271                    { # remove symbol version
17272                        $Symbol = $SN;
17273                    }
17274
17275                    if($SV and defined $CompatProblems{$Level}{$SN}
17276                    and defined $CompatProblems{$Level}{$SN}{$Kind}{$Loc})
17277                    { # duplicated problems for versioned symbols
17278                        next;
17279                    }
17280                }
17281
17282                my $Type_Name = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}{"Type_Name"};
17283                if($Type_Name ne $Target_TypeName) {
17284                    next;
17285                }
17286
17287                $SymLocKind{$Symbol}{$Loc}{$Kind} = 1;
17288                last;
17289            }
17290        }
17291
17292        # if(keys(%SymLocKind)>=$LIMIT)
17293        # {
17294        #     last;
17295        # }
17296    }
17297
17298    %KLocs = (); # clear
17299
17300    my %SymSel = ();
17301    my $Num = 0;
17302    foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymLocKind))
17303    {
17304        LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Symbol}}))
17305        {
17306            foreach my $Kind (sort keys(%{$SymLocKind{$Symbol}{$Loc}}))
17307            {
17308                $SymSel{$Symbol}{"Loc"} = $Loc;
17309                $SymSel{$Symbol}{"Kind"} = $Kind;
17310                last LOOP;
17311            }
17312        }
17313
17314        $Num += 1;
17315
17316        if($Num>=$LIMIT) {
17317            last;
17318        }
17319    }
17320
17321    my $Affected = "";
17322
17323    if($ReportFormat eq "xml")
17324    { # XML
17325        $Affected .= "      <affected>\n";
17326
17327        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17328        {
17329            my $Loc = $SymSel{$Symbol}{"Loc"};
17330            my $PName = getParamName($Loc);
17331            my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $Loc);
17332
17333            my $Target = "";
17334            if($PName)
17335            {
17336                $Target .= " param=\"$PName\"";
17337                $Desc=~s/parameter $PName /parameter \@param /;
17338            }
17339            elsif($Loc=~/\Aretval(\-|\Z)/i) {
17340                $Target .= " affected=\"retval\"";
17341            }
17342            elsif($Loc=~/\Athis(\-|\Z)/i) {
17343                $Target .= " affected=\"this\"";
17344            }
17345
17346            if($Desc=~s/\AField ([^\s]+) /Field \@field /) {
17347                $Target .= " field=\"$1\"";
17348            }
17349
17350            $Affected .= "        <symbol name=\"$Symbol\"$Target>\n";
17351            $Affected .= "          <comment>".xmlSpecChars($Desc)."</comment>\n";
17352            $Affected .= "        </symbol>\n";
17353        }
17354        $Affected .= "      </affected>\n";
17355    }
17356    else
17357    { # HTML
17358        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17359        {
17360            my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $SymSel{$Symbol}{"Loc"});
17361            my $S = get_Signature($Symbol, 1);
17362            my $PName = getParamName($SymSel{$Symbol}{"Loc"});
17363            my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
17364
17365            $Affected .= "<span class='iname_a'>".highLight_Signature_PPos_Italic($S, $Pos, 1, 0, 0)."</span><br/>\n";
17366            $Affected .= "<div class='affect'>".htmlSpecChars($Desc)."</div>\n";
17367        }
17368
17369        if(keys(%SymLocKind)>$LIMIT) {
17370            $Affected .= " <b>...</b>\n<br/>\n"; # and others ...
17371        }
17372
17373        $Affected = "<div class='affected'>".$Affected."</div>\n";
17374        if($Affected)
17375        {
17376            $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
17377            $Affected = $ContentSpanStart_Affected."[+] affected symbols (".keys(%SymLocKind).")".$ContentSpanEnd.$Affected;
17378        }
17379    }
17380
17381    return $Affected;
17382}
17383
17384sub cmpLocations($$)
17385{
17386    my ($L1, $L2) = @_;
17387    if($L2=~/\A(retval|this)\b/
17388    and $L1!~/\A(retval|this)\b/)
17389    {
17390        if($L1!~/\-\>/) {
17391            return 1;
17392        }
17393        elsif($L2=~/\-\>/) {
17394            return 1;
17395        }
17396    }
17397    return 0;
17398}
17399
17400sub getAffectDesc($$$$)
17401{
17402    my ($Level, $Symbol, $Kind, $Location) = @_;
17403
17404    my %Problem = %{$CompatProblems{$Level}{$Symbol}{$Kind}{$Location}};
17405
17406    my $Location_I = $Location;
17407    $Location=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
17408
17409    my @Sentence = ();
17410
17411    if($Kind eq "Overridden_Virtual_Method"
17412    or $Kind eq "Overridden_Virtual_Method_B") {
17413        push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method.");
17414    }
17415    elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
17416    {
17417        my %SymInfo = %{$CompleteSignature{1}{$Symbol}};
17418
17419        if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
17420        {
17421            my $METHOD_TYPE = $SymInfo{"Constructor"}?"constructor":"method";
17422            my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
17423
17424            if($ClassName eq $Problem{"Type_Name"}) {
17425                push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class.");
17426            }
17427            else {
17428                push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'.");
17429            }
17430        }
17431        else
17432        {
17433            my $TypeID = undef;
17434
17435            if($Location=~/retval/)
17436            { # return value
17437                if(index($Location, "->")!=-1) {
17438                    push(@Sentence, "Field \'".$Location."\' in return value");
17439                }
17440                else {
17441                    push(@Sentence, "Return value");
17442                }
17443
17444                $TypeID = $SymInfo{"Return"};
17445            }
17446            elsif($Location=~/this/)
17447            { # "this" pointer
17448                if(index($Location, "->")!=-1) {
17449                    push(@Sentence, "Field \'".$Location."\' in the object of this method");
17450                }
17451                else {
17452                    push(@Sentence, "\'this\' pointer");
17453                }
17454
17455                $TypeID = $SymInfo{"Class"};
17456            }
17457            else
17458            { # parameters
17459
17460                my $PName = getParamName($Location);
17461                my $PPos = getParamPos($PName, $Symbol, 1);
17462
17463                if(index($Location, "->")!=-1) {
17464                    push(@Sentence, "Field \'".$Location."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17465                }
17466                else {
17467                    push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17468                }
17469                if($PName) {
17470                    push(@Sentence, "\'".$PName."\'");
17471                }
17472
17473                $TypeID = $SymInfo{"Param"}{$PPos}{"type"};
17474            }
17475
17476            if($Location!~/this/)
17477            {
17478                if(my %PureType = get_PureType($TypeID, $TypeInfo{1}))
17479                {
17480                    if($PureType{"Type"} eq "Pointer") {
17481                        push(@Sentence, "(pointer)");
17482                    }
17483                    elsif($PureType{"Type"} eq "Ref") {
17484                        push(@Sentence, "(reference)");
17485                    }
17486                }
17487            }
17488
17489            if($Location eq "this") {
17490                push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17491            }
17492            else
17493            {
17494                my $Location_T = $Location;
17495                $Location_T=~s/\A\w+(\->|\Z)//; # location in type
17496
17497                my $TypeID_Problem = $TypeID;
17498                if($Location_T) {
17499                    $TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
17500                }
17501
17502                if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Problem{"Type_Name"}) {
17503                    push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'.");
17504                }
17505                else {
17506                    push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17507                }
17508            }
17509        }
17510    }
17511    if($ExtendedSymbols{$Symbol}) {
17512        push(@Sentence, " This is a symbol from an external library that may use the \'$TargetLibraryName\' library and change the ABI after recompiling.");
17513    }
17514
17515    my $Sent = join(" ", @Sentence);
17516
17517    $Sent=~s/->/./g;
17518
17519    if($ReportFormat eq "xml")
17520    {
17521        $Sent=~s/'//g;
17522    }
17523
17524    return $Sent;
17525}
17526
17527sub getFieldType($$$)
17528{
17529    my ($Location, $TypeId, $LibVersion) = @_;
17530
17531    my @Fields = split(/\->/, $Location);
17532
17533    foreach my $Name (@Fields)
17534    {
17535        my %Info = get_BaseType($TypeId, $LibVersion);
17536
17537        foreach my $Pos (keys(%{$Info{"Memb"}}))
17538        {
17539            if($Info{"Memb"}{$Pos}{"name"} eq $Name)
17540            {
17541                $TypeId = $Info{"Memb"}{$Pos}{"type"};
17542                last;
17543            }
17544        }
17545    }
17546
17547    return $TypeId;
17548}
17549
17550sub get_XmlSign($$)
17551{
17552    my ($Symbol, $LibVersion) = @_;
17553    my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17554    my $Report = "";
17555    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}}))
17556    {
17557        my $Name = $Info->{"Param"}{$Pos}{"name"};
17558        my $Type = $Info->{"Param"}{$Pos}{"type"};
17559        my $TypeName = $TypeInfo{$LibVersion}{$Type}{"Name"};
17560        foreach my $Typedef (keys(%ChangedTypedef))
17561        {
17562            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
17563                $TypeName=~s/\b\Q$Typedef\E\b/$Base/g;
17564            }
17565        }
17566        $Report .= "    <param pos=\"$Pos\">\n";
17567        $Report .= "      <name>".$Name."</name>\n";
17568        $Report .= "      <type>".xmlSpecChars($TypeName)."</type>\n";
17569        $Report .= "    </param>\n";
17570    }
17571    if(my $Return = $Info->{"Return"})
17572    {
17573        my $RTName = $TypeInfo{$LibVersion}{$Return}{"Name"};
17574        $Report .= "    <retval>\n";
17575        $Report .= "      <type>".xmlSpecChars($RTName)."</type>\n";
17576        $Report .= "    </retval>\n";
17577    }
17578    return $Report;
17579}
17580
17581sub get_Report_SymbolsInfo($)
17582{
17583    my $Level = $_[0];
17584    my $Report = "<symbols_info>\n";
17585    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
17586    {
17587        my ($SN, $SS, $SV) = separate_symbol($Symbol);
17588        if($SV and defined $CompatProblems{$Level}{$SN}) {
17589            next;
17590        }
17591        $Report .= "  <symbol name=\"$Symbol\">\n";
17592        my ($S1, $P1, $S2, $P2) = ();
17593        if(not $AddedInt{$Level}{$Symbol})
17594        {
17595            if(defined $CompleteSignature{1}{$Symbol}
17596            and defined $CompleteSignature{1}{$Symbol}{"Header"})
17597            {
17598                $P1 = get_XmlSign($Symbol, 1);
17599                $S1 = get_Signature($Symbol, 1);
17600            }
17601            elsif($Symbol=~/\A(_Z|\?)/) {
17602                $S1 = $tr_name{$Symbol};
17603            }
17604        }
17605        if(not $RemovedInt{$Level}{$Symbol})
17606        {
17607            if(defined $CompleteSignature{2}{$Symbol}
17608            and defined $CompleteSignature{2}{$Symbol}{"Header"})
17609            {
17610                $P2 = get_XmlSign($Symbol, 2);
17611                $S2 = get_Signature($Symbol, 2);
17612            }
17613            elsif($Symbol=~/\A(_Z|\?)/) {
17614                $S2 = $tr_name{$Symbol};
17615            }
17616        }
17617        if($S1)
17618        {
17619            $Report .= "    <old signature=\"".xmlSpecChars($S1)."\">\n";
17620            $Report .= $P1;
17621            $Report .= "    </old>\n";
17622        }
17623        if($S2 and $S2 ne $S1)
17624        {
17625            $Report .= "    <new signature=\"".xmlSpecChars($S2)."\">\n";
17626            $Report .= $P2;
17627            $Report .= "    </new>\n";
17628        }
17629        $Report .= "  </symbol>\n";
17630    }
17631    $Report .= "</symbols_info>\n";
17632    return $Report;
17633}
17634
17635sub writeReport($$)
17636{
17637    my ($Level, $Report) = @_;
17638    if($ReportFormat eq "xml") {
17639        $Report = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".$Report;
17640    }
17641    if($StdOut)
17642    { # --stdout option
17643        print STDOUT $Report;
17644    }
17645    else
17646    {
17647        my $RPath = getReportPath($Level);
17648        mkpath(get_dirname($RPath));
17649
17650        open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
17651        print REPORT $Report;
17652        close(REPORT);
17653    }
17654}
17655
17656sub getReport($)
17657{
17658    my $Level = $_[0];
17659    if($ReportFormat eq "xml")
17660    { # XML
17661        if($Level eq "Join")
17662        {
17663            my $Report = "<reports>\n";
17664            $Report .= getReport("Binary");
17665            $Report .= getReport("Source");
17666            $Report .= "</reports>\n";
17667            return $Report;
17668        }
17669        else
17670        {
17671            my $Report = "<report kind=\"".lc($Level)."\" version=\"$XML_REPORT_VERSION\">\n\n";
17672            my ($Summary, $MetaData) = get_Summary($Level);
17673            $Report .= $Summary."\n";
17674            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17675            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17676
17677            # additional symbols info (if needed)
17678            # $Report .= get_Report_SymbolsInfo($Level);
17679
17680            $Report .= "</report>\n";
17681            return $Report;
17682        }
17683    }
17684    else
17685    { # HTML
17686        my $CssStyles = readModule("Styles", "Report.css");
17687        my $JScripts = readModule("Scripts", "Sections.js");
17688        if($Level eq "Join")
17689        {
17690            $CssStyles .= "\n".readModule("Styles", "Tabs.css");
17691            $JScripts .= "\n".readModule("Scripts", "Tabs.js");
17692            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
17693            my $Keywords = $TargetTitle.", compatibility, API, ABI, report";
17694            my $Description = "API/ABI compatibility report for the $TargetTitle $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17695            my ($BSummary, $BMetaData) = get_Summary("Binary");
17696            my ($SSummary, $SMetaData) = get_Summary("Source");
17697            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>";
17698            $Report .= get_Report_Title("Join")."
17699            <br/>
17700            <div class='tabset'>
17701            <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
17702            <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
17703            </div>";
17704            $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>";
17705            $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>";
17706            $Report .= getReportFooter();
17707            $Report .= "\n</body></html>\n";
17708            return $Report;
17709        }
17710        else
17711        {
17712            my ($Summary, $MetaData) = get_Summary($Level);
17713            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." ".lc($Level)." compatibility report";
17714            my $Keywords = $TargetTitle.", ".lc($Level)." compatibility, API, report";
17715            my $Description = "$Level compatibility report for the ".$TargetTitle." ".$TargetComponent." between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17716            if($Level eq "Binary")
17717            {
17718                if(getArch(1) eq getArch(2)
17719                and getArch(1) ne "unknown") {
17720                    $Description .= " on ".showArch(getArch(1));
17721                }
17722            }
17723            my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."\n<body>\n<div><a name='Top'></a>\n";
17724            $Report .= get_Report_Title($Level)."\n".$Summary."\n";
17725            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17726            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17727            $Report .= get_SourceInfo();
17728            $Report .= "</div>\n<br/><br/><br/>\n";
17729            $Report .= getReportFooter();
17730            $Report .= "\n</body></html>\n";
17731            return $Report;
17732        }
17733    }
17734}
17735
17736sub getLegend()
17737{
17738    return "<br/>
17739<table class='summary'>
17740<tr>
17741    <td class='new'>added</td>
17742    <td class='passed'>compatible</td>
17743</tr>
17744<tr>
17745    <td class='warning'>warning</td>
17746    <td class='failed'>incompatible</td>
17747</tr></table>\n";
17748}
17749
17750sub createReport()
17751{
17752    if($JoinReport)
17753    { # --stdout
17754        writeReport("Join", getReport("Join"));
17755    }
17756    elsif($DoubleReport)
17757    { # default
17758        writeReport("Binary", getReport("Binary"));
17759        writeReport("Source", getReport("Source"));
17760    }
17761    elsif($BinaryOnly)
17762    { # --binary
17763        writeReport("Binary", getReport("Binary"));
17764    }
17765    elsif($SourceOnly)
17766    { # --source
17767        writeReport("Source", getReport("Source"));
17768    }
17769}
17770
17771sub getReportFooter()
17772{
17773    my $Footer = "";
17774
17775    $Footer .= "<hr/>\n";
17776    $Footer .= "<div class='footer' align='right'>";
17777    $Footer .= "<i>Generated by <a href='".$HomePage."'>ABI Compliance Checker</a> $TOOL_VERSION &#160;</i>\n";
17778    $Footer .= "</div>\n";
17779    $Footer .= "<br/>\n";
17780
17781    return $Footer;
17782}
17783
17784sub get_Report_Problems($$)
17785{
17786    my ($Severity, $Level) = @_;
17787
17788    my $Report = get_Report_TypeProblems($Severity, $Level);
17789    if(my $SProblems = get_Report_SymbolProblems($Severity, $Level)) {
17790        $Report .= $SProblems;
17791    }
17792
17793    if($Severity eq "Low" or $Severity eq "Safe") {
17794        $Report .= get_Report_ChangedConstants($Severity, $Level);
17795    }
17796
17797    if($ReportFormat eq "html")
17798    {
17799        if($Report)
17800        { # add anchor
17801            if($JoinReport)
17802            {
17803                if($Severity eq "Safe") {
17804                    $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
17805                }
17806                else {
17807                    $Report = "<a name=\'".$Severity."_Risk_".$Level."_Problems\'></a>".$Report;
17808                }
17809            }
17810            else
17811            {
17812                if($Severity eq "Safe") {
17813                    $Report = "<a name=\'Other_Changes\'></a>".$Report;
17814                }
17815                else {
17816                    $Report = "<a name=\'".$Severity."_Risk_Problems\'></a>".$Report;
17817                }
17818            }
17819        }
17820    }
17821    return $Report;
17822}
17823
17824sub composeHTML_Head($$$$$)
17825{
17826    my ($Title, $Keywords, $Description, $Styles, $Scripts) = @_;
17827    return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
17828    <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
17829    <head>
17830    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />
17831    <meta name=\"keywords\" content=\"$Keywords\" />
17832    <meta name=\"description\" content=\"$Description\" />
17833    <title>
17834        $Title
17835    </title>
17836    <style type=\"text/css\">
17837    $Styles
17838    </style>
17839    <script type=\"text/javascript\" language=\"JavaScript\">
17840    <!--
17841    $Scripts
17842    -->
17843    </script>
17844    </head>";
17845}
17846
17847sub insertIDs($)
17848{
17849    my $Text = $_[0];
17850    while($Text=~/CONTENT_ID/)
17851    {
17852        if(int($Content_Counter)%2) {
17853            $ContentID -= 1;
17854        }
17855        $Text=~s/CONTENT_ID/c_$ContentID/;
17856        $ContentID += 1;
17857        $Content_Counter += 1;
17858    }
17859    return $Text;
17860}
17861
17862sub checkPreprocessedUnit($)
17863{
17864    my $Path = $_[0];
17865    my ($CurHeader, $CurHeaderName) = ("", "");
17866    my $CurClass = ""; # extra info
17867    open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n");
17868
17869    while(my $Line = <PREPROC>)
17870    { # detecting public and private constants
17871        if(substr($Line, 0, 1) eq "#")
17872        {
17873            chomp($Line);
17874            if($Line=~/\A\#\s+\d+\s+\"(.+)\"/)
17875            {
17876                $CurHeader = path_format($1, $OSgroup);
17877                $CurHeaderName = get_filename($CurHeader);
17878                $CurClass = "";
17879
17880                if(index($CurHeader, $TMP_DIR)==0) {
17881                    next;
17882                }
17883
17884                if(substr($CurHeaderName, 0, 1) eq "<")
17885                { # <built-in>, <command-line>, etc.
17886                    $CurHeaderName = "";
17887                    $CurHeader = "";
17888                }
17889
17890                if($ExtraInfo)
17891                {
17892                    if($CurHeaderName) {
17893                        $PreprocessedHeaders{$Version}{$CurHeader} = 1;
17894                    }
17895                }
17896            }
17897            if(not $ExtraDump)
17898            {
17899                if($CurHeaderName)
17900                {
17901                    if(not $Include_Neighbors{$Version}{$CurHeaderName}
17902                    and not $Registered_Headers{$Version}{$CurHeader})
17903                    { # not a target
17904                        next;
17905                    }
17906                    if(not is_target_header($CurHeaderName, 1)
17907                    and not is_target_header($CurHeaderName, 2))
17908                    { # user-defined header
17909                        next;
17910                    }
17911                }
17912            }
17913
17914            if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/)
17915            {
17916                my ($Name, $Value) = ($1, $2);
17917                if(not $Constants{$Version}{$Name}{"Access"})
17918                {
17919                    $Constants{$Version}{$Name}{"Access"} = "public";
17920                    $Constants{$Version}{$Name}{"Value"} = $Value;
17921                    if($CurHeaderName) {
17922                        $Constants{$Version}{$Name}{"Header"} = $CurHeaderName;
17923                    }
17924                }
17925            }
17926            elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) {
17927                $Constants{$Version}{$1}{"Access"} = "private";
17928            }
17929        }
17930        else
17931        {
17932            if(defined $ExtraDump)
17933            {
17934                if($Line=~/(\w+)\s*\(/)
17935                { # functions
17936                    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
17937                }
17938                #elsif($Line=~/(\w+)\s*;/)
17939                #{ # data
17940                #    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
17941                #}
17942                elsif($Line=~/(\A|\s)class\s+(\w+)/) {
17943                    $CurClass = $2;
17944                }
17945            }
17946        }
17947    }
17948    close(PREPROC);
17949    foreach my $Constant (keys(%{$Constants{$Version}}))
17950    {
17951        if($Constants{$Version}{$Constant}{"Access"} eq "private")
17952        {
17953            delete($Constants{$Version}{$Constant});
17954            next;
17955        }
17956        if(not $ExtraDump and ($Constant=~/_h\Z/i
17957        or isBuiltIn($Constants{$Version}{$Constant}{"Header"})))
17958        { # skip
17959            delete($Constants{$Version}{$Constant});
17960        }
17961        else {
17962            delete($Constants{$Version}{$Constant}{"Access"});
17963        }
17964    }
17965    if($Debug)
17966    {
17967        mkpath($DEBUG_PATH{$Version});
17968        copy($Path, $DEBUG_PATH{$Version}."/preprocessor.txt");
17969    }
17970}
17971
17972sub uncoverConstant($$)
17973{
17974    my ($LibVersion, $Constant) = @_;
17975    return "" if(not $LibVersion or not $Constant);
17976    return $Constant if(isCyclical(\@RecurConstant, $Constant));
17977    if(defined $Cache{"uncoverConstant"}{$LibVersion}{$Constant}) {
17978        return $Cache{"uncoverConstant"}{$LibVersion}{$Constant};
17979    }
17980
17981    if(defined $Constants{$LibVersion}{$Constant})
17982    {
17983        my $Value = $Constants{$LibVersion}{$Constant}{"Value"};
17984        if(defined $Constants{$LibVersion}{$Value})
17985        {
17986            push(@RecurConstant, $Constant);
17987            my $Uncovered = uncoverConstant($LibVersion, $Value);
17988            if($Uncovered ne "") {
17989                $Value = $Uncovered;
17990            }
17991            pop(@RecurConstant);
17992        }
17993
17994        # FIXME: uncover $Value using all the enum constants
17995        # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define)
17996        return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = $Value);
17997    }
17998    return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = "");
17999}
18000
18001sub simpleConstant($$)
18002{
18003    my ($LibVersion, $Value) = @_;
18004    if($Value=~/\W/)
18005    {
18006        my $Value_Copy = $Value;
18007        while($Value_Copy=~s/([a-z_]\w+)/\@/i)
18008        {
18009            my $Word = $1;
18010            if($Value!~/$Word\s*\(/)
18011            {
18012                my $Val = uncoverConstant($LibVersion, $Word);
18013                if($Val ne "")
18014                {
18015                    $Value=~s/\b$Word\b/$Val/g;
18016                }
18017            }
18018        }
18019    }
18020    return $Value;
18021}
18022
18023sub computeValue($)
18024{
18025    my $Value = $_[0];
18026
18027    if($Value=~/\A\((-?[\d]+)\)\Z/) {
18028        return $1;
18029    }
18030
18031    if($Value=~/\A[\d\-\+()]+\Z/) {
18032        return eval($Value);
18033    }
18034
18035    return $Value;
18036}
18037
18038my %IgnoreConstant = map {$_=>1} (
18039    "VERSION",
18040    "VERSIONCODE",
18041    "VERNUM",
18042    "VERS_INFO",
18043    "PATCHLEVEL",
18044    "INSTALLPREFIX",
18045    "VBUILD",
18046    "VPATCH",
18047    "VMINOR",
18048    "BUILD_STRING",
18049    "BUILD_TIME",
18050    "PACKAGE_STRING",
18051    "PRODUCTION",
18052    "CONFIGURE_COMMAND",
18053    "INSTALLDIR",
18054    "BINDIR",
18055    "CONFIG_FILE_PATH",
18056    "DATADIR",
18057    "EXTENSION_DIR",
18058    "INCLUDE_PATH",
18059    "LIBDIR",
18060    "LOCALSTATEDIR",
18061    "SBINDIR",
18062    "SYSCONFDIR",
18063    "RELEASE",
18064    "SOURCE_ID",
18065    "SUBMINOR",
18066    "MINOR",
18067    "MINNOR",
18068    "MINORVERSION",
18069    "MAJOR",
18070    "MAJORVERSION",
18071    "MICRO",
18072    "MICROVERSION",
18073    "BINARY_AGE",
18074    "INTERFACE_AGE",
18075    "CORE_ABI",
18076    "PATCH",
18077    "COPYRIGHT",
18078    "TIMESTAMP",
18079    "REVISION",
18080    "PACKAGE_TAG",
18081    "PACKAGEDATE",
18082    "NUMVERSION",
18083    "Release",
18084    "Version"
18085);
18086
18087sub constantFilter($$$)
18088{
18089    my ($Name, $Value, $Level) = @_;
18090
18091    if($Level eq "Binary")
18092    {
18093        if($Name=~/_t\Z/)
18094        { # __malloc_ptr_t
18095            return 1;
18096        }
18097        foreach (keys(%IgnoreConstant))
18098        {
18099            if($Name=~/(\A|_)$_(_|\Z)/)
18100            { # version
18101                return 1;
18102            }
18103            if(/\A[A-Z].*[a-z]\Z/)
18104            {
18105                if($Name=~/(\A|[a-z])$_([A-Z]|\Z)/)
18106                { # version
18107                    return 1;
18108                }
18109            }
18110        }
18111        if($Name=~/(\A|_)(lib|open|)$TargetLibraryShortName(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i)
18112        { # version
18113            return 1;
18114        }
18115        if($Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Value=~/[\/\\]\w+[\/\\]\w+/)
18116        { # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ...
18117            return 1;
18118        }
18119
18120        if($Value=~/\A["'].*['"]/i)
18121        { # string
18122            return 0;
18123        }
18124
18125        if($Value=~/\A[({]*\s*[a-z_]+\w*(\s+|[\|,])/i)
18126        { # static int gcry_pth_init
18127          # extern ABC
18128          # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE...
18129          # { H5FD_MEM_SUPER, H5FD_MEM_SUPER, ...
18130            return 1;
18131        }
18132        if($Value=~/\w+\s*\(/i)
18133        { # foo(p)
18134            return 1;
18135        }
18136        if($Value=~/\A[a-z_]+\w*\Z/i)
18137        { # asn1_node_st
18138          # __SMTH_P
18139            return 1;
18140        }
18141    }
18142
18143    return 0;
18144}
18145
18146sub mergeConstants($)
18147{
18148    my $Level = $_[0];
18149    foreach my $Constant (keys(%{$Constants{1}}))
18150    {
18151        if($SkipConstants{1}{$Constant})
18152        { # skipped by the user
18153            next;
18154        }
18155
18156        if(my $Header = $Constants{1}{$Constant}{"Header"})
18157        {
18158            if(not is_target_header($Header, 1)
18159            and not is_target_header($Header, 2))
18160            { # user-defined header
18161                next;
18162            }
18163        }
18164        else {
18165            next;
18166        }
18167
18168        my $Old_Value = uncoverConstant(1, $Constant);
18169
18170        if(constantFilter($Constant, $Old_Value, $Level))
18171        { # separate binary and source problems
18172            next;
18173        }
18174
18175        if(not defined $Constants{2}{$Constant}{"Value"})
18176        { # removed
18177            %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = (
18178                "Target"=>$Constant,
18179                "Old_Value"=>$Old_Value  );
18180            next;
18181        }
18182
18183        if($Constants{2}{$Constant}{"Value"} eq "")
18184        { # empty value
18185          # TODO: implement a rule
18186            next;
18187        }
18188
18189        my $New_Value = uncoverConstant(2, $Constant);
18190
18191        my $Old_Value_Pure = $Old_Value;
18192        my $New_Value_Pure = $New_Value;
18193
18194        $Old_Value_Pure=~s/(\W)\s+/$1/g;
18195        $Old_Value_Pure=~s/\s+(\W)/$1/g;
18196        $New_Value_Pure=~s/(\W)\s+/$1/g;
18197        $New_Value_Pure=~s/\s+(\W)/$1/g;
18198
18199        next if($New_Value_Pure eq "" or $Old_Value_Pure eq "");
18200
18201        if($New_Value_Pure ne $Old_Value_Pure)
18202        { # different values
18203            if(simpleConstant(1, $Old_Value) eq simpleConstant(2, $New_Value))
18204            { # complex values
18205                next;
18206            }
18207            if(computeValue($Old_Value) eq computeValue($New_Value))
18208            { # expressions
18209                next;
18210            }
18211            if(convert_integer($Old_Value) eq convert_integer($New_Value))
18212            { # 0x0001 and 0x1, 0x1 and 1 equal constants
18213                next;
18214            }
18215            if($Old_Value eq "0" and $New_Value eq "NULL")
18216            { # 0 => NULL
18217                next;
18218            }
18219            if($Old_Value eq "NULL" and $New_Value eq "0")
18220            { # NULL => 0
18221                next;
18222            }
18223            %{$CompatProblems_Constants{$Level}{$Constant}{"Changed_Constant"}} = (
18224                "Target"=>$Constant,
18225                "Old_Value"=>$Old_Value,
18226                "New_Value"=>$New_Value  );
18227        }
18228    }
18229
18230    foreach my $Constant (keys(%{$Constants{2}}))
18231    {
18232        if(not defined $Constants{1}{$Constant}{"Value"})
18233        {
18234            if($SkipConstants{2}{$Constant})
18235            { # skipped by the user
18236                next;
18237            }
18238
18239            if(my $Header = $Constants{2}{$Constant}{"Header"})
18240            {
18241                if(not is_target_header($Header, 1)
18242                and not is_target_header($Header, 2))
18243                { # user-defined header
18244                    next;
18245                }
18246            }
18247            else {
18248                next;
18249            }
18250
18251            my $New_Value = uncoverConstant(2, $Constant);
18252            if(not defined $New_Value or $New_Value eq "") {
18253                next;
18254            }
18255
18256            if(constantFilter($Constant, $New_Value, $Level))
18257            { # separate binary and source problems
18258                next;
18259            }
18260
18261            %{$CompatProblems_Constants{$Level}{$Constant}{"Added_Constant"}} = (
18262                "Target"=>$Constant,
18263                "New_Value"=>$New_Value  );
18264        }
18265    }
18266}
18267
18268sub convert_integer($)
18269{
18270    my $Value = $_[0];
18271    if($Value=~/\A0x[a-f0-9]+\Z/)
18272    { # hexadecimal
18273        return hex($Value);
18274    }
18275    elsif($Value=~/\A0[0-7]+\Z/)
18276    { # octal
18277        return oct($Value);
18278    }
18279    elsif($Value=~/\A0b[0-1]+\Z/)
18280    { # binary
18281        return oct($Value);
18282    }
18283    else {
18284        return $Value;
18285    }
18286}
18287
18288sub readSymbols($)
18289{
18290    my $LibVersion = $_[0];
18291    my @LibPaths = getSOPaths($LibVersion);
18292    if($#LibPaths==-1 and not $CheckHeadersOnly)
18293    {
18294        if($LibVersion==1)
18295        {
18296            printMsg("WARNING", "checking headers only");
18297            $CheckHeadersOnly = 1;
18298        }
18299        else {
18300            exitStatus("Error", "$SLIB_TYPE libraries are not found in ".$Descriptor{$LibVersion}{"Version"});
18301        }
18302    }
18303
18304    foreach my $LibPath (@LibPaths) {
18305        readSymbols_Lib($LibVersion, $LibPath, 0, "+Weak", 1, 1);
18306    }
18307
18308    if($CheckUndefined)
18309    {
18310        my %UndefinedLibs = ();
18311
18312        my @Libs = (keys(%{$Library_Symbol{$LibVersion}}), keys(%{$DepLibrary_Symbol{$LibVersion}}));
18313
18314        foreach my $LibName (sort @Libs)
18315        {
18316            if(defined $UndefinedSymbols{$LibVersion}{$LibName})
18317            {
18318                foreach my $Symbol (keys(%{$UndefinedSymbols{$LibVersion}{$LibName}}))
18319                {
18320                    if($Symbol_Library{$LibVersion}{$Symbol}
18321                    or $DepSymbol_Library{$LibVersion}{$Symbol})
18322                    { # exported by target library
18323                        next;
18324                    }
18325                    if(index($Symbol, '@')!=-1)
18326                    { # exported default symbol version (@@)
18327                        $Symbol=~s/\@/\@\@/;
18328                        if($Symbol_Library{$LibVersion}{$Symbol}
18329                        or $DepSymbol_Library{$LibVersion}{$Symbol}) {
18330                            next;
18331                        }
18332                    }
18333                    foreach my $Path (find_SymbolLibs($LibVersion, $Symbol)) {
18334                        $UndefinedLibs{$Path} = 1;
18335                    }
18336                }
18337            }
18338        }
18339        if($ExtraInfo)
18340        { # extra information for other tools
18341            if(my @Paths = sort keys(%UndefinedLibs))
18342            {
18343                my $LibString = "";
18344                my %Dirs = ();
18345                foreach (@Paths)
18346                {
18347                    $KnownLibs{$_} = 1;
18348                    my ($Dir, $Name) = separate_path($_);
18349
18350                    if(not grep {$Dir eq $_} (@{$SystemPaths{"lib"}})) {
18351                        $Dirs{esc($Dir)} = 1;
18352                    }
18353
18354                    $Name = parse_libname($Name, "name", $OStarget);
18355                    $Name=~s/\Alib//;
18356
18357                    $LibString .= " -l$Name";
18358                }
18359
18360                foreach my $Dir (sort {$b cmp $a} keys(%Dirs))
18361                {
18362                    $LibString = " -L".esc($Dir).$LibString;
18363                }
18364
18365                writeFile($ExtraInfo."/libs-string", $LibString);
18366            }
18367        }
18368    }
18369
18370    if($ExtraInfo) {
18371        writeFile($ExtraInfo."/lib-paths", join("\n", sort keys(%KnownLibs)));
18372    }
18373
18374    if(not $CheckHeadersOnly)
18375    {
18376        if($#LibPaths!=-1)
18377        {
18378            if(not keys(%{$Symbol_Library{$LibVersion}}))
18379            {
18380                printMsg("WARNING", "the set of public symbols in library(ies) is empty ($LibVersion)");
18381                printMsg("WARNING", "checking headers only");
18382                $CheckHeadersOnly = 1;
18383            }
18384        }
18385    }
18386
18387   # clean memory
18388   %SystemObjects = ();
18389}
18390
18391my %Prefix_Lib_Map=(
18392 # symbols for autodetecting library dependencies (by prefix)
18393    "pthread_" => ["libpthread"],
18394    "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"],
18395    "cairo_" => ["libcairo"],
18396    "gtk_" => ["libgtk-x11-2.0"],
18397    "atk_" => ["libatk-1.0"],
18398    "gdk_" => ["libgdk-x11-2.0"],
18399    "gl" => ["libGL"],
18400    "glu" => ["libGLU"],
18401    "popt" => ["libpopt"],
18402    "Py" => ["libpython"],
18403    "jpeg_" => ["libjpeg"],
18404    "BZ2_" => ["libbz2"],
18405    "Fc" => ["libfontconfig"],
18406    "Xft" => ["libXft"],
18407    "SSL_" => ["libssl"],
18408    "sem_" => ["libpthread"],
18409    "snd_" => ["libasound"],
18410    "art_" => ["libart_lgpl_2"],
18411    "dbus_g" => ["libdbus-glib-1"],
18412    "GOMP_" => ["libgomp"],
18413    "omp_" => ["libgomp"],
18414    "cms" => ["liblcms"]
18415);
18416
18417my %Pattern_Lib_Map=(
18418    "SL[a-z]" => ["libslang"]
18419);
18420
18421my %Symbol_Lib_Map=(
18422 # symbols for autodetecting library dependencies (by name)
18423    "pow" => "libm",
18424    "fmod" => "libm",
18425    "sin" => "libm",
18426    "floor" => "libm",
18427    "cos" => "libm",
18428    "dlopen" => "libdl",
18429    "deflate" => "libz",
18430    "inflate" => "libz",
18431    "move_panel" => "libpanel",
18432    "XOpenDisplay" => "libX11",
18433    "resize_term" => "libncurses",
18434    "clock_gettime" => "librt",
18435    "crypt" => "libcrypt"
18436);
18437
18438sub find_SymbolLibs($$)
18439{
18440    my ($LibVersion, $Symbol) = @_;
18441
18442    if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/)
18443    { # debug symbols
18444        return ();
18445    }
18446
18447    my %Paths = ();
18448
18449    if(my $LibName = $Symbol_Lib_Map{$Symbol})
18450    {
18451        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18452            $Paths{$Path} = 1;
18453        }
18454    }
18455
18456    if(my $SymbolPrefix = getPrefix($Symbol))
18457    {
18458        if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) {
18459            return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}};
18460        }
18461
18462        if(not keys(%Paths))
18463        {
18464            if(defined $Prefix_Lib_Map{$SymbolPrefix})
18465            {
18466                foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}})
18467                {
18468                    if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18469                        $Paths{$Path} = 1;
18470                    }
18471                }
18472            }
18473        }
18474
18475        if(not keys(%Paths))
18476        {
18477            foreach my $Prefix (sort keys(%Pattern_Lib_Map))
18478            {
18479                if($Symbol=~/\A$Prefix/)
18480                {
18481                    foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}})
18482                    {
18483                        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18484                            $Paths{$Path} = 1;
18485                        }
18486                    }
18487                }
18488            }
18489        }
18490
18491        if(not keys(%Paths))
18492        {
18493            if($SymbolPrefix)
18494            { # try to find a library by symbol prefix
18495                if($SymbolPrefix eq "inotify" and
18496                index($Symbol, "\@GLIBC")!=-1)
18497                {
18498                    if(my $Path = get_LibPath($LibVersion, "libc.$LIB_EXT")) {
18499                        $Paths{$Path} = 1;
18500                    }
18501                }
18502                else
18503                {
18504                    if(my $Path = get_LibPath_Prefix($LibVersion, $SymbolPrefix)) {
18505                        $Paths{$Path} = 1;
18506                    }
18507                }
18508            }
18509        }
18510
18511        if(my @Paths = keys(%Paths)) {
18512            $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths;
18513        }
18514    }
18515    return keys(%Paths);
18516}
18517
18518sub get_LibPath_Prefix($$)
18519{
18520    my ($LibVersion, $Prefix) = @_;
18521
18522    $Prefix = lc($Prefix);
18523    $Prefix=~s/[_]+\Z//g;
18524
18525    foreach ("-2", "2", "-1", "1", "")
18526    { # libgnome-2.so
18527      # libxml2.so
18528      # libdbus-1.so
18529        if(my $Path = get_LibPath($LibVersion, "lib".$Prefix.$_.".".$LIB_EXT)) {
18530            return $Path;
18531        }
18532    }
18533    return "";
18534}
18535
18536sub getPrefix($)
18537{
18538    my $Str = $_[0];
18539    if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/)
18540    { # XmuValidArea: Xmu
18541        return $1;
18542    }
18543    elsif($Str=~/\A([_]*[a-z]+)[A-Z]/)
18544    { # snfReadFont: snf
18545        return $1;
18546    }
18547    elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/)
18548    { # XRRTimes: XRR
18549        return $1;
18550    }
18551    elsif($Str=~/\A([_]*[a-z]{1,2}\d+)[a-z\d]*_[a-z]+/i)
18552    { # H5HF_delete: H5
18553        return $1;
18554    }
18555    elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i)
18556    { # alarm_event_add: alarm_
18557        return $1;
18558    }
18559    elsif($Str=~/\A(([a-z])\2{1,})/i)
18560    { # ffopen
18561        return $1;
18562    }
18563    return "";
18564}
18565
18566sub getSymbolSize($$)
18567{ # size from the shared library
18568    my ($Symbol, $LibVersion) = @_;
18569    return 0 if(not $Symbol);
18570    if(defined $Symbol_Library{$LibVersion}{$Symbol}
18571    and my $LibName = $Symbol_Library{$LibVersion}{$Symbol})
18572    {
18573        if(defined $Library_Symbol{$LibVersion}{$LibName}{$Symbol}
18574        and my $Size = $Library_Symbol{$LibVersion}{$LibName}{$Symbol})
18575        {
18576            if($Size<0) {
18577                return -$Size;
18578            }
18579        }
18580    }
18581    return 0;
18582}
18583
18584sub canonifyName($$)
18585{ # make TIFFStreamOpen(char const*, std::basic_ostream<char, std::char_traits<char> >*)
18586  # to be TIFFStreamOpen(char const*, std::basic_ostream<char>*)
18587    my ($Name, $Type) = @_;
18588
18589    # single
18590    while($Name=~/([^<>,]+),\s*$DEFAULT_STD_PARMS<([^<>,]+)>\s*/ and $1 eq $3)
18591    {
18592        my $P = $1;
18593        $Name=~s/\Q$P\E,\s*$DEFAULT_STD_PARMS<\Q$P\E>\s*/$P/g;
18594    }
18595
18596    # double
18597    if($Name=~/$DEFAULT_STD_PARMS/)
18598    {
18599        if($Type eq "S")
18600        {
18601            my ($ShortName, $FuncParams) = split_Signature($Name);
18602
18603            foreach my $FParam (separate_Params($FuncParams, 0, 0))
18604            {
18605                if(index($FParam, "<")!=-1)
18606                {
18607                    $FParam=~s/>([^<>]+)\Z/>/; # remove quals
18608                    my $FParam_N = canonifyName($FParam, "T");
18609                    if($FParam_N ne $FParam) {
18610                        $Name=~s/\Q$FParam\E/$FParam_N/g;
18611                    }
18612                }
18613            }
18614        }
18615        elsif($Type eq "T")
18616        {
18617            my ($ShortTmpl, $TmplParams) = template_Base($Name);
18618
18619            my @TParams = separate_Params($TmplParams, 0, 0);
18620            if($#TParams>=1)
18621            {
18622                my $FParam = $TParams[0];
18623                foreach my $Pos (1 .. $#TParams)
18624                {
18625                    my $TParam = $TParams[$Pos];
18626                    if($TParam=~/\A$DEFAULT_STD_PARMS<\Q$FParam\E\s*>\Z/) {
18627                        $Name=~s/\Q$FParam, $TParam\E\s*/$FParam/g;
18628                    }
18629                }
18630            }
18631        }
18632    }
18633    if($Type eq "S") {
18634        return formatName($Name, "S");
18635    }
18636    return $Name;
18637}
18638
18639sub translateSymbols(@)
18640{
18641    my $LibVersion = pop(@_);
18642    my (@MnglNames1, @MnglNames2, @UnmangledNames) = ();
18643    foreach my $Symbol (sort @_)
18644    {
18645        if(index($Symbol, "_Z")==0)
18646        {
18647            next if($tr_name{$Symbol});
18648            $Symbol=~s/[\@\$]+(.*)\Z//;
18649            push(@MnglNames1, $Symbol);
18650        }
18651        elsif(index($Symbol, "?")==0)
18652        {
18653            next if($tr_name{$Symbol});
18654            push(@MnglNames2, $Symbol);
18655        }
18656        else
18657        { # not mangled
18658            $tr_name{$Symbol} = $Symbol;
18659            $mangled_name_gcc{$Symbol} = $Symbol;
18660            $mangled_name{$LibVersion}{$Symbol} = $Symbol;
18661        }
18662    }
18663    if($#MnglNames1 > -1)
18664    { # GCC names
18665        @UnmangledNames = reverse(unmangleArray(@MnglNames1));
18666        foreach my $MnglName (@MnglNames1)
18667        {
18668            if(my $Unmangled = pop(@UnmangledNames))
18669            {
18670                $tr_name{$MnglName} = canonifyName($Unmangled, "S");
18671                if(not $mangled_name_gcc{$tr_name{$MnglName}}) {
18672                    $mangled_name_gcc{$tr_name{$MnglName}} = $MnglName;
18673                }
18674                if(index($MnglName, "_ZTV")==0
18675                and $tr_name{$MnglName}=~/vtable for (.+)/)
18676                { # bind class name and v-table symbol
18677                    my $ClassName = $1;
18678                    $ClassVTable{$ClassName} = $MnglName;
18679                    $VTableClass{$MnglName} = $ClassName;
18680                }
18681            }
18682        }
18683    }
18684    if($#MnglNames2 > -1)
18685    { # MSVC names
18686        @UnmangledNames = reverse(unmangleArray(@MnglNames2));
18687        foreach my $MnglName (@MnglNames2)
18688        {
18689            if(my $Unmangled = pop(@UnmangledNames))
18690            {
18691                $tr_name{$MnglName} = formatName($Unmangled, "S");
18692                $mangled_name{$LibVersion}{$tr_name{$MnglName}} = $MnglName;
18693            }
18694        }
18695    }
18696    return \%tr_name;
18697}
18698
18699sub link_symbol($$$)
18700{
18701    my ($Symbol, $RunWith, $Deps) = @_;
18702    if(link_symbol_internal($Symbol, $RunWith, \%Symbol_Library)) {
18703        return 1;
18704    }
18705    if($Deps eq "+Deps")
18706    { # check the dependencies
18707        if(link_symbol_internal($Symbol, $RunWith, \%DepSymbol_Library)) {
18708            return 1;
18709        }
18710    }
18711    return 0;
18712}
18713
18714sub link_symbol_internal($$$)
18715{
18716    my ($Symbol, $RunWith, $Where) = @_;
18717    return 0 if(not $Where or not $Symbol);
18718    if($Where->{$RunWith}{$Symbol})
18719    { # the exact match by symbol name
18720        return 1;
18721    }
18722    if(my $VSym = $SymVer{$RunWith}{$Symbol})
18723    { # indirect symbol version, i.e.
18724      # foo_old and its symlink foo@v (or foo@@v)
18725      # foo_old may be in symtab table
18726        if($Where->{$RunWith}{$VSym}) {
18727            return 1;
18728        }
18729    }
18730    my ($Sym, $Spec, $Ver) = separate_symbol($Symbol);
18731    if($Sym and $Ver)
18732    { # search for the symbol with the same version
18733      # or without version
18734        if($Where->{$RunWith}{$Sym})
18735        { # old: foo@v|foo@@v
18736          # new: foo
18737            return 1;
18738        }
18739        if($Where->{$RunWith}{$Sym."\@".$Ver})
18740        { # old: foo|foo@@v
18741          # new: foo@v
18742            return 1;
18743        }
18744        if($Where->{$RunWith}{$Sym."\@\@".$Ver})
18745        { # old: foo|foo@v
18746          # new: foo@@v
18747            return 1;
18748        }
18749    }
18750    return 0;
18751}
18752
18753sub readSymbols_App($)
18754{
18755    my $Path = $_[0];
18756    return () if(not $Path);
18757    my @Imported = ();
18758    if($OStarget eq "macos")
18759    {
18760        my $NM = get_CmdPath("nm");
18761        if(not $NM) {
18762            exitStatus("Not_Found", "can't find \"nm\"");
18763        }
18764        open(APP, "$NM -g \"$Path\" 2>\"$TMP_DIR/null\" |");
18765        while(<APP>)
18766        {
18767            if(/ U _([\w\$]+)\s*\Z/) {
18768                push(@Imported, $1);
18769            }
18770        }
18771        close(APP);
18772    }
18773    elsif($OStarget eq "windows")
18774    {
18775        my $DumpBinCmd = get_CmdPath("dumpbin");
18776        if(not $DumpBinCmd) {
18777            exitStatus("Not_Found", "can't find \"dumpbin.exe\"");
18778        }
18779        open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TMP_DIR/null\" |");
18780        while(<APP>)
18781        {
18782            if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) {
18783                push(@Imported, $1);
18784            }
18785        }
18786        close(APP);
18787    }
18788    else
18789    {
18790        my $ReadelfCmd = get_CmdPath("readelf");
18791        if(not $ReadelfCmd) {
18792            exitStatus("Not_Found", "can't find \"readelf\"");
18793        }
18794        open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TMP_DIR/null\" |");
18795        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
18796        while(<APP>)
18797        {
18798            if(defined $symtab)
18799            { # do nothing with symtab
18800                if(index($_, "'.dynsym'")!=-1)
18801                { # dynamic table
18802                    $symtab = undef;
18803                }
18804            }
18805            elsif(index($_, "'.symtab'")!=-1)
18806            { # symbol table
18807                $symtab = 1;
18808            }
18809            elsif(my @Info = readline_ELF($_))
18810            {
18811                my ($Ndx, $Symbol) = ($Info[5], $Info[6]);
18812                if($Ndx eq "UND")
18813                { # only imported symbols
18814                    push(@Imported, $Symbol);
18815                }
18816            }
18817        }
18818        close(APP);
18819    }
18820    return @Imported;
18821}
18822
18823my %ELF_BIND = map {$_=>1} (
18824    "WEAK",
18825    "GLOBAL"
18826);
18827
18828my %ELF_TYPE = map {$_=>1} (
18829    "FUNC",
18830    "IFUNC",
18831    "OBJECT",
18832    "COMMON"
18833);
18834
18835my %ELF_VIS = map {$_=>1} (
18836    "DEFAULT",
18837    "PROTECTED"
18838);
18839
18840sub readline_ELF($)
18841{ # read the line of 'readelf' output corresponding to the symbol
18842    my @Info = split(/\s+/, $_[0]);
18843    #  Num:   Value      Size Type   Bind   Vis       Ndx  Name
18844    #  3629:  000b09c0   32   FUNC   GLOBAL DEFAULT   13   _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4
18845    #  135:   00000000    0   FUNC   GLOBAL DEFAULT   UND  av_image_fill_pointers@LIBAVUTIL_52 (3)
18846    shift(@Info); # spaces
18847    shift(@Info); # num
18848
18849    if($#Info==7)
18850    { # UND SYMBOL (N)
18851        if($Info[7]=~/\(\d+\)/) {
18852            pop(@Info);
18853        }
18854    }
18855
18856    if($#Info!=6)
18857    { # other lines
18858        return ();
18859    }
18860    return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND");
18861    return () if(not defined $ELF_BIND{$Info[3]});
18862    return () if(not defined $ELF_VIS{$Info[4]});
18863    if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/)
18864    { # 1272: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS CXXABI_1.3
18865        return ();
18866    }
18867    if($OStarget eq "symbian")
18868    { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll
18869        if(index($Info[6], "_._.absent_export_")!=-1)
18870        { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll
18871            return ();
18872        }
18873        $Info[6]=~s/\@.+//g; # remove version
18874    }
18875    if(index($Info[2], "0x") == 0)
18876    { # size == 0x3d158
18877        $Info[2] = hex($Info[2]);
18878    }
18879    return @Info;
18880}
18881
18882sub get_LibPath($$)
18883{
18884    my ($LibVersion, $Name) = @_;
18885    return "" if(not $LibVersion or not $Name);
18886    if(defined $Cache{"get_LibPath"}{$LibVersion}{$Name}) {
18887        return $Cache{"get_LibPath"}{$LibVersion}{$Name};
18888    }
18889    return ($Cache{"get_LibPath"}{$LibVersion}{$Name} = get_LibPath_I($LibVersion, $Name));
18890}
18891
18892sub get_LibPath_I($$)
18893{
18894    my ($LibVersion, $Name) = @_;
18895    if(is_abs($Name))
18896    {
18897        if(-f $Name)
18898        { # absolute path
18899            return $Name;
18900        }
18901        else
18902        { # broken
18903            return "";
18904        }
18905    }
18906    if(defined $RegisteredObjects{$LibVersion}{$Name})
18907    { # registered paths
18908        return $RegisteredObjects{$LibVersion}{$Name};
18909    }
18910    if(defined $RegisteredSONAMEs{$LibVersion}{$Name})
18911    { # registered paths
18912        return $RegisteredSONAMEs{$LibVersion}{$Name};
18913    }
18914    if(my $DefaultPath = $DyLib_DefaultPath{$Name})
18915    { # ldconfig default paths
18916        return $DefaultPath;
18917    }
18918    foreach my $Dir (@DefaultLibPaths, @{$SystemPaths{"lib"}})
18919    { # search in default linker directories
18920      # and then in all system paths
18921        if(-f $Dir."/".$Name) {
18922            return join_P($Dir,$Name);
18923        }
18924    }
18925    if(not defined $Cache{"checkSystemFiles"}) {
18926        checkSystemFiles();
18927    }
18928    if(my @AllObjects = keys(%{$SystemObjects{$Name}})) {
18929        return $AllObjects[0];
18930    }
18931    if(my $ShortName = parse_libname($Name, "name+ext", $OStarget))
18932    {
18933        if($ShortName ne $Name)
18934        { # FIXME: check this case
18935            if(my $Path = get_LibPath($LibVersion, $ShortName)) {
18936                return $Path;
18937            }
18938        }
18939    }
18940    # can't find
18941    return "";
18942}
18943
18944sub readSymbols_Lib($$$$$$)
18945{
18946    my ($LibVersion, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_;
18947    return () if(not $LibVersion or not $Lib_Path);
18948
18949    my $Real_Path = realpath($Lib_Path);
18950
18951    if(not $Real_Path)
18952    { # broken link
18953        return ();
18954    }
18955
18956    my $Lib_Name = get_filename($Real_Path);
18957
18958    if($ExtraInfo)
18959    {
18960        $KnownLibs{$Real_Path} = 1;
18961        $KnownLibs{$Lib_Path} = 1; # links
18962    }
18963
18964    if($IsNeededLib)
18965    {
18966        if($CheckedDyLib{$LibVersion}{$Lib_Name}) {
18967            return ();
18968        }
18969    }
18970    return () if(isCyclical(\@RecurLib, $Lib_Name) or $#RecurLib>=1);
18971    $CheckedDyLib{$LibVersion}{$Lib_Name} = 1;
18972
18973    push(@RecurLib, $Lib_Name);
18974    my (%Value_Interface, %Interface_Value, %NeededLib) = ();
18975    my $Lib_ShortName = parse_libname($Lib_Name, "name+ext", $OStarget);
18976
18977    if(not $IsNeededLib)
18978    { # special cases: libstdc++ and libc
18979        if(my $ShortName = parse_libname($Lib_Name, "short", $OStarget))
18980        {
18981            if($ShortName eq "libstdc++")
18982            { # libstdc++.so.6
18983                $STDCXX_TESTING = 1;
18984            }
18985            elsif($ShortName eq "libc")
18986            { # libc-2.11.3.so
18987                $GLIBC_TESTING = 1;
18988            }
18989        }
18990    }
18991    my $DebugPath = "";
18992    if($Debug and not $DumpSystem)
18993    { # debug mode
18994        $DebugPath = $DEBUG_PATH{$LibVersion}."/libs/".get_filename($Lib_Path).".txt";
18995        mkpath(get_dirname($DebugPath));
18996    }
18997    if($OStarget eq "macos")
18998    { # Mac OS X: *.dylib, *.a
18999        my $NM = get_CmdPath("nm");
19000        if(not $NM) {
19001            exitStatus("Not_Found", "can't find \"nm\"");
19002        }
19003        $NM .= " -g \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19004        if($DebugPath)
19005        { # debug mode
19006          # write to file
19007            system($NM." >\"$DebugPath\"");
19008            open(LIB, $DebugPath);
19009        }
19010        else
19011        { # write to pipe
19012            open(LIB, $NM." |");
19013        }
19014        while(<LIB>)
19015        {
19016            if($CheckUndefined)
19017            {
19018                if(not $IsNeededLib)
19019                {
19020                    if(/ U _([\w\$]+)\s*\Z/)
19021                    {
19022                        $UndefinedSymbols{$LibVersion}{$Lib_Name}{$1} = 0;
19023                        next;
19024                    }
19025                }
19026            }
19027
19028            if(/ [STD] _([\w\$]+)\s*\Z/)
19029            {
19030                my $Symbol = $1;
19031                if($IsNeededLib)
19032                {
19033                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19034                    {
19035                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19036                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
19037                    }
19038                }
19039                else
19040                {
19041                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19042                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
19043                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19044                    {
19045                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
19046                            setLanguage($LibVersion, "C++");
19047                        }
19048                    }
19049                }
19050            }
19051        }
19052        close(LIB);
19053
19054        if($Deps)
19055        {
19056            if($LIB_TYPE eq "dynamic")
19057            { # dependencies
19058
19059                my $OtoolCmd = get_CmdPath("otool");
19060                if(not $OtoolCmd) {
19061                    exitStatus("Not_Found", "can't find \"otool\"");
19062                }
19063
19064                open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
19065                while(<LIB>)
19066                {
19067                    if(/\s*([\/\\].+\.$LIB_EXT)\s*/
19068                    and $1 ne $Lib_Path) {
19069                        $NeededLib{$1} = 1;
19070                    }
19071                }
19072                close(LIB);
19073            }
19074        }
19075    }
19076    elsif($OStarget eq "windows")
19077    { # Windows *.dll, *.lib
19078        my $DumpBinCmd = get_CmdPath("dumpbin");
19079        if(not $DumpBinCmd) {
19080            exitStatus("Not_Found", "can't find \"dumpbin\"");
19081        }
19082        $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TMP_DIR/null";
19083        if($DebugPath)
19084        { # debug mode
19085          # write to file
19086            system($DumpBinCmd." >\"$DebugPath\"");
19087            open(LIB, $DebugPath);
19088        }
19089        else
19090        { # write to pipe
19091            open(LIB, $DumpBinCmd." |");
19092        }
19093        while(<LIB>)
19094        { # 1197 4AC 0000A620 SetThreadStackGuarantee
19095          # 1198 4AD          SetThreadToken (forwarded to ...)
19096          # 3368 _o2i_ECPublicKey
19097          # 1 0 00005B30 ??0?N = ... (with pdb)
19098            if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i
19099            or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/
19100            or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/)
19101            { # dynamic, static and forwarded symbols
19102                my $realname = $1;
19103                if($IsNeededLib)
19104                {
19105                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19106                    {
19107                        $DepSymbol_Library{$LibVersion}{$realname} = $Lib_Name;
19108                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
19109                    }
19110                }
19111                else
19112                {
19113                    $Symbol_Library{$LibVersion}{$realname} = $Lib_Name;
19114                    $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
19115                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19116                    {
19117                        if(index($realname, "_Z")==0 or index($realname, "?")==0) {
19118                            setLanguage($LibVersion, "C++");
19119                        }
19120                    }
19121                }
19122            }
19123        }
19124        close(LIB);
19125
19126        if($Deps)
19127        {
19128            if($LIB_TYPE eq "dynamic")
19129            { # dependencies
19130                open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
19131                while(<LIB>)
19132                {
19133                    if(/\s*([^\s]+?\.$LIB_EXT)\s*/i
19134                    and $1 ne $Lib_Path) {
19135                        $NeededLib{path_format($1, $OSgroup)} = 1;
19136                    }
19137                }
19138                close(LIB);
19139            }
19140        }
19141    }
19142    else
19143    { # Unix; *.so, *.a
19144      # Symbian: *.dso, *.lib
19145        my $ReadelfCmd = get_CmdPath("readelf");
19146        if(not $ReadelfCmd) {
19147            exitStatus("Not_Found", "can't find \"readelf\"");
19148        }
19149        my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19150        if($DebugPath)
19151        { # debug mode
19152          # write to file
19153            system($Cmd." >\"$DebugPath\"");
19154            open(LIB, $DebugPath);
19155        }
19156        else
19157        { # write to pipe
19158            open(LIB, $Cmd." |");
19159        }
19160        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
19161        while(<LIB>)
19162        {
19163            if($LIB_TYPE eq "dynamic")
19164            { # dynamic library specifics
19165                if(defined $symtab)
19166                {
19167                    if(index($_, "'.dynsym'")!=-1)
19168                    { # dynamic table
19169                        $symtab = undef;
19170                    }
19171                    # do nothing with symtab
19172                    next;
19173                }
19174                elsif(index($_, "'.symtab'")!=-1)
19175                { # symbol table
19176                    $symtab = 1;
19177                    next;
19178                }
19179            }
19180            if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_))
19181            { # read ELF entry
19182                if($Ndx eq "UND")
19183                { # ignore interfaces that are imported from somewhere else
19184                    if($CheckUndefined)
19185                    {
19186                        if(not $IsNeededLib) {
19187                            $UndefinedSymbols{$LibVersion}{$Lib_Name}{$Symbol} = 0;
19188                        }
19189                    }
19190                    next;
19191                }
19192                if($Bind eq "WEAK")
19193                {
19194                    $WeakSymbols{$LibVersion}{$Symbol} = 1;
19195                    if($Weak eq "-Weak")
19196                    { # skip WEAK symbols
19197                        next;
19198                    }
19199                }
19200                my $Short = $Symbol;
19201                $Short=~s/\@.+//g;
19202                if($Type eq "OBJECT")
19203                { # global data
19204                    $GlobalDataObject{$LibVersion}{$Symbol} = $Size;
19205                    $GlobalDataObject{$LibVersion}{$Short} = $Size;
19206                }
19207                if($IsNeededLib)
19208                {
19209                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19210                    {
19211                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19212                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19213                    }
19214                }
19215                else
19216                {
19217                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19218                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19219                    if($Vers)
19220                    {
19221                        if($LIB_EXT eq "so")
19222                        { # value
19223                            $Interface_Value{$LibVersion}{$Symbol} = $Value;
19224                            $Value_Interface{$LibVersion}{$Value}{$Symbol} = 1;
19225                        }
19226                    }
19227                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19228                    {
19229                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
19230                            setLanguage($LibVersion, "C++");
19231                        }
19232                    }
19233                }
19234            }
19235        }
19236        close(LIB);
19237
19238        if($Deps and $LIB_TYPE eq "dynamic")
19239        { # dynamic library specifics
19240            $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19241            open(LIB, $Cmd." |");
19242
19243            while(<LIB>)
19244            {
19245                if(/NEEDED.+\[([^\[\]]+)\]/)
19246                { # dependencies:
19247                  # 0x00000001 (NEEDED) Shared library: [libc.so.6]
19248                    $NeededLib{$1} = 1;
19249                }
19250            }
19251
19252            close(LIB);
19253        }
19254    }
19255    if($Vers)
19256    {
19257        if(not $IsNeededLib and $LIB_EXT eq "so")
19258        { # get symbol versions
19259            my %Found = ();
19260
19261            # by value
19262            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19263            {
19264                next if(index($Symbol,"\@")==-1);
19265                if(my $Value = $Interface_Value{$LibVersion}{$Symbol})
19266                {
19267                    foreach my $Symbol_SameValue (keys(%{$Value_Interface{$LibVersion}{$Value}}))
19268                    {
19269                        if($Symbol_SameValue ne $Symbol
19270                        and index($Symbol_SameValue,"\@")==-1)
19271                        {
19272                            $SymVer{$LibVersion}{$Symbol_SameValue} = $Symbol;
19273                            $Found{$Symbol} = 1;
19274                            last;
19275                        }
19276                    }
19277                }
19278            }
19279
19280            # default
19281            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19282            {
19283                next if(defined $Found{$Symbol});
19284                next if(index($Symbol,"\@\@")==-1);
19285
19286                if($Symbol=~/\A([^\@]*)\@\@/
19287                and not $SymVer{$LibVersion}{$1})
19288                {
19289                    $SymVer{$LibVersion}{$1} = $Symbol;
19290                    $Found{$Symbol} = 1;
19291                }
19292            }
19293
19294            # non-default
19295            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19296            {
19297                next if(defined $Found{$Symbol});
19298                next if(index($Symbol,"\@")==-1);
19299
19300                if($Symbol=~/\A([^\@]*)\@([^\@]*)/
19301                and not $SymVer{$LibVersion}{$1})
19302                {
19303                    $SymVer{$LibVersion}{$1} = $Symbol;
19304                    $Found{$Symbol} = 1;
19305                }
19306            }
19307        }
19308    }
19309    if($Deps)
19310    {
19311        foreach my $DyLib (sort keys(%NeededLib))
19312        {
19313            $Library_Needed{$LibVersion}{$Lib_Name}{get_filename($DyLib)} = 1;
19314
19315            if(my $DepPath = get_LibPath($LibVersion, $DyLib))
19316            {
19317                if(not $CheckedDyLib{$LibVersion}{get_filename($DepPath)}) {
19318                    readSymbols_Lib($LibVersion, $DepPath, 1, "+Weak", $Deps, $Vers);
19319                }
19320            }
19321        }
19322    }
19323    pop(@RecurLib);
19324    return $Library_Symbol{$LibVersion};
19325}
19326
19327sub get_prefixes($)
19328{
19329    my %Prefixes = ();
19330    get_prefixes_I([$_[0]], \%Prefixes);
19331    return keys(%Prefixes);
19332}
19333
19334sub get_prefixes_I($$)
19335{
19336    foreach my $P (@{$_[0]})
19337    {
19338        my @Parts = reverse(split(/[\/\\]+/, $P));
19339        my $Name = $Parts[0];
19340        foreach (1 .. $#Parts)
19341        {
19342            $_[1]->{$Name}{$P} = 1;
19343            last if($_>4 or $Parts[$_] eq "include");
19344            $Name = $Parts[$_].$SLASH.$Name;
19345        }
19346    }
19347}
19348
19349sub checkSystemFiles()
19350{
19351    $Cache{"checkSystemFiles"} = 1;
19352
19353    my @SysHeaders = ();
19354
19355    foreach my $DevelPath (@{$SystemPaths{"lib"}})
19356    {
19357        next if(not -d $DevelPath);
19358
19359        my @Files = cmd_find($DevelPath,"f");
19360        foreach my $Link (cmd_find($DevelPath,"l"))
19361        { # add symbolic links
19362            if(-f $Link) {
19363                push(@Files, $Link);
19364            }
19365        }
19366
19367        # search for headers in /usr/lib
19368        my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files;
19369        @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers;
19370        push(@SysHeaders, @Headers);
19371
19372        # search for libraries in /usr/lib (including symbolic links)
19373        my @Libs = grep { /\.$LIB_EXT[0-9.]*\Z/ } @Files;
19374        foreach my $Path (@Libs)
19375        {
19376            my $N = get_filename($Path);
19377            $SystemObjects{$N}{$Path} = 1;
19378            $SystemObjects{parse_libname($N, "name+ext", $OStarget)}{$Path} = 1;
19379        }
19380    }
19381
19382    foreach my $DevelPath (@{$SystemPaths{"include"}})
19383    {
19384        next if(not -d $DevelPath);
19385        # search for all header files in the /usr/include
19386        # with or without extension (ncurses.h, QtCore, ...)
19387        push(@SysHeaders, cmd_find($DevelPath,"f"));
19388        foreach my $Link (cmd_find($DevelPath,"l"))
19389        { # add symbolic links
19390            if(-f $Link) {
19391                push(@SysHeaders, $Link);
19392            }
19393        }
19394    }
19395    get_prefixes_I(\@SysHeaders, \%SystemHeaders);
19396}
19397
19398sub getSOPaths($)
19399{
19400    my $LibVersion = $_[0];
19401    my @Paths = ();
19402    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Libs"}))
19403    {
19404        if(not -e $Dest) {
19405            exitStatus("Access_Error", "can't access \'$Dest\'");
19406        }
19407        $Dest = get_abs_path($Dest);
19408        my @SoPaths_Dest = getSOPaths_Dest($Dest, $LibVersion);
19409        foreach (@SoPaths_Dest) {
19410            push(@Paths, $_);
19411        }
19412    }
19413    return sort @Paths;
19414}
19415
19416sub skipLib($$)
19417{
19418    my ($Path, $LibVersion) = @_;
19419    return 1 if(not $Path or not $LibVersion);
19420    my $Name = get_filename($Path);
19421    if($SkipLibs{$LibVersion}{"Name"}{$Name}) {
19422        return 1;
19423    }
19424    my $ShortName = parse_libname($Name, "name+ext", $OStarget);
19425    if($SkipLibs{$LibVersion}{"Name"}{$ShortName}) {
19426        return 1;
19427    }
19428    foreach my $Dir (keys(%{$SkipLibs{$LibVersion}{"Path"}}))
19429    {
19430        if($Path=~/\Q$Dir\E([\/\\]|\Z)/) {
19431            return 1;
19432        }
19433    }
19434    foreach my $P (keys(%{$SkipLibs{$LibVersion}{"Pattern"}}))
19435    {
19436        if($Name=~/$P/) {
19437            return 1;
19438        }
19439        if($P=~/[\/\\]/ and $Path=~/$P/) {
19440            return 1;
19441        }
19442    }
19443    return 0;
19444}
19445
19446sub specificHeader($$)
19447{
19448    my ($Header, $Spec) = @_;
19449    my $Name = get_filename($Header);
19450
19451    if($Spec eq "windows")
19452    {# MS Windows
19453        return 1 if($Name=~/(\A|[._-])(win|wince|wnt)(\d\d|[._-]|\Z)/i);
19454        return 1 if($Name=~/([._-]w|win)(32|64)/i);
19455        return 1 if($Name=~/\A(Win|Windows)[A-Z]/);
19456        return 1 if($Name=~/\A(w|win|windows)(32|64|\.)/i);
19457        my @Dirs = (
19458            "win32",
19459            "win64",
19460            "win",
19461            "windows",
19462            "msvcrt"
19463        ); # /gsf-win32/
19464        if(my $DIRs = join("|", @Dirs)) {
19465            return 1 if($Header=~/[\/\\](|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19466        }
19467    }
19468    elsif($Spec eq "macos")
19469    { # Mac OS
19470        return 1 if($Name=~/(\A|[_-])mac[._-]/i);
19471    }
19472
19473    return 0;
19474}
19475
19476sub skipAlienHeader($)
19477{
19478    my $Path = $_[0];
19479    my $Name = get_filename($Path);
19480    my $Dir = get_dirname($Path);
19481
19482    if($Tolerance=~/2/)
19483    { # 2 - skip internal headers
19484        my @Terms = (
19485            "p",
19486            "priv",
19487            "int",
19488            "impl",
19489            "implementation",
19490            "internal",
19491            "private",
19492            "old",
19493            "compat",
19494            "debug",
19495            "test",
19496            "gen"
19497        );
19498
19499        my @Dirs = (
19500            "private",
19501            "priv",
19502            "port",
19503            "impl",
19504            "internal",
19505            "detail",
19506            "details",
19507            "old",
19508            "compat",
19509            "debug",
19510            "config",
19511            "compiler",
19512            "platform",
19513            "test"
19514        );
19515
19516        if(my $TERMs = join("|", @Terms)) {
19517            return 1 if($Name=~/(\A|[._-])($TERMs)([._-]|\Z)/i);
19518        }
19519        if(my $DIRs = join("|", @Dirs)) {
19520            return 1 if($Dir=~/(\A|[\/\\])(|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19521        }
19522
19523        return 1 if($Name=~/[a-z](Imp|Impl|I|P)(\.|\Z)/);
19524    }
19525
19526    if($Tolerance=~/1/)
19527    { # 1 - skip non-Linux headers
19528        if($OSgroup ne "windows")
19529        {
19530            if(specificHeader($Path, "windows")) {
19531                return 1;
19532            }
19533        }
19534        if($OSgroup ne "macos")
19535        {
19536            if(specificHeader($Path, "macos")) {
19537                return 1;
19538            }
19539        }
19540    }
19541
19542    # valid
19543    return 0;
19544}
19545
19546sub skipHeader($$)
19547{
19548    my ($Path, $LibVersion) = @_;
19549    return 1 if(not $Path or not $LibVersion);
19550    if(defined $Cache{"skipHeader"}{$Path}) {
19551        return $Cache{"skipHeader"}{$Path};
19552    }
19553    if(defined $Tolerance and $Tolerance=~/1|2/)
19554    { # --tolerant
19555        if(skipAlienHeader($Path)) {
19556            return ($Cache{"skipHeader"}{$Path} = 1);
19557        }
19558    }
19559    if(not keys(%{$SkipHeaders{$LibVersion}})) {
19560        return 0;
19561    }
19562    return ($Cache{"skipHeader"}{$Path} = skipHeader_I(@_));
19563}
19564
19565sub skipHeader_I($$)
19566{ # returns:
19567  #  1 - if header should NOT be included and checked
19568  #  2 - if header should NOT be included, but should be checked
19569    my ($Path, $LibVersion) = @_;
19570    my $Name = get_filename($Path);
19571    if(my $Kind = $SkipHeaders{$LibVersion}{"Name"}{$Name}) {
19572        return $Kind;
19573    }
19574    foreach my $D (sort {$SkipHeaders{$LibVersion}{"Path"}{$a} cmp $SkipHeaders{$LibVersion}{"Path"}{$b}}
19575    keys(%{$SkipHeaders{$LibVersion}{"Path"}}))
19576    {
19577        if(index($Path, $D)!=-1)
19578        {
19579            if($Path=~/\Q$D\E([\/\\]|\Z)/) {
19580                return $SkipHeaders{$LibVersion}{"Path"}{$D};
19581            }
19582        }
19583    }
19584    foreach my $P (sort {$SkipHeaders{$LibVersion}{"Pattern"}{$a} cmp $SkipHeaders{$LibVersion}{"Pattern"}{$b}}
19585    keys(%{$SkipHeaders{$LibVersion}{"Pattern"}}))
19586    {
19587        if(my $Kind = $SkipHeaders{$LibVersion}{"Pattern"}{$P})
19588        {
19589            if($Name=~/$P/) {
19590                return $Kind;
19591            }
19592            if($P=~/[\/\\]/ and $Path=~/$P/) {
19593                return $Kind;
19594            }
19595        }
19596    }
19597
19598    return 0;
19599}
19600
19601sub registerObject_Dir($$)
19602{
19603    my ($Dir, $LibVersion) = @_;
19604    if(grep {$_ eq $Dir} @{$SystemPaths{"lib"}})
19605    { # system directory
19606        return;
19607    }
19608    if($RegisteredObject_Dirs{$LibVersion}{$Dir})
19609    { # already registered
19610        return;
19611    }
19612    foreach my $Path (find_libs($Dir,"",1))
19613    {
19614        next if(ignore_path($Path));
19615        next if(skipLib($Path, $LibVersion));
19616        registerObject($Path, $LibVersion);
19617    }
19618    $RegisteredObject_Dirs{$LibVersion}{$Dir} = 1;
19619}
19620
19621sub registerObject($$)
19622{
19623    my ($Path, $LibVersion) = @_;
19624
19625    my $Name = get_filename($Path);
19626    $RegisteredObjects{$LibVersion}{$Name} = $Path;
19627    if($OStarget=~/linux|bsd|gnu/i)
19628    {
19629        if(my $SONAME = getSONAME($Path)) {
19630            $RegisteredSONAMEs{$LibVersion}{$SONAME} = $Path;
19631        }
19632    }
19633    if(my $Short = parse_libname($Name, "name+ext", $OStarget)) {
19634        $RegisteredObjects_Short{$LibVersion}{$Short} = $Path;
19635    }
19636
19637    if(not $CheckedArch{$LibVersion} and -f $Path)
19638    {
19639        if(my $ObjArch = getArch_Object($Path))
19640        {
19641            if($ObjArch ne getArch_GCC($LibVersion))
19642            { # translation unit dump generated by the GCC compiler should correspond to the input objects
19643                $CheckedArch{$LibVersion} = 1;
19644                printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option.");
19645            }
19646        }
19647    }
19648}
19649
19650sub getArch_Object($)
19651{
19652    my $Path = $_[0];
19653
19654    my %MachineType = (
19655        "14C" => "x86",
19656        "8664" => "x86_64",
19657        "1C0" => "arm",
19658        "200" => "ia64"
19659    );
19660
19661    my %ArchName = (
19662        "s390:31-bit" => "s390",
19663        "s390:64-bit" => "s390x",
19664        "powerpc:common" => "ppc32",
19665        "powerpc:common64" => "ppc64",
19666        "i386:x86-64" => "x86_64",
19667        "mips:3000" => "mips",
19668        "sparc:v8plus" => "sparcv9"
19669    );
19670
19671    if($OStarget eq "windows")
19672    {
19673        my $DumpbinCmd = get_CmdPath("dumpbin");
19674        if(not $DumpbinCmd) {
19675            exitStatus("Not_Found", "can't find \"dumpbin\"");
19676        }
19677
19678        my $Cmd = $DumpbinCmd." /headers \"$Path\"";
19679        my $Out = `$Cmd`;
19680
19681        if($Out=~/(\w+)\smachine/)
19682        {
19683            if(my $Type = $MachineType{uc($1)})
19684            {
19685                return $Type;
19686            }
19687        }
19688    }
19689    elsif($OStarget=~/linux|bsd|gnu/)
19690    {
19691        my $ObjdumpCmd = get_CmdPath("objdump");
19692        if(not $ObjdumpCmd) {
19693            exitStatus("Not_Found", "can't find \"objdump\"");
19694        }
19695
19696        my $Cmd = $ObjdumpCmd." -f \"$Path\"";
19697
19698        if($OSgroup eq "windows") {
19699            $Cmd = "set LANG=$LOCALE & ".$Cmd;
19700        }
19701        else {
19702            $Cmd = "LANG=$LOCALE ".$Cmd;
19703        }
19704        my $Out = `$Cmd`;
19705
19706        if($Out=~/architecture:\s+([\w\-\:]+)/)
19707        {
19708            my $Arch = $1;
19709            if($Arch=~s/\:(.+)//)
19710            {
19711                my $Suffix = $1;
19712
19713                if(my $Name = $ArchName{$Arch.":".$Suffix})
19714                {
19715                    $Arch = $Name;
19716                }
19717            }
19718
19719            if($Arch=~/i[3-6]86/) {
19720                $Arch = "x86";
19721            }
19722
19723            if($Arch eq "x86-64") {
19724                $Arch = "x86_64";
19725            }
19726
19727            if($Arch eq "ia64-elf64") {
19728                $Arch = "ia64";
19729            }
19730
19731            return $Arch;
19732        }
19733    }
19734    else
19735    { # macos, etc.
19736        # TODO
19737    }
19738
19739    return undef;
19740}
19741
19742sub getSONAME($)
19743{
19744    my $Path = $_[0];
19745    return if(not $Path);
19746    if(defined $Cache{"getSONAME"}{$Path}) {
19747        return $Cache{"getSONAME"}{$Path};
19748    }
19749    my $ObjdumpCmd = get_CmdPath("objdump");
19750    if(not $ObjdumpCmd) {
19751        exitStatus("Not_Found", "can't find \"objdump\"");
19752    }
19753    my $SonameCmd = "$ObjdumpCmd -x \"$Path\" 2>$TMP_DIR/null";
19754    if($OSgroup eq "windows") {
19755        $SonameCmd .= " | find \"SONAME\"";
19756    }
19757    else {
19758        $SonameCmd .= " | grep SONAME";
19759    }
19760    if(my $SonameInfo = `$SonameCmd`)
19761    {
19762        if($SonameInfo=~/SONAME\s+([^\s]+)/) {
19763            return ($Cache{"getSONAME"}{$Path} = $1);
19764        }
19765    }
19766    return ($Cache{"getSONAME"}{$Path}="");
19767}
19768
19769sub getSOPaths_Dest($$)
19770{
19771    my ($Dest, $LibVersion) = @_;
19772    if(skipLib($Dest, $LibVersion)) {
19773        return ();
19774    }
19775    if(-f $Dest)
19776    {
19777        if(not parse_libname($Dest, "name", $OStarget)) {
19778            exitStatus("Error", "incorrect format of library (should be *.$LIB_EXT): \'$Dest\'");
19779        }
19780        registerObject($Dest, $LibVersion);
19781        registerObject_Dir(get_dirname($Dest), $LibVersion);
19782        return ($Dest);
19783    }
19784    elsif(-d $Dest)
19785    {
19786        $Dest=~s/[\/\\]+\Z//g;
19787        my %Libs = ();
19788        if(grep { $Dest eq $_ } @{$SystemPaths{"lib"}})
19789        { # you have specified /usr/lib as the search directory (<libs>) in the XML descriptor
19790          # and the real name of the library by -l option (bz2, stdc++, Xaw, ...)
19791            foreach my $Path (cmd_find($Dest,"","*".esc($TargetLibraryName)."*.$LIB_EXT*",2))
19792            { # all files and symlinks that match the name of a library
19793                if(get_filename($Path)=~/\A(|lib)\Q$TargetLibraryName\E[\d\-]*\.$LIB_EXT[\d\.]*\Z/i)
19794                {
19795                    registerObject($Path, $LibVersion);
19796                    $Libs{realpath($Path)}=1;
19797                }
19798            }
19799        }
19800        else
19801        { # search for all files and symlinks
19802            foreach my $Path (find_libs($Dest,"",""))
19803            {
19804                next if(ignore_path($Path));
19805                next if(skipLib($Path, $LibVersion));
19806                registerObject($Path, $LibVersion);
19807                $Libs{realpath($Path)}=1;
19808            }
19809            if($OSgroup eq "macos")
19810            { # shared libraries on MacOS X may have no extension
19811                foreach my $Path (cmd_find($Dest,"f"))
19812                {
19813                    next if(ignore_path($Path));
19814                    next if(skipLib($Path, $LibVersion));
19815                    if(get_filename($Path)!~/\./
19816                    and cmd_file($Path)=~/(shared|dynamic)\s+library/i)
19817                    {
19818                        registerObject($Path, $LibVersion);
19819                        $Libs{realpath($Path)}=1;
19820                    }
19821                }
19822            }
19823        }
19824        return keys(%Libs);
19825    }
19826    else {
19827        return ();
19828    }
19829}
19830
19831sub isCyclical($$)
19832{
19833    my ($Stack, $Value) = @_;
19834    return (grep {$_ eq $Value} @{$Stack});
19835}
19836
19837sub getGCC_Opts($)
19838{ # to use in module
19839    my $LibVersion = $_[0];
19840
19841    my @Opts = ();
19842
19843    if($CompilerOptions{$LibVersion})
19844    { # user-defined options
19845        push(@Opts, $CompilerOptions{$LibVersion});
19846    }
19847    if($GccOptions)
19848    { # additional
19849        push(@Opts, $GccOptions);
19850    }
19851
19852    if(@Opts) {
19853        return join(" ", @Opts);
19854    }
19855
19856    return undef;
19857}
19858
19859sub getArch_GCC($)
19860{
19861    my $LibVersion = $_[0];
19862
19863    if(defined $Cache{"getArch_GCC"}{$LibVersion}) {
19864        return $Cache{"getArch_GCC"}{$LibVersion};
19865    }
19866
19867    my $Arch = undef;
19868
19869    if($GCC_PATH)
19870    {
19871        writeFile("$TMP_DIR/test.c", "int main(){return 0;}\n");
19872
19873        my $Cmd = $GCC_PATH." test.c -o test";
19874        if(my $Opts = getGCC_Opts($LibVersion))
19875        { # user-defined options
19876            $Cmd .= " ".$Opts;
19877        }
19878
19879        chdir($TMP_DIR);
19880        system($Cmd);
19881        chdir($ORIG_DIR);
19882
19883        $Arch = getArch_Object("$TMP_DIR/test");
19884
19885        unlink("$TMP_DIR/test.c");
19886        unlink("$TMP_DIR/test");
19887    }
19888
19889    if(not $Arch) {
19890        exitStatus("Error", "can't check ARCH type");
19891    }
19892
19893    return ($Cache{"getArch_GCC"}{$LibVersion} = $Arch);
19894}
19895
19896sub detectWordSize($)
19897{
19898    my $LibVersion = $_[0];
19899
19900    my $Size = undef;
19901
19902    # speed up detection
19903    if(my $Arch = getArch($LibVersion))
19904    {
19905        if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) {
19906            $Size = "8";
19907        }
19908        elsif($Arch=~/\A(x86|s390|ppc32)\Z/) {
19909            $Size = "4";
19910        }
19911    }
19912
19913    if($GCC_PATH)
19914    {
19915        writeFile("$TMP_DIR/empty.h", "");
19916
19917        my $Cmd = $GCC_PATH." -E -dD empty.h";
19918        if(my $Opts = getGCC_Opts($LibVersion))
19919        { # user-defined options
19920            $Cmd .= " ".$Opts;
19921        }
19922
19923        chdir($TMP_DIR);
19924        my $Defines = `$Cmd`;
19925        chdir($ORIG_DIR);
19926
19927        unlink("$TMP_DIR/empty.h");
19928
19929        if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/)
19930        { # GCC 4
19931            $Size = $1;
19932        }
19933        elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/)
19934        { # GCC 3
19935            my $PTRDIFF = $1;
19936            if($PTRDIFF=~/long/) {
19937                $Size = "8";
19938            }
19939            else {
19940                $Size = "4";
19941            }
19942        }
19943    }
19944
19945    if(not $Size) {
19946        exitStatus("Error", "can't check WORD size");
19947    }
19948
19949    return $Size;
19950}
19951
19952sub getWordSize($)
19953{ # to use in module
19954    return $WORD_SIZE{$_[0]};
19955}
19956
19957sub majorVersion($)
19958{
19959    my $V = $_[0];
19960    return 0 if(not $V);
19961    my @VParts = split(/\./, $V);
19962    return $VParts[0];
19963}
19964
19965sub cmpVersions($$)
19966{ # compare two versions in dotted-numeric format
19967    my ($V1, $V2) = @_;
19968    return 0 if($V1 eq $V2);
19969    my @V1Parts = split(/\./, $V1);
19970    my @V2Parts = split(/\./, $V2);
19971    for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++)
19972    {
19973        return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
19974        return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
19975    }
19976    return -1 if($#V1Parts < $#V2Parts);
19977    return 1 if($#V1Parts > $#V2Parts);
19978    return 0;
19979}
19980
19981sub read_ABI_Dump($$)
19982{
19983    my ($LibVersion, $Path) = @_;
19984    return if(not $LibVersion or not -e $Path);
19985    my $FilePath = "";
19986    if(isDump_U($Path))
19987    { # input *.abi
19988        $FilePath = $Path;
19989    }
19990    else
19991    { # input *.abi.tar.gz
19992        $FilePath = unpackDump($Path);
19993        if(not isDump_U($FilePath)) {
19994            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
19995        }
19996    }
19997
19998    my $ABI = {};
19999
20000    my $Line = readLineNum($FilePath, 0);
20001    if($Line=~/xml/)
20002    { # XML format
20003        loadModule("XmlDump");
20004        $ABI = readXmlDump($FilePath);
20005    }
20006    else
20007    { # Perl Data::Dumper format (default)
20008        open(DUMP, $FilePath);
20009        local $/ = undef;
20010        my $Content = <DUMP>;
20011        close(DUMP);
20012
20013        if(get_dirname($FilePath) eq $TMP_DIR."/unpack")
20014        { # remove temp file
20015            unlink($FilePath);
20016        }
20017        if($Content!~/};\s*\Z/) {
20018            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
20019        }
20020        $ABI = eval($Content);
20021        if(not $ABI) {
20022            exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
20023        }
20024    }
20025    # new dumps (>=1.22) have a personal versioning
20026    my $DVersion = $ABI->{"ABI_DUMP_VERSION"};
20027    my $ToolVersion = $ABI->{"ABI_COMPLIANCE_CHECKER_VERSION"};
20028    if(not $DVersion)
20029    { # old dumps (<=1.21.6) have been marked by the tool version
20030        $DVersion = $ToolVersion;
20031    }
20032    $UsedDump{$LibVersion}{"V"} = $DVersion;
20033    $UsedDump{$LibVersion}{"M"} = $ABI->{"LibraryName"};
20034
20035    if($ABI->{"PublicABI"}) {
20036        $UsedDump{$LibVersion}{"Public"} = 1;
20037    }
20038
20039    if($ABI->{"ABI_DUMP_VERSION"})
20040    {
20041        if(cmpVersions($DVersion, $ABI_DUMP_VERSION)>0)
20042        { # Don't know how to parse future dump formats
20043            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $ABI_DUMP_VERSION)");
20044        }
20045    }
20046    else
20047    { # support for old ABI dumps
20048        if(cmpVersions($DVersion, $TOOL_VERSION)>0)
20049        { # Don't know how to parse future dump formats
20050            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $TOOL_VERSION)");
20051        }
20052    }
20053
20054    if(majorVersion($DVersion)<2)
20055    {
20056        exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (allowed only 2.0<=V<=$ABI_DUMP_VERSION)");
20057    }
20058
20059    if(defined $ABI->{"ABI_DUMPER_VERSION"})
20060    { # DWARF ABI Dump
20061        $UseConv_Real{$LibVersion}{"P"} = 1;
20062        $UseConv_Real{$LibVersion}{"R"} = 0; # not implemented yet
20063
20064        $UsedDump{$LibVersion}{"DWARF"} = 1;
20065
20066        if(not $TargetComponent_Opt)
20067        {
20068            if($ABI->{"LibraryName"}=~/\.ko[\.\d]*\Z/) {
20069                $TargetComponent = "module";
20070            }
20071            else {
20072                $TargetComponent = "object";
20073            }
20074        }
20075    }
20076
20077    if(not checkDump($LibVersion, "2.11"))
20078    { # old ABI dumps
20079        $UsedDump{$LibVersion}{"BinOnly"} = 1;
20080    }
20081    elsif($ABI->{"BinOnly"})
20082    { # ABI dump created with --binary option
20083        $UsedDump{$LibVersion}{"BinOnly"} = 1;
20084    }
20085    else
20086    { # default
20087        $UsedDump{$LibVersion}{"SrcBin"} = 1;
20088    }
20089
20090    if(defined $ABI->{"Mode"}
20091    and $ABI->{"Mode"} eq "Extended")
20092    { # --ext option
20093        $ExtendedCheck = 1;
20094    }
20095    if($ABI->{"Extra"}) {
20096        $ExtraDump = 1;
20097    }
20098
20099    if(my $Lang = $ABI->{"Language"})
20100    {
20101        $UsedDump{$LibVersion}{"L"} = $Lang;
20102        setLanguage($LibVersion, $Lang);
20103    }
20104    if(checkDump($LibVersion, "2.15")) {
20105        $TypeInfo{$LibVersion} = $ABI->{"TypeInfo"};
20106    }
20107    else
20108    { # support for old ABI dumps
20109        my $TInfo = $ABI->{"TypeInfo"};
20110        if(not $TInfo)
20111        { # support for older ABI dumps
20112            $TInfo = $ABI->{"TypeDescr"};
20113        }
20114        my %Tid_TDid = ();
20115        foreach my $TDid (keys(%{$TInfo}))
20116        {
20117            foreach my $Tid (keys(%{$TInfo->{$TDid}}))
20118            {
20119                $MAX_ID = $Tid if($Tid>$MAX_ID);
20120                $MAX_ID = $TDid if($TDid and $TDid>$MAX_ID);
20121                $Tid_TDid{$Tid}{$TDid} = 1;
20122            }
20123        }
20124        my %NewID = ();
20125        foreach my $Tid (keys(%Tid_TDid))
20126        {
20127            my @TDids = keys(%{$Tid_TDid{$Tid}});
20128            if($#TDids>=1)
20129            {
20130                foreach my $TDid (@TDids)
20131                {
20132                    if($TDid) {
20133                        %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
20134                    }
20135                    else
20136                    {
20137                        my $ID = ++$MAX_ID;
20138
20139                        $NewID{$TDid}{$Tid} = $ID;
20140                        %{$TypeInfo{$LibVersion}{$ID}} = %{$TInfo->{$TDid}{$Tid}};
20141                        $TypeInfo{$LibVersion}{$ID}{"Tid"} = $ID;
20142                    }
20143                }
20144            }
20145            else
20146            {
20147                my $TDid = $TDids[0];
20148                %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
20149            }
20150        }
20151        foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
20152        {
20153            my %Info = %{$TypeInfo{$LibVersion}{$Tid}};
20154            if(defined $Info{"BaseType"})
20155            {
20156                my $Bid = $Info{"BaseType"}{"Tid"};
20157                my $BDid = $Info{"BaseType"}{"TDid"};
20158                $BDid="" if(not defined $BDid);
20159                delete($TypeInfo{$LibVersion}{$Tid}{"BaseType"}{"TDid"});
20160                if(defined $NewID{$BDid} and my $ID = $NewID{$BDid}{$Bid}) {
20161                    $TypeInfo{$LibVersion}{$Tid}{"BaseType"} = $ID;
20162                }
20163            }
20164            delete($TypeInfo{$LibVersion}{$Tid}{"TDid"});
20165        }
20166    }
20167    read_Machine_DumpInfo($ABI, $LibVersion);
20168    $SymbolInfo{$LibVersion} = $ABI->{"SymbolInfo"};
20169    if(not $SymbolInfo{$LibVersion})
20170    { # support for old dumps
20171        $SymbolInfo{$LibVersion} = $ABI->{"FuncDescr"};
20172    }
20173    if(not keys(%{$SymbolInfo{$LibVersion}}))
20174    { # validation of old-version dumps
20175        if(not $ExtendedCheck) {
20176            exitStatus("Invalid_Dump", "the input dump d$LibVersion is invalid");
20177        }
20178    }
20179    if(checkDump($LibVersion, "2.15")) {
20180        $DepLibrary_Symbol{$LibVersion} = $ABI->{"DepSymbols"};
20181    }
20182    else
20183    { # support for old ABI dumps
20184        my $DepSymbols = $ABI->{"DepSymbols"};
20185        if(not $DepSymbols) {
20186            $DepSymbols = $ABI->{"DepInterfaces"};
20187        }
20188        if(not $DepSymbols)
20189        { # Cannot reconstruct DepSymbols. This may result in false
20190          # positives if the old dump is for library 2. Not a problem if
20191          # old dumps are only from old libraries.
20192            $DepSymbols = {};
20193        }
20194        foreach my $Symbol (keys(%{$DepSymbols})) {
20195            $DepSymbol_Library{$LibVersion}{$Symbol} = 1;
20196        }
20197    }
20198    $SymVer{$LibVersion} = $ABI->{"SymbolVersion"};
20199
20200    if(my $V = $TargetVersion{$LibVersion}) {
20201        $Descriptor{$LibVersion}{"Version"} = $V;
20202    }
20203    else {
20204        $Descriptor{$LibVersion}{"Version"} = $ABI->{"LibraryVersion"};
20205    }
20206
20207    if(not $SkipTypes{$LibVersion})
20208    { # if not defined by -skip-types option
20209        if(defined $ABI->{"SkipTypes"})
20210        {
20211            foreach my $TName (keys(%{$ABI->{"SkipTypes"}}))
20212            {
20213                $SkipTypes{$LibVersion}{$TName} = 1;
20214            }
20215        }
20216        if(defined $ABI->{"OpaqueTypes"})
20217        { # support for old dumps
20218            foreach my $TName (keys(%{$ABI->{"OpaqueTypes"}}))
20219            {
20220                $SkipTypes{$LibVersion}{$TName} = 1;
20221            }
20222        }
20223    }
20224
20225    if(not $SkipSymbols{$LibVersion})
20226    { # if not defined by -skip-symbols option
20227        $SkipSymbols{$LibVersion} = $ABI->{"SkipSymbols"};
20228        if(not $SkipSymbols{$LibVersion})
20229        { # support for old dumps
20230            $SkipSymbols{$LibVersion} = $ABI->{"SkipInterfaces"};
20231        }
20232        if(not $SkipSymbols{$LibVersion})
20233        { # support for old dumps
20234            $SkipSymbols{$LibVersion} = $ABI->{"InternalInterfaces"};
20235        }
20236    }
20237    $SkipNameSpaces{$LibVersion} = $ABI->{"SkipNameSpaces"};
20238
20239    if(not $TargetHeaders{$LibVersion})
20240    { # if not defined by -headers-list option
20241        $TargetHeaders{$LibVersion} = $ABI->{"TargetHeaders"};
20242    }
20243
20244    foreach my $Path (keys(%{$ABI->{"SkipHeaders"}}))
20245    {
20246        $SkipHeadersList{$LibVersion}{$Path} = $ABI->{"SkipHeaders"}{$Path};
20247        my ($CPath, $Type) = classifyPath($Path);
20248        $SkipHeaders{$LibVersion}{$Type}{$CPath} = $ABI->{"SkipHeaders"}{$Path};
20249    }
20250
20251    read_Source_DumpInfo($ABI, $LibVersion);
20252    read_Libs_DumpInfo($ABI, $LibVersion);
20253
20254    if(not checkDump($LibVersion, "2.10.1")
20255    or not $TargetHeaders{$LibVersion})
20256    { # support for old ABI dumps: added target headers
20257        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20258            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20259        }
20260
20261        if(not $ABI->{"PublicABI"})
20262        {
20263            foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20264                $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20265            }
20266        }
20267    }
20268    $Constants{$LibVersion} = $ABI->{"Constants"};
20269    if(defined $ABI->{"GccConstants"})
20270    { # 3.0
20271        foreach my $Name (keys(%{$ABI->{"GccConstants"}})) {
20272            $Constants{$LibVersion}{$Name}{"Value"} = $ABI->{"GccConstants"}{$Name};
20273        }
20274    }
20275
20276    $NestedNameSpaces{$LibVersion} = $ABI->{"NameSpaces"};
20277    if(not $NestedNameSpaces{$LibVersion})
20278    { # support for old dumps
20279      # Cannot reconstruct NameSpaces. This may affect design
20280      # of the compatibility report.
20281        $NestedNameSpaces{$LibVersion} = {};
20282    }
20283    # target system type
20284    # needed to adopt HTML report
20285    if(not $DumpSystem)
20286    { # to use in createSymbolsList(...)
20287        $OStarget = $ABI->{"Target"};
20288    }
20289    # recreate environment
20290    foreach my $Lib_Name (keys(%{$Library_Symbol{$LibVersion}}))
20291    {
20292        foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
20293        {
20294            $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20295            if($Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol}<=-1)
20296            { # data marked as -size in the dump
20297                $GlobalDataObject{$LibVersion}{$Symbol} = -$Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol};
20298            }
20299            if($COMMON_LANGUAGE{$LibVersion} ne "C++")
20300            {
20301                if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
20302                    setLanguage($LibVersion, "C++");
20303                }
20304            }
20305        }
20306    }
20307    foreach my $Lib_Name (keys(%{$DepLibrary_Symbol{$LibVersion}}))
20308    {
20309        foreach my $Symbol (keys(%{$DepLibrary_Symbol{$LibVersion}{$Lib_Name}})) {
20310            $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20311        }
20312    }
20313
20314    my @VFunc = ();
20315    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20316    {
20317        if(my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20318        {
20319            if(not $Symbol_Library{$LibVersion}{$MnglName}
20320            and not $DepSymbol_Library{$LibVersion}{$MnglName}) {
20321                push(@VFunc, $MnglName);
20322            }
20323        }
20324    }
20325    translateSymbols(@VFunc, $LibVersion);
20326    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
20327    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
20328
20329    if(not checkDump($LibVersion, "3.0"))
20330    { # support for old ABI dumps
20331        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20332        {
20333            if(my $BaseType = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20334            {
20335                if(ref($BaseType) eq "HASH") {
20336                    $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}{"Tid"};
20337                }
20338            }
20339        }
20340    }
20341
20342    if(not checkDump($LibVersion, "3.2"))
20343    { # support for old ABI dumps
20344        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20345        {
20346            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"})
20347            {
20348                foreach my $Offset (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"VTable"}})) {
20349                    $TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset} = simplifyVTable($TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset});
20350                }
20351            }
20352        }
20353
20354        # repair target headers list
20355        delete($TargetHeaders{$LibVersion});
20356        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20357            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20358        }
20359        foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20360            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20361        }
20362
20363        # non-target constants from anon enums
20364        foreach my $Name (keys(%{$Constants{$LibVersion}}))
20365        {
20366            if(not $ExtraDump
20367            and not is_target_header($Constants{$LibVersion}{$Name}{"Header"}, $LibVersion))
20368            {
20369                delete($Constants{$LibVersion}{$Name});
20370            }
20371        }
20372    }
20373
20374    if(not checkDump($LibVersion, "2.20"))
20375    { # support for old ABI dumps
20376        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20377        {
20378            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20379
20380            if($TType=~/Struct|Union|Enum|Typedef/)
20381            { # repair complex types first
20382                next;
20383            }
20384
20385            if(my $BaseId = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20386            {
20387                my $BType = lc($TypeInfo{$LibVersion}{$BaseId}{"Type"});
20388                if($BType=~/Struct|Union|Enum/i)
20389                {
20390                    my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"};
20391                    $TypeInfo{$LibVersion}{$TypeId}{"Name"}=~s/\A\Q$BName\E\b/$BType $BName/g;
20392                }
20393            }
20394        }
20395        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20396        {
20397            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20398            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
20399            if($TType=~/Struct|Union|Enum/) {
20400                $TypeInfo{$LibVersion}{$TypeId}{"Name"} = lc($TType)." ".$TName;
20401            }
20402        }
20403    }
20404
20405    foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20406    { # NOTE: order is important
20407        if(defined $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"})
20408        { # support for old ABI dumps < 2.0 (ACC 1.22)
20409            foreach my $BId (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}}))
20410            {
20411                if(my $Access = $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}{$BId})
20412                {
20413                    if($Access ne "public") {
20414                        $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId}{"access"} = $Access;
20415                    }
20416                }
20417                $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId} = {};
20418            }
20419            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseClass"});
20420        }
20421        if(my $Header = $TypeInfo{$LibVersion}{$TypeId}{"Header"})
20422        { # support for old ABI dumps
20423            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = path_format($Header, $OSgroup);
20424        }
20425        elsif(my $Source = $TypeInfo{$LibVersion}{$TypeId}{"Source"})
20426        { # DWARF ABI Dumps
20427            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = $Source;
20428        }
20429        if(not defined $TypeInfo{$LibVersion}{$TypeId}{"Tid"}) {
20430            $TypeInfo{$LibVersion}{$TypeId}{"Tid"} = $TypeId;
20431        }
20432
20433        # support for old formatting of type names
20434        $TypeInfo{$LibVersion}{$TypeId}{"Name"} = formatName($TypeInfo{$LibVersion}{$TypeId}{"Name"}, "T");
20435
20436        my %TInfo = %{$TypeInfo{$LibVersion}{$TypeId}};
20437        if(defined $TInfo{"Base"})
20438        {
20439            foreach my $SubId (keys(%{$TInfo{"Base"}}))
20440            {
20441                if($SubId eq $TypeId)
20442                { # Fix erroneus ABI dump
20443                    delete($TypeInfo{$LibVersion}{$TypeId}{"Base"}{$SubId});
20444                    next;
20445                }
20446
20447                $Class_SubClasses{$LibVersion}{$SubId}{$TypeId} = 1;
20448            }
20449        }
20450        if($TInfo{"Type"} eq "MethodPtr")
20451        {
20452            if(defined $TInfo{"Param"})
20453            { # support for old ABI dumps <= 1.17
20454                if(not defined $TInfo{"Param"}{"0"})
20455                {
20456                    my $Max = keys(%{$TInfo{"Param"}});
20457                    foreach my $Pos (1 .. $Max) {
20458                        $TInfo{"Param"}{$Pos-1} = $TInfo{"Param"}{$Pos};
20459                    }
20460                    delete($TInfo{"Param"}{$Max});
20461                    %{$TypeInfo{$LibVersion}{$TypeId}} = %TInfo;
20462                }
20463            }
20464        }
20465        if($TInfo{"BaseType"} eq $TypeId)
20466        { # fix ABI dump
20467            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseType"});
20468        }
20469
20470        if($TInfo{"Type"} eq "Typedef" and not $TInfo{"Artificial"})
20471        {
20472            if(my $BTid = $TInfo{"BaseType"})
20473            {
20474                my $BName = $TypeInfo{$LibVersion}{$BTid}{"Name"};
20475                if(not $BName)
20476                { # broken type
20477                    next;
20478                }
20479                if($TInfo{"Name"} eq $BName)
20480                { # typedef to "class Class"
20481                  # should not be registered in TName_Tid
20482                    next;
20483                }
20484                if(not $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}}) {
20485                    $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}} = $BName;
20486                }
20487            }
20488        }
20489        if(not $TName_Tid{$LibVersion}{$TInfo{"Name"}})
20490        { # classes: class (id1), typedef (artificial, id2 > id1)
20491            $TName_Tid{$LibVersion}{$TInfo{"Name"}} = $TypeId;
20492        }
20493    }
20494
20495    if(not checkDump($LibVersion, "2.15"))
20496    { # support for old ABI dumps
20497        my %Dups = ();
20498        foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20499        {
20500            if(my $ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20501            {
20502                if(not defined $TypeInfo{$LibVersion}{$ClassId})
20503                { # remove template decls
20504                    delete($SymbolInfo{$LibVersion}{$InfoId});
20505                    next;
20506                }
20507            }
20508            my $MName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
20509            if(not $MName and $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20510            { # templates
20511                delete($SymbolInfo{$LibVersion}{$InfoId});
20512            }
20513        }
20514    }
20515
20516    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20517    {
20518        if(my $Class = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
20519        and not $SymbolInfo{$LibVersion}{$InfoId}{"Static"}
20520        and not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
20521        { # support for old ABI dumps (< 3.1)
20522            if(not defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
20523            or $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
20524            { # add "this" first parameter
20525                my $ThisTid = getTypeIdByName($TypeInfo{$LibVersion}{$Class}{"Name"}."*const", $LibVersion);
20526                my %PInfo = ("name"=>"this", "type"=>"$ThisTid");
20527
20528                if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
20529                {
20530                    my @Pos = sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
20531                    foreach my $Pos (reverse(0 .. $#Pos)) {
20532                        %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos+1}} = %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos}};
20533                    }
20534                }
20535                $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{"0"} = \%PInfo;
20536            }
20537        }
20538
20539        if(not $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20540        { # ABI dumps have no mangled names for C-functions
20541            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
20542        }
20543        if(my $Header = $SymbolInfo{$LibVersion}{$InfoId}{"Header"})
20544        { # support for old ABI dumps
20545            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = path_format($Header, $OSgroup);
20546        }
20547        elsif(my $Source = $SymbolInfo{$LibVersion}{$InfoId}{"Source"})
20548        { # DWARF ABI Dumps
20549            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = $Source;
20550        }
20551    }
20552
20553    $Descriptor{$LibVersion}{"Dump"} = 1;
20554}
20555
20556sub read_Machine_DumpInfo($$)
20557{
20558    my ($ABI, $LibVersion) = @_;
20559    if($ABI->{"Arch"}) {
20560        $CPU_ARCH{$LibVersion} = $ABI->{"Arch"};
20561    }
20562    if($ABI->{"WordSize"}) {
20563        $WORD_SIZE{$LibVersion} = $ABI->{"WordSize"};
20564    }
20565    else
20566    { # support for old dumps
20567        $WORD_SIZE{$LibVersion} = $ABI->{"SizeOfPointer"};
20568    }
20569    if(not $WORD_SIZE{$LibVersion})
20570    { # support for old dumps (<1.23)
20571        if(my $Tid = getTypeIdByName("char*", $LibVersion))
20572        { # size of char*
20573            $WORD_SIZE{$LibVersion} = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20574        }
20575        else
20576        {
20577            my $PSize = 0;
20578            foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
20579            {
20580                if($TypeInfo{$LibVersion}{$Tid}{"Type"} eq "Pointer")
20581                { # any "pointer"-type
20582                    $PSize = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20583                    last;
20584                }
20585            }
20586            if($PSize)
20587            { # a pointer type size
20588                $WORD_SIZE{$LibVersion} = $PSize;
20589            }
20590            else {
20591                printMsg("WARNING", "cannot identify a WORD size in the ABI dump (too old format)");
20592            }
20593        }
20594    }
20595    if($ABI->{"GccVersion"}) {
20596        $GCC_VERSION{$LibVersion} = $ABI->{"GccVersion"};
20597    }
20598}
20599
20600sub read_Libs_DumpInfo($$)
20601{
20602    my ($ABI, $LibVersion) = @_;
20603    $Library_Symbol{$LibVersion} = $ABI->{"Symbols"};
20604    if(not $Library_Symbol{$LibVersion})
20605    { # support for old dumps
20606        $Library_Symbol{$LibVersion} = $ABI->{"Interfaces"};
20607    }
20608    if(keys(%{$Library_Symbol{$LibVersion}})
20609    and not $DumpAPI) {
20610        $Descriptor{$LibVersion}{"Libs"} = "OK";
20611    }
20612}
20613
20614sub read_Source_DumpInfo($$)
20615{
20616    my ($ABI, $LibVersion) = @_;
20617
20618    if(keys(%{$ABI->{"Headers"}})
20619    and not $DumpAPI) {
20620        $Descriptor{$LibVersion}{"Headers"} = "OK";
20621    }
20622    foreach my $Identity (sort {$ABI->{"Headers"}{$a}<=>$ABI->{"Headers"}{$b}} keys(%{$ABI->{"Headers"}}))
20623    {
20624        $Registered_Headers{$LibVersion}{$Identity}{"Identity"} = $Identity;
20625        $Registered_Headers{$LibVersion}{$Identity}{"Pos"} = $ABI->{"Headers"}{$Identity};
20626    }
20627
20628    if(keys(%{$ABI->{"Sources"}})
20629    and not $DumpAPI) {
20630        $Descriptor{$LibVersion}{"Sources"} = "OK";
20631    }
20632    foreach my $Name (sort {$ABI->{"Sources"}{$a}<=>$ABI->{"Sources"}{$b}} keys(%{$ABI->{"Sources"}}))
20633    {
20634        $Registered_Sources{$LibVersion}{$Name}{"Identity"} = $Name;
20635        $Registered_Sources{$LibVersion}{$Name}{"Pos"} = $ABI->{"Headers"}{$Name};
20636    }
20637}
20638
20639sub find_libs($$$)
20640{
20641    my ($Path, $Type, $MaxDepth) = @_;
20642    # FIXME: correct the search pattern
20643    return cmd_find($Path, $Type, '\.'.$LIB_EXT.'[0-9.]*\Z', $MaxDepth, 1);
20644}
20645
20646sub createDescriptor($$)
20647{
20648    my ($LibVersion, $Path) = @_;
20649    if(not $LibVersion or not $Path
20650    or not -e $Path) {
20651        return "";
20652    }
20653    if(-d $Path)
20654    { # directory with headers files and shared objects
20655        return "
20656            <version>
20657                ".$TargetVersion{$LibVersion}."
20658            </version>
20659
20660            <headers>
20661                $Path
20662            </headers>
20663
20664            <libs>
20665                $Path
20666            </libs>";
20667    }
20668    else
20669    { # files
20670        if($Path=~/\.(xml|desc)\Z/i)
20671        { # standard XML-descriptor
20672            return readFile($Path);
20673        }
20674        elsif(is_header($Path, 2, $LibVersion))
20675        { # header file
20676            $CheckHeadersOnly = 1;
20677
20678            if($LibVersion==1) {
20679                $TargetVersion{$LibVersion} = "X";
20680            }
20681
20682            if($LibVersion==2) {
20683                $TargetVersion{$LibVersion} = "Y";
20684            }
20685
20686            return "
20687                <version>
20688                    ".$TargetVersion{$LibVersion}."
20689                </version>
20690
20691                <headers>
20692                    $Path
20693                </headers>
20694
20695                <libs>
20696                    none
20697                </libs>";
20698        }
20699        else
20700        { # standard XML-descriptor
20701            return readFile($Path);
20702        }
20703    }
20704}
20705
20706sub detect_lib_default_paths()
20707{
20708    my %LPaths = ();
20709    if($OSgroup eq "bsd")
20710    {
20711        if(my $LdConfig = get_CmdPath("ldconfig"))
20712        {
20713            foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TMP_DIR/null\"`))
20714            {
20715                if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/)
20716                {
20717                    my $Name = "lib".$1;
20718                    if(not defined $LPaths{$Name}) {
20719                        $LPaths{$Name} = $2;
20720                    }
20721                }
20722            }
20723        }
20724        else {
20725            printMsg("WARNING", "can't find ldconfig");
20726        }
20727    }
20728    else
20729    {
20730        if(my $LdConfig = get_CmdPath("ldconfig"))
20731        {
20732            if($SystemRoot and $OSgroup eq "linux")
20733            { # use host (x86) ldconfig with the target (arm) ld.so.conf
20734                if(-e $SystemRoot."/etc/ld.so.conf") {
20735                    $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf";
20736                }
20737            }
20738            foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TMP_DIR/null\"`))
20739            {
20740                if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/)
20741                {
20742                    my ($Name, $Path) = ($1, $2);
20743                    $Path=~s/[\/]{2,}/\//;
20744                    if(not defined $LPaths{$Name})
20745                    { # get first element from the list of available paths
20746
20747                      # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
20748                      # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6
20749                      # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6
20750
20751                        $LPaths{$Name} = $Path;
20752                    }
20753                }
20754            }
20755        }
20756        elsif($OSgroup eq "linux") {
20757            printMsg("WARNING", "can't find ldconfig");
20758        }
20759    }
20760    return \%LPaths;
20761}
20762
20763sub detect_bin_default_paths()
20764{
20765    my $EnvPaths = $ENV{"PATH"};
20766    if($OSgroup eq "beos") {
20767        $EnvPaths.=":".$ENV{"BETOOLS"};
20768    }
20769    my $Sep = ($OSgroup eq "windows")?";":":|;";
20770    foreach my $Path (split(/$Sep/, $EnvPaths))
20771    {
20772        $Path = path_format($Path, $OSgroup);
20773        next if(not $Path);
20774        if($SystemRoot
20775        and $Path=~/\A\Q$SystemRoot\E\//)
20776        { # do NOT use binaries from target system
20777            next;
20778        }
20779        push_U(\@DefaultBinPaths, $Path);
20780    }
20781}
20782
20783sub detect_inc_default_paths()
20784{
20785    my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]);
20786    writeFile("$TMP_DIR/empty.h", "");
20787    foreach my $Line (split(/\n/, `$GCC_PATH -v -x c++ -E \"$TMP_DIR/empty.h\" 2>&1`))
20788    { # detecting GCC default include paths
20789        next if(index($Line, "/cc1plus ")!=-1);
20790
20791        if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/)
20792        {
20793            my $Path = realpath($1);
20794            $Path = path_format($Path, $OSgroup);
20795            if(index($Path, "c++")!=-1
20796            or index($Path, "/g++/")!=-1)
20797            {
20798                push_U($DPaths{"Cpp"}, $Path);
20799                if(not defined $MAIN_CPP_DIR
20800                or get_depth($MAIN_CPP_DIR)>get_depth($Path)) {
20801                    $MAIN_CPP_DIR = $Path;
20802                }
20803            }
20804            elsif(index($Path, "gcc")!=-1) {
20805                push_U($DPaths{"Gcc"}, $Path);
20806            }
20807            else
20808            {
20809                if($Path=~/local[\/\\]+include/)
20810                { # local paths
20811                    next;
20812                }
20813                if($SystemRoot
20814                and $Path!~/\A\Q$SystemRoot\E(\/|\Z)/)
20815                { # The GCC include path for user headers is not a part of the system root
20816                  # The reason: you are not specified the --cross-gcc option or selected a wrong compiler
20817                  # or it is the internal cross-GCC path like arm-linux-gnueabi/include
20818                    next;
20819                }
20820                push_U($DPaths{"Inc"}, $Path);
20821            }
20822        }
20823    }
20824    unlink("$TMP_DIR/empty.h");
20825    return %DPaths;
20826}
20827
20828sub detect_default_paths($)
20829{
20830    my ($HSearch, $LSearch, $BSearch, $GSearch) = (1, 1, 1, 1);
20831    my $Search = $_[0];
20832    if($Search!~/inc/) {
20833        $HSearch = 0;
20834    }
20835    if($Search!~/lib/) {
20836        $LSearch = 0;
20837    }
20838    if($Search!~/bin/) {
20839        $BSearch = 0;
20840    }
20841    if($Search!~/gcc/) {
20842        $GSearch = 0;
20843    }
20844    if(@{$SystemPaths{"include"}})
20845    { # <search_headers> section of the XML descriptor
20846      # do NOT search for systems headers
20847        $HSearch = 0;
20848    }
20849    if(@{$SystemPaths{"lib"}})
20850    { # <search_libs> section of the XML descriptor
20851      # do NOT search for systems libraries
20852        $LSearch = 0;
20853    }
20854    foreach my $Type (keys(%{$OS_AddPath{$OSgroup}}))
20855    { # additional search paths
20856        next if($Type eq "include" and not $HSearch);
20857        next if($Type eq "lib" and not $LSearch);
20858        next if($Type eq "bin" and not $BSearch);
20859        push_U($SystemPaths{$Type}, grep { -d $_ } @{$OS_AddPath{$OSgroup}{$Type}});
20860    }
20861    if($OSgroup ne "windows")
20862    { # unix-like
20863        foreach my $Type ("include", "lib", "bin")
20864        { # automatic detection of system "devel" directories
20865            next if($Type eq "include" and not $HSearch);
20866            next if($Type eq "lib" and not $LSearch);
20867            next if($Type eq "bin" and not $BSearch);
20868            my ($UsrDir, $RootDir) = ("/usr", "/");
20869            if($SystemRoot and $Type ne "bin")
20870            { # 1. search for target headers and libraries
20871              # 2. use host commands: ldconfig, readelf, etc.
20872                ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot);
20873            }
20874            push_U($SystemPaths{$Type}, cmd_find($RootDir,"d","*$Type*",1));
20875            if(-d $RootDir."/".$Type)
20876            { # if "/lib" is symbolic link
20877                if($RootDir eq "/") {
20878                    push_U($SystemPaths{$Type}, "/".$Type);
20879                }
20880                else {
20881                    push_U($SystemPaths{$Type}, $RootDir."/".$Type);
20882                }
20883            }
20884            if(-d $UsrDir)
20885            {
20886                push_U($SystemPaths{$Type}, cmd_find($UsrDir,"d","*$Type*",1));
20887                if(-d $UsrDir."/".$Type)
20888                { # if "/usr/lib" is symbolic link
20889                    push_U($SystemPaths{$Type}, $UsrDir."/".$Type);
20890                }
20891            }
20892        }
20893    }
20894    if($BSearch)
20895    {
20896        detect_bin_default_paths();
20897        push_U($SystemPaths{"bin"}, @DefaultBinPaths);
20898    }
20899    # check environment variables
20900    if($OSgroup eq "beos")
20901    {
20902        foreach (my @Paths = @{$SystemPaths{"bin"}})
20903        {
20904            if($_ eq ".") {
20905                next;
20906            }
20907            # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/
20908            if(my @Dirs = sort cmd_find($_, "d", "bin")) {
20909                push_U($SystemPaths{"bin"}, sort {get_depth($a)<=>get_depth($b)} @Dirs);
20910            }
20911        }
20912        if($HSearch)
20913        {
20914            push_U(\@DefaultIncPaths, grep { is_abs($_) } (
20915                split(/:|;/, $ENV{"BEINCLUDES"})
20916                ));
20917        }
20918        if($LSearch)
20919        {
20920            push_U(\@DefaultLibPaths, grep { is_abs($_) } (
20921                split(/:|;/, $ENV{"BELIBRARIES"}),
20922                split(/:|;/, $ENV{"LIBRARY_PATH"})
20923                ));
20924        }
20925    }
20926    if($LSearch)
20927    { # using linker to get system paths
20928        if(my $LPaths = detect_lib_default_paths())
20929        { # unix-like
20930            my %Dirs = ();
20931            foreach my $Name (keys(%{$LPaths}))
20932            {
20933                if($SystemRoot
20934                and $LPaths->{$Name}!~/\A\Q$SystemRoot\E\//)
20935                { # wrong ldconfig configuration
20936                  # check your <sysroot>/etc/ld.so.conf
20937                    next;
20938                }
20939                $DyLib_DefaultPath{$Name} = $LPaths->{$Name};
20940                if(my $Dir = get_dirname($LPaths->{$Name})) {
20941                    $Dirs{$Dir} = 1;
20942                }
20943            }
20944            push_U(\@DefaultLibPaths, sort {get_depth($a)<=>get_depth($b)} sort keys(%Dirs));
20945        }
20946        push_U($SystemPaths{"lib"}, @DefaultLibPaths);
20947    }
20948    if($BSearch)
20949    {
20950        if($CrossGcc)
20951        { # --cross-gcc=arm-linux-gcc
20952            if(-e $CrossGcc)
20953            { # absolute or relative path
20954                $GCC_PATH = get_abs_path($CrossGcc);
20955            }
20956            elsif($CrossGcc!~/\// and get_CmdPath($CrossGcc))
20957            { # command name
20958                $GCC_PATH = $CrossGcc;
20959            }
20960            else {
20961                exitStatus("Access_Error", "can't access \'$CrossGcc\'");
20962            }
20963            if($GCC_PATH=~/\s/) {
20964                $GCC_PATH = "\"".$GCC_PATH."\"";
20965            }
20966        }
20967    }
20968    if($GSearch)
20969    { # GCC path and default include dirs
20970        if(not $CrossGcc)
20971        { # try default gcc
20972            $GCC_PATH = get_CmdPath("gcc");
20973        }
20974        if(not $GCC_PATH)
20975        { # try to find gcc-X.Y
20976            foreach my $Path (@{$SystemPaths{"bin"}})
20977            {
20978                if(my @GCCs = cmd_find($Path, "", '/gcc-[0-9.]*\Z', 1, 1))
20979                { # select the latest version
20980                    @GCCs = sort {$b cmp $a} @GCCs;
20981                    if(check_gcc($GCCs[0], "3"))
20982                    {
20983                        $GCC_PATH = $GCCs[0];
20984                        last;
20985                    }
20986                }
20987            }
20988        }
20989        if(not $GCC_PATH) {
20990            exitStatus("Not_Found", "can't find GCC>=3.0 in PATH");
20991        }
20992
20993        my $GCC_Ver = get_dumpversion($GCC_PATH);
20994        if($GCC_Ver eq "4.8")
20995        { # on Ubuntu -dumpversion returns 4.8 for gcc 4.8.4
20996            my $Info = `$GCC_PATH --version`;
20997
20998            if($Info=~/gcc\s+(|\([^()]+\)\s+)(\d+\.\d+\.\d+)/)
20999            { # gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
21000              # gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)
21001                $GCC_Ver = $2;
21002            }
21003        }
21004
21005        if($GCC_Ver)
21006        {
21007            my $GccTarget = get_dumpmachine($GCC_PATH);
21008
21009            if($GccTarget=~/linux/)
21010            {
21011                $OStarget = "linux";
21012                $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
21013            }
21014            elsif($GccTarget=~/symbian/)
21015            {
21016                $OStarget = "symbian";
21017                $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
21018            }
21019
21020            printMsg("INFO", "Using GCC $GCC_Ver ($GccTarget, target: ".getArch_GCC(1).")");
21021
21022            # check GCC version
21023            if($GCC_Ver=~/\A4\.8(|\.[012])\Z/)
21024            { # bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850
21025              # introduced in 4.8 and fixed in 4.8.3
21026                printMsg("WARNING", "Not working properly with GCC $GCC_Ver. Please update GCC to 4.8.3 or downgrade it to 4.7. You can use a local GCC installation by --gcc-path=PATH option.");
21027
21028                $EMERGENCY_MODE_48 = 1;
21029            }
21030        }
21031        else {
21032            exitStatus("Error", "something is going wrong with the GCC compiler");
21033        }
21034    }
21035    if($HSearch)
21036    {
21037        # GCC standard paths
21038        if($GCC_PATH and not $NoStdInc)
21039        {
21040            my %DPaths = detect_inc_default_paths();
21041            @DefaultCppPaths = @{$DPaths{"Cpp"}};
21042            @DefaultGccPaths = @{$DPaths{"Gcc"}};
21043            @DefaultIncPaths = @{$DPaths{"Inc"}};
21044            push_U($SystemPaths{"include"}, @DefaultIncPaths);
21045        }
21046
21047        # users include paths
21048        my $IncPath = "/usr/include";
21049        if($SystemRoot) {
21050            $IncPath = $SystemRoot.$IncPath;
21051        }
21052        if(-d $IncPath) {
21053            push_U(\@UsersIncPath, $IncPath);
21054        }
21055    }
21056
21057    if($ExtraInfo)
21058    {
21059        writeFile($ExtraInfo."/default-libs", join("\n", @DefaultLibPaths));
21060        writeFile($ExtraInfo."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths)));
21061    }
21062}
21063
21064sub getLIB_EXT($)
21065{
21066    my $Target = $_[0];
21067    if(my $Ext = $OS_LibExt{$LIB_TYPE}{$Target}) {
21068        return $Ext;
21069    }
21070    return $OS_LibExt{$LIB_TYPE}{"default"};
21071}
21072
21073sub getAR_EXT($)
21074{
21075    my $Target = $_[0];
21076    if(my $Ext = $OS_Archive{$Target}) {
21077        return $Ext;
21078    }
21079    return $OS_Archive{"default"};
21080}
21081
21082sub get_dumpversion($)
21083{
21084    my $Cmd = $_[0];
21085    return "" if(not $Cmd);
21086    if($Cache{"get_dumpversion"}{$Cmd}) {
21087        return $Cache{"get_dumpversion"}{$Cmd};
21088    }
21089    my $V = `$Cmd -dumpversion 2>\"$TMP_DIR/null\"`;
21090    chomp($V);
21091    return ($Cache{"get_dumpversion"}{$Cmd} = $V);
21092}
21093
21094sub get_dumpmachine($)
21095{
21096    my $Cmd = $_[0];
21097    return "" if(not $Cmd);
21098    if($Cache{"get_dumpmachine"}{$Cmd}) {
21099        return $Cache{"get_dumpmachine"}{$Cmd};
21100    }
21101    my $Machine = `$Cmd -dumpmachine 2>\"$TMP_DIR/null\"`;
21102    chomp($Machine);
21103    return ($Cache{"get_dumpmachine"}{$Cmd} = $Machine);
21104}
21105
21106sub checkCmd($)
21107{
21108    my $Cmd = $_[0];
21109    return "" if(not $Cmd);
21110    my @Options = (
21111        "--version",
21112        "-help"
21113    );
21114    foreach my $Opt (@Options)
21115    {
21116        my $Info = `$Cmd $Opt 2>\"$TMP_DIR/null\"`;
21117        if($Info) {
21118            return 1;
21119        }
21120    }
21121    return 0;
21122}
21123
21124sub check_gcc($$)
21125{
21126    my ($Cmd, $ReqVer) = @_;
21127    return 0 if(not $Cmd or not $ReqVer);
21128    if(defined $Cache{"check_gcc"}{$Cmd}{$ReqVer}) {
21129        return $Cache{"check_gcc"}{$Cmd}{$ReqVer};
21130    }
21131    if(my $GccVer = get_dumpversion($Cmd))
21132    {
21133        $GccVer=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818")
21134        if(cmpVersions($GccVer, $ReqVer)>=0) {
21135            return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = $Cmd);
21136        }
21137    }
21138    return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = "");
21139}
21140
21141sub get_depth($)
21142{
21143    if(defined $Cache{"get_depth"}{$_[0]}) {
21144        return $Cache{"get_depth"}{$_[0]};
21145    }
21146    return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!));
21147}
21148
21149sub registerGccHeaders()
21150{
21151    return if($Cache{"registerGccHeaders"}); # this function should be called once
21152
21153    foreach my $Path (@DefaultGccPaths)
21154    {
21155        my @Headers = cmd_find($Path,"f");
21156        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
21157        foreach my $HPath (@Headers)
21158        {
21159            my $FileName = get_filename($HPath);
21160            if(not defined $DefaultGccHeader{$FileName})
21161            { # skip duplicated
21162                $DefaultGccHeader{$FileName} = $HPath;
21163            }
21164        }
21165    }
21166    $Cache{"registerGccHeaders"} = 1;
21167}
21168
21169sub registerCppHeaders()
21170{
21171    return if($Cache{"registerCppHeaders"}); # this function should be called once
21172
21173    foreach my $CppDir (@DefaultCppPaths)
21174    {
21175        my @Headers = cmd_find($CppDir,"f");
21176        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
21177        foreach my $Path (@Headers)
21178        {
21179            my $FileName = get_filename($Path);
21180            if(not defined $DefaultCppHeader{$FileName})
21181            { # skip duplicated
21182                $DefaultCppHeader{$FileName} = $Path;
21183            }
21184        }
21185    }
21186    $Cache{"registerCppHeaders"} = 1;
21187}
21188
21189sub parse_libname($$$)
21190{
21191    return "" if(not $_[0]);
21192    if(defined $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]}) {
21193        return $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]};
21194    }
21195    return ($Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]} = parse_libname_I(@_));
21196}
21197
21198sub parse_libname_I($$$)
21199{
21200    my ($Name, $Type, $Target) = @_;
21201
21202    if($Target eq "symbian") {
21203        return parse_libname_symbian($Name, $Type);
21204    }
21205    elsif($Target eq "windows") {
21206        return parse_libname_windows($Name, $Type);
21207    }
21208
21209    # unix
21210    my $Ext = getLIB_EXT($Target);
21211    if($Name=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/)
21212    { # libSDL-1.2.so.0.7.1
21213      # libwbxml2.so.0.0.18
21214      # libopcodes-2.21.53-system.20110810.so
21215        if($Type eq "name")
21216        { # libSDL-1.2
21217          # libwbxml2
21218            return $2;
21219        }
21220        elsif($Type eq "name+ext")
21221        { # libSDL-1.2.so
21222          # libwbxml2.so
21223            return $1;
21224        }
21225        elsif($Type eq "version")
21226        {
21227            if(defined $7
21228            and $7 ne "")
21229            { # 0.7.1
21230                return $7;
21231            }
21232            else
21233            { # libc-2.5.so (=>2.5 version)
21234                my $MV = $5;
21235                $MV=~s/\A[\-\_]+//g;
21236                return $MV;
21237            }
21238        }
21239        elsif($Type eq "short")
21240        { # libSDL
21241          # libwbxml2
21242            return $3;
21243        }
21244        elsif($Type eq "shortest")
21245        { # SDL
21246          # wbxml
21247            return shortest_name($3);
21248        }
21249    }
21250    return "";# error
21251}
21252
21253sub parse_libname_symbian($$)
21254{
21255    my ($Name, $Type) = @_;
21256    my $Ext = getLIB_EXT("symbian");
21257    if($Name=~/(((.+?)(\{.+\}|))\.$Ext)\Z/)
21258    { # libpthread{00010001}.dso
21259        if($Type eq "name")
21260        { # libpthread{00010001}
21261            return $2;
21262        }
21263        elsif($Type eq "name+ext")
21264        { # libpthread{00010001}.dso
21265            return $1;
21266        }
21267        elsif($Type eq "version")
21268        { # 00010001
21269            my $V = $4;
21270            $V=~s/\{(.+)\}/$1/;
21271            return $V;
21272        }
21273        elsif($Type eq "short")
21274        { # libpthread
21275            return $3;
21276        }
21277        elsif($Type eq "shortest")
21278        { # pthread
21279            return shortest_name($3);
21280        }
21281    }
21282    return "";# error
21283}
21284
21285sub parse_libname_windows($$)
21286{
21287    my ($Name, $Type) = @_;
21288    my $Ext = getLIB_EXT("windows");
21289    if($Name=~/((.+?)\.$Ext)\Z/)
21290    { # netapi32.dll
21291        if($Type eq "name")
21292        { # netapi32
21293            return $2;
21294        }
21295        elsif($Type eq "name+ext")
21296        { # netapi32.dll
21297            return $1;
21298        }
21299        elsif($Type eq "version")
21300        { # DLL version embedded
21301          # at binary-level
21302            return "";
21303        }
21304        elsif($Type eq "short")
21305        { # netapi32
21306            return $2;
21307        }
21308        elsif($Type eq "shortest")
21309        { # netapi
21310            return shortest_name($2);
21311        }
21312    }
21313    return "";# error
21314}
21315
21316sub shortest_name($)
21317{
21318    my $Name = $_[0];
21319    # remove prefix
21320    $Name=~s/\A(lib|open)//;
21321    # remove suffix
21322    $Name=~s/[\W\d_]+\Z//i;
21323    $Name=~s/([a-z]{2,})(lib)\Z/$1/i;
21324    return $Name;
21325}
21326
21327sub createSymbolsList($$$$$)
21328{
21329    my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_;
21330
21331    read_ABI_Dump(1, $DPath);
21332    prepareSymbols(1);
21333
21334    my %SymbolHeaderLib = ();
21335    my $Total = 0;
21336
21337    # Get List
21338    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
21339    {
21340        if(not link_symbol($Symbol, 1, "-Deps"))
21341        { # skip src only and all external functions
21342            next;
21343        }
21344        if(not symbolFilter($Symbol, 1, "Public", "Binary"))
21345        { # skip other symbols
21346            next;
21347        }
21348        my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
21349        if(not $HeaderName)
21350        { # skip src only and all external functions
21351            next;
21352        }
21353        my $DyLib = $Symbol_Library{1}{$Symbol};
21354        if(not $DyLib)
21355        { # skip src only and all external functions
21356            next;
21357        }
21358        $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1;
21359        $Total+=1;
21360    }
21361    # Draw List
21362    my $SYMBOLS_LIST = "<h1>Public symbols in <span style='color:Blue;'>$LName</span> (<span style='color:Red;'>$LVersion</span>)";
21363    $SYMBOLS_LIST .= " on <span style='color:Blue;'>".showArch($ArchName)."</span><br/>Total: $Total</h1><br/>";
21364    foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib))
21365    {
21366        foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}}))
21367        {
21368            my %NS_Symbol = ();
21369            foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) {
21370                $NS_Symbol{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
21371            }
21372            foreach my $NameSpace (sort keys(%NS_Symbol))
21373            {
21374                $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace);
21375                my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NS_Symbol{$NameSpace}});
21376                foreach my $Symbol (@SortedInterfaces)
21377                {
21378                    my $SubReport = "";
21379                    my $Signature = get_Signature($Symbol, 1);
21380                    if($NameSpace) {
21381                        $Signature=~s/\b\Q$NameSpace\E::\b//g;
21382                    }
21383                    if($Symbol=~/\A(_Z|\?)/)
21384                    {
21385                        if($Signature) {
21386                            $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span><br/><br/>".$ContentDivEnd."\n");
21387                        }
21388                        else {
21389                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21390                        }
21391                    }
21392                    else
21393                    {
21394                        if($Signature) {
21395                            $SubReport = "<span class='iname'>".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
21396                        }
21397                        else {
21398                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21399                        }
21400                    }
21401                    $SYMBOLS_LIST .= $SubReport;
21402                }
21403            }
21404            $SYMBOLS_LIST .= "<br/>\n";
21405        }
21406    }
21407    # clear info
21408    (%TypeInfo, %SymbolInfo, %Library_Symbol, %DepSymbol_Library,
21409    %DepLibrary_Symbol, %SymVer, %SkipTypes, %SkipSymbols,
21410    %NestedNameSpaces, %ClassMethods, %AllocableClass, %ClassNames,
21411    %CompleteSignature, %SkipNameSpaces, %Symbol_Library, %Library_Symbol) = ();
21412    ($Content_Counter, $ContentID) = (0, 0);
21413    # print report
21414    my $CssStyles = readModule("Styles", "SymbolsList.css");
21415    my $JScripts = readModule("Scripts", "Sections.js");
21416    $SYMBOLS_LIST = "<a name='Top'></a>".$SYMBOLS_LIST.$TOP_REF."<br/>\n";
21417    my $Title = "$LName: public symbols";
21418    my $Keywords = "$LName, API, symbols";
21419    my $Description = "List of symbols in $LName ($LVersion) on ".showArch($ArchName);
21420    $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."
21421    <body><div>\n$SYMBOLS_LIST</div>
21422    <br/><br/>\n".getReportFooter()."
21423    </body></html>";
21424    writeFile($SaveTo, $SYMBOLS_LIST);
21425}
21426
21427sub add_target_libs($)
21428{
21429    foreach (@{$_[0]}) {
21430        $TargetLibs{$_} = 1;
21431    }
21432}
21433
21434sub is_target_lib($)
21435{
21436    my $LName = $_[0];
21437    if(not $LName) {
21438        return 0;
21439    }
21440    if($TargetLibraryName
21441    and $LName!~/\Q$TargetLibraryName\E/) {
21442        return 0;
21443    }
21444    if(keys(%TargetLibs)
21445    and not $TargetLibs{$LName}
21446    and not $TargetLibs{parse_libname($LName, "name+ext", $OStarget)}) {
21447        return 0;
21448    }
21449    return 1;
21450}
21451
21452sub is_target_header($$)
21453{ # --header, --headers-list
21454    my ($H, $V) = @_;
21455    if(keys(%{$TargetHeaders{$V}}))
21456    {
21457        if($TargetHeaders{$V}{$H}) {
21458            return 1;
21459        }
21460    }
21461    return 0;
21462}
21463
21464sub readLibs($)
21465{
21466    my $LibVersion = $_[0];
21467    if($OStarget eq "windows")
21468    { # dumpbin.exe will crash
21469        # without VS Environment
21470        check_win32_env();
21471    }
21472    readSymbols($LibVersion);
21473    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
21474    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
21475}
21476
21477sub dump_sorting($)
21478{
21479    my $Hash = $_[0];
21480    return [] if(not $Hash);
21481    my @Keys = keys(%{$Hash});
21482    return [] if($#Keys<0);
21483    if($Keys[0]=~/\A\d+\Z/)
21484    { # numbers
21485        return [sort {int($a)<=>int($b)} @Keys];
21486    }
21487    else
21488    { # strings
21489        return [sort {$a cmp $b} @Keys];
21490    }
21491}
21492
21493sub printMsg($$)
21494{
21495    my ($Type, $Msg) = @_;
21496    if($Type!~/\AINFO/) {
21497        $Msg = $Type.": ".$Msg;
21498    }
21499    if($Type!~/_C\Z/) {
21500        $Msg .= "\n";
21501    }
21502    if($Quiet)
21503    { # --quiet option
21504        appendFile($COMMON_LOG_PATH, $Msg);
21505    }
21506    else
21507    {
21508        if($Type eq "ERROR") {
21509            print STDERR $Msg;
21510        }
21511        else {
21512            print $Msg;
21513        }
21514    }
21515}
21516
21517sub exitStatus($$)
21518{
21519    my ($Code, $Msg) = @_;
21520    printMsg("ERROR", $Msg);
21521    exit($ERROR_CODE{$Code});
21522}
21523
21524sub exitReport()
21525{ # the tool has run without any errors
21526    printReport();
21527    if($COMPILE_ERRORS)
21528    { # errors in headers may add false positives/negatives
21529        exit($ERROR_CODE{"Compile_Error"});
21530    }
21531    if($BinaryOnly and $RESULT{"Binary"}{"Problems"})
21532    { # --binary
21533        exit($ERROR_CODE{"Incompatible"});
21534    }
21535    elsif($SourceOnly and $RESULT{"Source"}{"Problems"})
21536    { # --source
21537        exit($ERROR_CODE{"Incompatible"});
21538    }
21539    elsif($RESULT{"Source"}{"Problems"}
21540    or $RESULT{"Binary"}{"Problems"})
21541    { # default
21542        exit($ERROR_CODE{"Incompatible"});
21543    }
21544    else {
21545        exit($ERROR_CODE{"Compatible"});
21546    }
21547}
21548
21549sub readRules($)
21550{
21551    my $Kind = $_[0];
21552    if(not -f $RULES_PATH{$Kind}) {
21553        exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
21554    }
21555    my $Content = readFile($RULES_PATH{$Kind});
21556    while(my $Rule = parseTag(\$Content, "rule"))
21557    {
21558        my $RId = parseTag(\$Rule, "id");
21559        my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
21560        foreach my $Prop (@Properties) {
21561            if(my $Value = parseTag(\$Rule, lc($Prop)))
21562            {
21563                $Value=~s/\n[ ]*//;
21564                $CompatRules{$Kind}{$RId}{$Prop} = $Value;
21565            }
21566        }
21567        if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) {
21568            $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols";
21569        }
21570        else {
21571            $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
21572        }
21573    }
21574}
21575
21576sub getReportPath($)
21577{
21578    my $Level = $_[0];
21579    my $Dir = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"};
21580    if($Level eq "Binary")
21581    {
21582        if($BinaryReportPath)
21583        { # --bin-report-path
21584            return $BinaryReportPath;
21585        }
21586        elsif($OutputReportPath)
21587        { # --report-path
21588            return $OutputReportPath;
21589        }
21590        else
21591        { # default
21592            return $Dir."/abi_compat_report.$ReportFormat";
21593        }
21594    }
21595    elsif($Level eq "Source")
21596    {
21597        if($SourceReportPath)
21598        { # --src-report-path
21599            return $SourceReportPath;
21600        }
21601        elsif($OutputReportPath)
21602        { # --report-path
21603            return $OutputReportPath;
21604        }
21605        else
21606        { # default
21607            return $Dir."/src_compat_report.$ReportFormat";
21608        }
21609    }
21610    else
21611    {
21612        if($OutputReportPath)
21613        { # --report-path
21614            return $OutputReportPath;
21615        }
21616        else
21617        { # default
21618            return $Dir."/compat_report.$ReportFormat";
21619        }
21620    }
21621}
21622
21623sub printStatMsg($)
21624{
21625    my $Level = $_[0];
21626    printMsg("INFO", "total \"$Level\" compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
21627}
21628
21629sub listAffected($)
21630{
21631    my $Level = $_[0];
21632    my $List = "";
21633    foreach (keys(%{$TotalAffected{$Level}}))
21634    {
21635        if($StrictCompat and $TotalAffected{$Level}{$_} eq "Low")
21636        { # skip "Low"-severity problems
21637            next;
21638        }
21639        $List .= "$_\n";
21640    }
21641    my $Dir = get_dirname(getReportPath($Level));
21642    if($Level eq "Binary") {
21643        writeFile($Dir."/abi_affected.txt", $List);
21644    }
21645    elsif($Level eq "Source") {
21646        writeFile($Dir."/src_affected.txt", $List);
21647    }
21648}
21649
21650sub printReport()
21651{
21652    printMsg("INFO", "creating compatibility report ...");
21653    createReport();
21654    if($JoinReport or $DoubleReport)
21655    {
21656        if($RESULT{"Binary"}{"Problems"}
21657        or $RESULT{"Source"}{"Problems"}) {
21658            printMsg("INFO", "result: INCOMPATIBLE (Binary: ".$RESULT{"Binary"}{"Affected"}."\%, Source: ".$RESULT{"Source"}{"Affected"}."\%)");
21659        }
21660        else {
21661            printMsg("INFO", "result: COMPATIBLE");
21662        }
21663        printStatMsg("Binary");
21664        printStatMsg("Source");
21665        if($ListAffected)
21666        { # --list-affected
21667            listAffected("Binary");
21668            listAffected("Source");
21669        }
21670    }
21671    elsif($BinaryOnly)
21672    {
21673        if($RESULT{"Binary"}{"Problems"}) {
21674            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Binary"}{"Affected"}."\%)");
21675        }
21676        else {
21677            printMsg("INFO", "result: COMPATIBLE");
21678        }
21679        printStatMsg("Binary");
21680        if($ListAffected)
21681        { # --list-affected
21682            listAffected("Binary");
21683        }
21684    }
21685    elsif($SourceOnly)
21686    {
21687        if($RESULT{"Source"}{"Problems"}) {
21688            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Source"}{"Affected"}."\%)");
21689        }
21690        else {
21691            printMsg("INFO", "result: COMPATIBLE");
21692        }
21693        printStatMsg("Source");
21694        if($ListAffected)
21695        { # --list-affected
21696            listAffected("Source");
21697        }
21698    }
21699    if($StdOut)
21700    {
21701        if($JoinReport or not $DoubleReport)
21702        { # --binary or --source
21703            printMsg("INFO", "compatibility report has been generated to stdout");
21704        }
21705        else
21706        { # default
21707            printMsg("INFO", "compatibility reports have been generated to stdout");
21708        }
21709    }
21710    else
21711    {
21712        if($JoinReport)
21713        {
21714            printMsg("INFO", "see detailed report:\n  ".getReportPath("Join"));
21715        }
21716        elsif($DoubleReport)
21717        { # default
21718            printMsg("INFO", "see detailed reports:\n  ".getReportPath("Binary")."\n  ".getReportPath("Source"));
21719        }
21720        elsif($BinaryOnly)
21721        { # --binary
21722            printMsg("INFO", "see detailed report:\n  ".getReportPath("Binary"));
21723        }
21724        elsif($SourceOnly)
21725        { # --source
21726            printMsg("INFO", "see detailed report:\n  ".getReportPath("Source"));
21727        }
21728    }
21729}
21730
21731sub check_win32_env()
21732{
21733    if(not $ENV{"DevEnvDir"}
21734    or not $ENV{"LIB"}) {
21735        exitStatus("Error", "can't start without VS environment (vsvars32.bat)");
21736    }
21737}
21738
21739sub diffSets($$)
21740{
21741    my ($S1, $S2) = @_;
21742    my @SK1 = keys(%{$S1});
21743    my @SK2 = keys(%{$S2});
21744    if($#SK1!=$#SK2) {
21745        return 1;
21746    }
21747    foreach my $K1 (@SK1)
21748    {
21749        if(not defined $S2->{$K1}) {
21750            return 1;
21751        }
21752    }
21753    return 0;
21754}
21755
21756sub defaultDumpPath($$)
21757{
21758    my ($N, $V) = @_;
21759    return "abi_dumps/".$N."/".$N."_".$V.".abi.".$AR_EXT; # gzipped by default
21760}
21761
21762sub create_ABI_Dump()
21763{
21764    if(not -e $DumpAPI) {
21765        exitStatus("Access_Error", "can't access \'$DumpAPI\'");
21766    }
21767
21768    if(isDump($DumpAPI)) {
21769        read_ABI_Dump(1, $DumpAPI);
21770    }
21771    else {
21772        readDescriptor(1, createDescriptor(1, $DumpAPI));
21773    }
21774
21775    if(not $Descriptor{1}{"Version"})
21776    { # set to default: N
21777        $Descriptor{1}{"Version"} = "N";
21778    }
21779
21780    initLogging(1);
21781    detect_default_paths("inc|lib|bin|gcc"); # complete analysis
21782
21783    my $DumpPath = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
21784    if($OutputDumpPath)
21785    { # user defined path
21786        $DumpPath = $OutputDumpPath;
21787    }
21788    my $Archive = ($DumpPath=~s/\Q.$AR_EXT\E\Z//g);
21789
21790    if(not $Archive and not $StdOut)
21791    { # check archive utilities
21792        if($OSgroup eq "windows")
21793        { # using zip
21794            my $ZipCmd = get_CmdPath("zip");
21795            if(not $ZipCmd) {
21796                exitStatus("Not_Found", "can't find \"zip\"");
21797            }
21798        }
21799        else
21800        { # using tar and gzip
21801            my $TarCmd = get_CmdPath("tar");
21802            if(not $TarCmd) {
21803                exitStatus("Not_Found", "can't find \"tar\"");
21804            }
21805            my $GzipCmd = get_CmdPath("gzip");
21806            if(not $GzipCmd) {
21807                exitStatus("Not_Found", "can't find \"gzip\"");
21808            }
21809        }
21810    }
21811
21812    if(not $Descriptor{1}{"Dump"})
21813    {
21814        if(not $CheckHeadersOnly) {
21815            readLibs(1);
21816        }
21817        if($CheckHeadersOnly) {
21818            setLanguage(1, "C++");
21819        }
21820        searchForHeaders(1);
21821        $WORD_SIZE{1} = detectWordSize(1);
21822    }
21823    if(not $Descriptor{1}{"Dump"})
21824    {
21825        if($Descriptor{1}{"Headers"}) {
21826            readHeaders(1);
21827        }
21828    }
21829    cleanDump(1);
21830    if(not keys(%{$SymbolInfo{1}}))
21831    { # check if created dump is valid
21832        if(not $ExtendedCheck)
21833        {
21834            if($CheckHeadersOnly) {
21835                exitStatus("Empty_Set", "the set of public symbols is empty");
21836            }
21837            else {
21838                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
21839            }
21840        }
21841    }
21842    my %HeadersInfo = ();
21843    foreach my $HPath (keys(%{$Registered_Headers{1}})) {
21844        $HeadersInfo{$Registered_Headers{1}{$HPath}{"Identity"}} = $Registered_Headers{1}{$HPath}{"Pos"};
21845    }
21846    if($ExtraDump)
21847    { # add unmangled names to the ABI dump
21848        my @Names = ();
21849        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
21850        {
21851            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"}) {
21852                push(@Names, $MnglName);
21853            }
21854        }
21855        translateSymbols(@Names, 1);
21856        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
21857        {
21858            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"})
21859            {
21860                if(my $Unmangled = $tr_name{$MnglName})
21861                {
21862                    if($MnglName ne $Unmangled) {
21863                        $SymbolInfo{1}{$InfoId}{"Unmangled"} = $Unmangled;
21864                    }
21865                }
21866            }
21867        }
21868    }
21869
21870    my %GccConstants = (); # built-in GCC constants
21871    foreach my $Name (keys(%{$Constants{1}}))
21872    {
21873        if(not defined $Constants{1}{$Name}{"Header"})
21874        {
21875            $GccConstants{$Name} = $Constants{1}{$Name}{"Value"};
21876            delete($Constants{1}{$Name});
21877        }
21878    }
21879
21880    printMsg("INFO", "creating library ABI dump ...");
21881    my %ABI = (
21882        "TypeInfo" => $TypeInfo{1},
21883        "SymbolInfo" => $SymbolInfo{1},
21884        "Symbols" => $Library_Symbol{1},
21885        "DepSymbols" => $DepLibrary_Symbol{1},
21886        "SymbolVersion" => $SymVer{1},
21887        "LibraryVersion" => $Descriptor{1}{"Version"},
21888        "LibraryName" => $TargetLibraryName,
21889        "Language" => $COMMON_LANGUAGE{1},
21890        "SkipTypes" => $SkipTypes{1},
21891        "SkipSymbols" => $SkipSymbols{1},
21892        "SkipNameSpaces" => $SkipNameSpaces{1},
21893        "SkipHeaders" => $SkipHeadersList{1},
21894        "Headers" => \%HeadersInfo,
21895        "Constants" => $Constants{1},
21896        "GccConstants" => \%GccConstants,
21897        "NameSpaces" => $NestedNameSpaces{1},
21898        "Target" => $OStarget,
21899        "Arch" => getArch(1),
21900        "WordSize" => $WORD_SIZE{1},
21901        "GccVersion" => get_dumpversion($GCC_PATH),
21902        "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION,
21903        "ABI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION
21904    );
21905    if(diffSets($TargetHeaders{1}, \%HeadersInfo)) {
21906        $ABI{"TargetHeaders"} = $TargetHeaders{1};
21907    }
21908    if($UseXML) {
21909        $ABI{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION;
21910    }
21911    if($ExtendedCheck)
21912    { # --ext option
21913        $ABI{"Mode"} = "Extended";
21914    }
21915    if($BinaryOnly)
21916    { # --binary
21917        $ABI{"BinOnly"} = 1;
21918    }
21919    if($ExtraDump)
21920    { # --extra-dump
21921        $ABI{"Extra"} = 1;
21922        $ABI{"UndefinedSymbols"} = $UndefinedSymbols{1};
21923        $ABI{"Needed"} = $Library_Needed{1};
21924    }
21925
21926    my $ABI_DUMP = "";
21927    if($UseXML)
21928    {
21929        loadModule("XmlDump");
21930        $ABI_DUMP = createXmlDump(\%ABI);
21931    }
21932    else
21933    { # default
21934        $ABI_DUMP = Dumper(\%ABI);
21935    }
21936    if($StdOut)
21937    { # --stdout option
21938        print STDOUT $ABI_DUMP;
21939        printMsg("INFO", "ABI dump has been generated to stdout");
21940        return;
21941    }
21942    else
21943    { # write to gzipped file
21944        my ($DDir, $DName) = separate_path($DumpPath);
21945        my $DPath = $TMP_DIR."/".$DName;
21946        if(not $Archive) {
21947            $DPath = $DumpPath;
21948        }
21949
21950        mkpath($DDir);
21951
21952        open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
21953        print DUMP $ABI_DUMP;
21954        close(DUMP);
21955
21956        if(not -s $DPath) {
21957            exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module");
21958        }
21959        if($Archive) {
21960            $DumpPath = createArchive($DPath, $DDir);
21961        }
21962
21963        if($OutputDumpPath) {
21964            printMsg("INFO", "library ABI has been dumped to:\n  $OutputDumpPath");
21965        }
21966        else {
21967            printMsg("INFO", "library ABI has been dumped to:\n  $DumpPath");
21968        }
21969        printMsg("INFO", "you can transfer this dump everywhere and use instead of the ".$Descriptor{1}{"Version"}." version descriptor");
21970    }
21971}
21972
21973sub quickEmptyReports()
21974{ # Quick "empty" reports
21975  # 4 times faster than merging equal dumps
21976  # NOTE: the dump contains the "LibraryVersion" attribute
21977  # if you change the version, then your dump will be different
21978  # OVERCOME: use -v1 and v2 options for comparing dumps
21979  # and don't change version in the XML descriptor (and dumps)
21980  # OVERCOME 2: separate meta info from the dumps in ACC 2.0
21981    if(-s $Descriptor{1}{"Path"} == -s $Descriptor{2}{"Path"})
21982    {
21983        my $FilePath1 = unpackDump($Descriptor{1}{"Path"});
21984        my $FilePath2 = unpackDump($Descriptor{2}{"Path"});
21985        if($FilePath1 and $FilePath2)
21986        {
21987            my $Line = readLineNum($FilePath1, 0);
21988            if($Line=~/xml/)
21989            { # XML format
21990                # is not supported yet
21991                return;
21992            }
21993
21994            local $/ = undef;
21995
21996            open(DUMP1, $FilePath1);
21997            my $Content1 = <DUMP1>;
21998            close(DUMP1);
21999
22000            open(DUMP2, $FilePath2);
22001            my $Content2 = <DUMP2>;
22002            close(DUMP2);
22003
22004            if($Content1 eq $Content2)
22005            {
22006                # clean memory
22007                undef $Content2;
22008
22009                # read a number of headers, libs, symbols and types
22010                my $ABIdump = eval($Content1);
22011
22012                # clean memory
22013                undef $Content1;
22014
22015                if(not $ABIdump) {
22016                    exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
22017                }
22018                if(not $ABIdump->{"TypeInfo"})
22019                { # support for old dumps
22020                    $ABIdump->{"TypeInfo"} = $ABIdump->{"TypeDescr"};
22021                }
22022                if(not $ABIdump->{"SymbolInfo"})
22023                { # support for old dumps
22024                    $ABIdump->{"SymbolInfo"} = $ABIdump->{"FuncDescr"};
22025                }
22026                read_Source_DumpInfo($ABIdump, 1);
22027                read_Libs_DumpInfo($ABIdump, 1);
22028                read_Machine_DumpInfo($ABIdump, 1);
22029                read_Machine_DumpInfo($ABIdump, 2);
22030
22031                %{$CheckedTypes{"Binary"}} = %{$ABIdump->{"TypeInfo"}};
22032                %{$CheckedTypes{"Source"}} = %{$ABIdump->{"TypeInfo"}};
22033
22034                %{$CheckedSymbols{"Binary"}} = %{$ABIdump->{"SymbolInfo"}};
22035                %{$CheckedSymbols{"Source"}} = %{$ABIdump->{"SymbolInfo"}};
22036
22037                $Descriptor{1}{"Version"} = $TargetVersion{1}?$TargetVersion{1}:$ABIdump->{"LibraryVersion"};
22038                $Descriptor{2}{"Version"} = $TargetVersion{2}?$TargetVersion{2}:$ABIdump->{"LibraryVersion"};
22039                exitReport();
22040            }
22041        }
22042    }
22043}
22044
22045sub initLogging($)
22046{
22047    my $LibVersion = $_[0];
22048    # create log directory
22049    my ($LOG_DIR, $LOG_FILE) = ("logs/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}, "log.txt");
22050    if($OutputLogPath{$LibVersion})
22051    { # user-defined by -log-path option
22052        ($LOG_DIR, $LOG_FILE) = separate_path($OutputLogPath{$LibVersion});
22053    }
22054    if($LogMode ne "n") {
22055        mkpath($LOG_DIR);
22056    }
22057    $LOG_PATH{$LibVersion} = get_abs_path($LOG_DIR)."/".$LOG_FILE;
22058    if($Debug)
22059    { # debug directory
22060        $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"};
22061
22062        if(not $ExtraInfo)
22063        { # enable --extra-info
22064            $ExtraInfo = $DEBUG_PATH{$LibVersion}."/extra-info";
22065        }
22066    }
22067    resetLogging($LibVersion);
22068}
22069
22070sub writeLog($$)
22071{
22072    my ($LibVersion, $Msg) = @_;
22073    if($LogMode ne "n") {
22074        appendFile($LOG_PATH{$LibVersion}, $Msg);
22075    }
22076}
22077
22078sub resetLogging($)
22079{
22080    my $LibVersion = $_[0];
22081    if($LogMode!~/a|n/)
22082    { # remove old log
22083        unlink($LOG_PATH{$LibVersion});
22084        if($Debug) {
22085            rmtree($DEBUG_PATH{$LibVersion});
22086        }
22087    }
22088}
22089
22090sub printErrorLog($)
22091{
22092    my $LibVersion = $_[0];
22093    if($LogMode ne "n") {
22094        printMsg("ERROR", "see log for details:\n  ".$LOG_PATH{$LibVersion}."\n");
22095    }
22096}
22097
22098sub isDump($)
22099{
22100    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.tar\.gz(\.\w+|)|\.zip|\.xml|)\Z/)
22101    { # NOTE: name.abi.tar.gz.amd64 (dh & cdbs)
22102        return $1;
22103    }
22104    return 0;
22105}
22106
22107sub isDump_U($)
22108{
22109    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.xml|)\Z/) {
22110        return $1;
22111    }
22112    return 0;
22113}
22114
22115sub compareInit()
22116{
22117    # read input XML descriptors or ABI dumps
22118    if(not $Descriptor{1}{"Path"}) {
22119        exitStatus("Error", "-old option is not specified");
22120    }
22121    if(not -e $Descriptor{1}{"Path"}) {
22122        exitStatus("Access_Error", "can't access \'".$Descriptor{1}{"Path"}."\'");
22123    }
22124
22125    if(not $Descriptor{2}{"Path"}) {
22126        exitStatus("Error", "-new option is not specified");
22127    }
22128    if(not -e $Descriptor{2}{"Path"}) {
22129        exitStatus("Access_Error", "can't access \'".$Descriptor{2}{"Path"}."\'");
22130    }
22131
22132    detect_default_paths("bin"); # to extract dumps
22133    if(isDump($Descriptor{1}{"Path"})
22134    and isDump($Descriptor{2}{"Path"}))
22135    { # optimization: equal ABI dumps
22136        quickEmptyReports();
22137    }
22138
22139    printMsg("INFO", "preparation, please wait ...");
22140
22141    if(isDump($Descriptor{1}{"Path"})) {
22142        read_ABI_Dump(1, $Descriptor{1}{"Path"});
22143    }
22144    else {
22145        readDescriptor(1, createDescriptor(1, $Descriptor{1}{"Path"}));
22146    }
22147
22148    if(isDump($Descriptor{2}{"Path"})) {
22149        read_ABI_Dump(2, $Descriptor{2}{"Path"});
22150    }
22151    else {
22152        readDescriptor(2, createDescriptor(2, $Descriptor{2}{"Path"}));
22153    }
22154
22155    if(not $Descriptor{1}{"Version"})
22156    { # set to default: X
22157        $Descriptor{1}{"Version"} = "X";
22158        print STDERR "WARNING: version number #1 is not set (use --v1=NUM option)\n";
22159    }
22160
22161    if(not $Descriptor{2}{"Version"})
22162    { # set to default: Y
22163        $Descriptor{2}{"Version"} = "Y";
22164        print STDERR "WARNING: version number #2 is not set (use --v2=NUM option)\n";
22165    }
22166
22167    if(not $UsedDump{1}{"V"}) {
22168        initLogging(1);
22169    }
22170
22171    if(not $UsedDump{2}{"V"}) {
22172        initLogging(2);
22173    }
22174
22175    # check input data
22176    if(not $Descriptor{1}{"Headers"}) {
22177        exitStatus("Error", "can't find header files info in descriptor d1");
22178    }
22179    if(not $Descriptor{2}{"Headers"}) {
22180        exitStatus("Error", "can't find header files info in descriptor d2");
22181    }
22182
22183    if(not $CheckHeadersOnly)
22184    {
22185        if(not $Descriptor{1}{"Libs"}) {
22186            exitStatus("Error", "can't find libraries info in descriptor d1");
22187        }
22188        if(not $Descriptor{2}{"Libs"}) {
22189            exitStatus("Error", "can't find libraries info in descriptor d2");
22190        }
22191    }
22192
22193    if($UseDumps)
22194    { # --use-dumps
22195      # parallel processing
22196        my $DumpPath1 = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
22197        my $DumpPath2 = defaultDumpPath($TargetLibraryName, $Descriptor{2}{"Version"});
22198
22199        unlink($DumpPath1);
22200        unlink($DumpPath2);
22201
22202        my $pid = fork();
22203        if($pid)
22204        { # dump on two CPU cores
22205            my @PARAMS = ("-dump", $Descriptor{1}{"Path"}, "-l", $TargetLibraryName);
22206            if($RelativeDirectory{1}) {
22207                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{1});
22208            }
22209            if($OutputLogPath{1}) {
22210                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{1});
22211            }
22212            if($CrossGcc) {
22213                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22214            }
22215            if($Quiet)
22216            {
22217                @PARAMS = (@PARAMS, "-quiet");
22218                @PARAMS = (@PARAMS, "-logging-mode", "a");
22219            }
22220            elsif($LogMode and $LogMode ne "w")
22221            { # "w" is default
22222                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22223            }
22224            if($ExtendedCheck) {
22225                @PARAMS = (@PARAMS, "-extended");
22226            }
22227            if($UserLang) {
22228                @PARAMS = (@PARAMS, "-lang", $UserLang);
22229            }
22230            if($TargetVersion{1}) {
22231                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{1});
22232            }
22233            if($BinaryOnly) {
22234                @PARAMS = (@PARAMS, "-binary");
22235            }
22236            if($SourceOnly) {
22237                @PARAMS = (@PARAMS, "-source");
22238            }
22239            if($SortDump) {
22240                @PARAMS = (@PARAMS, "-sort");
22241            }
22242            if($DumpFormat and $DumpFormat ne "perl") {
22243                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22244            }
22245            if($CheckHeadersOnly) {
22246                @PARAMS = (@PARAMS, "-headers-only");
22247            }
22248            if($Debug)
22249            {
22250                @PARAMS = (@PARAMS, "-debug");
22251                printMsg("INFO", "running perl $0 @PARAMS");
22252            }
22253            system("perl", $0, @PARAMS);
22254            if(not -f $DumpPath1) {
22255                exit(1);
22256            }
22257        }
22258        else
22259        { # child
22260            my @PARAMS = ("-dump", $Descriptor{2}{"Path"}, "-l", $TargetLibraryName);
22261            if($RelativeDirectory{2}) {
22262                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{2});
22263            }
22264            if($OutputLogPath{2}) {
22265                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{2});
22266            }
22267            if($CrossGcc) {
22268                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22269            }
22270            if($Quiet)
22271            {
22272                @PARAMS = (@PARAMS, "-quiet");
22273                @PARAMS = (@PARAMS, "-logging-mode", "a");
22274            }
22275            elsif($LogMode and $LogMode ne "w")
22276            { # "w" is default
22277                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22278            }
22279            if($ExtendedCheck) {
22280                @PARAMS = (@PARAMS, "-extended");
22281            }
22282            if($UserLang) {
22283                @PARAMS = (@PARAMS, "-lang", $UserLang);
22284            }
22285            if($TargetVersion{2}) {
22286                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{2});
22287            }
22288            if($BinaryOnly) {
22289                @PARAMS = (@PARAMS, "-binary");
22290            }
22291            if($SourceOnly) {
22292                @PARAMS = (@PARAMS, "-source");
22293            }
22294            if($SortDump) {
22295                @PARAMS = (@PARAMS, "-sort");
22296            }
22297            if($DumpFormat and $DumpFormat ne "perl") {
22298                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22299            }
22300            if($CheckHeadersOnly) {
22301                @PARAMS = (@PARAMS, "-headers-only");
22302            }
22303            if($Debug)
22304            {
22305                @PARAMS = (@PARAMS, "-debug");
22306                printMsg("INFO", "running perl $0 @PARAMS");
22307            }
22308            system("perl", $0, @PARAMS);
22309            if(not -f $DumpPath2) {
22310                exit(1);
22311            }
22312            else {
22313                exit(0);
22314            }
22315        }
22316        waitpid($pid, 0);
22317
22318        my @CMP_PARAMS = ("-l", $TargetLibraryName);
22319        @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1);
22320        @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2);
22321        if($TargetTitle ne $TargetLibraryName) {
22322            @CMP_PARAMS = (@CMP_PARAMS, "-title", $TargetTitle);
22323        }
22324        if($ShowRetVal) {
22325            @CMP_PARAMS = (@CMP_PARAMS, "-show-retval");
22326        }
22327        if($CrossGcc) {
22328            @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $CrossGcc);
22329        }
22330        @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a");
22331        if($Quiet) {
22332            @CMP_PARAMS = (@CMP_PARAMS, "-quiet");
22333        }
22334        if($ReportFormat and $ReportFormat ne "html")
22335        { # HTML is default format
22336            @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $ReportFormat);
22337        }
22338        if($OutputReportPath) {
22339            @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $OutputReportPath);
22340        }
22341        if($BinaryReportPath) {
22342            @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $BinaryReportPath);
22343        }
22344        if($SourceReportPath) {
22345            @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $SourceReportPath);
22346        }
22347        if($LoggingPath) {
22348            @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $LoggingPath);
22349        }
22350        if($CheckHeadersOnly) {
22351            @CMP_PARAMS = (@CMP_PARAMS, "-headers-only");
22352        }
22353        if($BinaryOnly) {
22354            @CMP_PARAMS = (@CMP_PARAMS, "-binary");
22355        }
22356        if($SourceOnly) {
22357            @CMP_PARAMS = (@CMP_PARAMS, "-source");
22358        }
22359        if($Debug)
22360        {
22361            @CMP_PARAMS = (@CMP_PARAMS, "-debug");
22362            printMsg("INFO", "running perl $0 @CMP_PARAMS");
22363        }
22364        system("perl", $0, @CMP_PARAMS);
22365        exit($?>>8);
22366    }
22367    if(not $Descriptor{1}{"Dump"}
22368    or not $Descriptor{2}{"Dump"})
22369    { # need GCC toolchain to analyze
22370      # header files and libraries
22371        detect_default_paths("inc|lib|gcc");
22372    }
22373    if(not $Descriptor{1}{"Dump"})
22374    {
22375        if(not $CheckHeadersOnly) {
22376            readLibs(1);
22377        }
22378        if($CheckHeadersOnly) {
22379            setLanguage(1, "C++");
22380        }
22381        searchForHeaders(1);
22382        $WORD_SIZE{1} = detectWordSize(1);
22383    }
22384    if(not $Descriptor{2}{"Dump"})
22385    {
22386        if(not $CheckHeadersOnly) {
22387            readLibs(2);
22388        }
22389        if($CheckHeadersOnly) {
22390            setLanguage(2, "C++");
22391        }
22392        searchForHeaders(2);
22393        $WORD_SIZE{2} = detectWordSize(2);
22394    }
22395    if($WORD_SIZE{1} ne $WORD_SIZE{2})
22396    { # support for old ABI dumps
22397      # try to synch different WORD sizes
22398        if(not checkDump(1, "2.1"))
22399        {
22400            $WORD_SIZE{1} = $WORD_SIZE{2};
22401            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{2}." bytes");
22402        }
22403        elsif(not checkDump(2, "2.1"))
22404        {
22405            $WORD_SIZE{2} = $WORD_SIZE{1};
22406            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{1}." bytes");
22407        }
22408    }
22409    elsif(not $WORD_SIZE{1}
22410    and not $WORD_SIZE{2})
22411    { # support for old ABI dumps
22412        $WORD_SIZE{1} = "4";
22413        $WORD_SIZE{2} = "4";
22414    }
22415    if($Descriptor{1}{"Dump"})
22416    { # support for old ABI dumps
22417        prepareTypes(1);
22418    }
22419    if($Descriptor{2}{"Dump"})
22420    { # support for old ABI dumps
22421        prepareTypes(2);
22422    }
22423    if($AppPath and not keys(%{$Symbol_Library{1}})) {
22424        printMsg("WARNING", "the application ".get_filename($AppPath)." has no symbols imported from the $SLIB_TYPE libraries");
22425    }
22426    # process input data
22427    if($Descriptor{1}{"Headers"}
22428    and not $Descriptor{1}{"Dump"}) {
22429        readHeaders(1);
22430    }
22431    if($Descriptor{2}{"Headers"}
22432    and not $Descriptor{2}{"Dump"}) {
22433        readHeaders(2);
22434    }
22435
22436    # clean memory
22437    %SystemHeaders = ();
22438    %mangled_name_gcc = ();
22439
22440    prepareSymbols(1);
22441    prepareSymbols(2);
22442
22443    # clean memory
22444    %SymbolInfo = ();
22445
22446    # Virtual Tables
22447    registerVTable(1);
22448    registerVTable(2);
22449
22450    if(not checkDump(1, "1.22")
22451    and checkDump(2, "1.22"))
22452    { # support for old ABI dumps
22453        foreach my $ClassName (keys(%{$VirtualTable{2}}))
22454        {
22455            if($ClassName=~/</)
22456            { # templates
22457                if(not defined $VirtualTable{1}{$ClassName})
22458                { # synchronize
22459                    delete($VirtualTable{2}{$ClassName});
22460                }
22461            }
22462        }
22463    }
22464
22465    registerOverriding(1);
22466    registerOverriding(2);
22467
22468    setVirtFuncPositions(1);
22469    setVirtFuncPositions(2);
22470
22471    # Other
22472    addParamNames(1);
22473    addParamNames(2);
22474
22475    detectChangedTypedefs();
22476}
22477
22478sub compareAPIs($)
22479{
22480    my $Level = $_[0];
22481
22482    readRules($Level);
22483    loadModule("CallConv");
22484
22485    if($Level eq "Binary") {
22486        printMsg("INFO", "comparing ABIs ...");
22487    }
22488    else {
22489        printMsg("INFO", "comparing APIs ...");
22490    }
22491
22492    if($CheckHeadersOnly
22493    or $Level eq "Source")
22494    { # added/removed in headers
22495        detectAdded_H($Level);
22496        detectRemoved_H($Level);
22497    }
22498    else
22499    { # added/removed in libs
22500        detectAdded($Level);
22501        detectRemoved($Level);
22502    }
22503
22504    mergeSymbols($Level);
22505    if(keys(%{$CheckedSymbols{$Level}})) {
22506        mergeConstants($Level);
22507    }
22508
22509    $Cache{"mergeTypes"} = (); # free memory
22510
22511    if($CheckHeadersOnly
22512    or $Level eq "Source")
22513    { # added/removed in headers
22514        mergeHeaders($Level);
22515    }
22516    else
22517    { # added/removed in libs
22518        mergeLibs($Level);
22519    }
22520}
22521
22522sub getSysOpts()
22523{
22524    my %Opts = (
22525    "OStarget"=>$OStarget,
22526    "Debug"=>$Debug,
22527    "Quiet"=>$Quiet,
22528    "LogMode"=>$LogMode,
22529    "CheckHeadersOnly"=>$CheckHeadersOnly,
22530
22531    "SystemRoot"=>$SystemRoot,
22532    "GCC_PATH"=>$GCC_PATH,
22533    "TargetSysInfo"=>$TargetSysInfo,
22534    "CrossPrefix"=>$CrossPrefix,
22535    "TargetLibraryName"=>$TargetLibraryName,
22536    "CrossGcc"=>$CrossGcc,
22537    "UseStaticLibs"=>$UseStaticLibs,
22538    "NoStdInc"=>$NoStdInc,
22539
22540    "BinaryOnly" => $BinaryOnly,
22541    "SourceOnly" => $SourceOnly
22542    );
22543    return \%Opts;
22544}
22545
22546sub get_CodeError($)
22547{
22548    my %CODE_ERROR = reverse(%ERROR_CODE);
22549    return $CODE_ERROR{$_[0]};
22550}
22551
22552sub scenario()
22553{
22554    if($StdOut)
22555    { # enable quiet mode
22556        $Quiet = 1;
22557        $JoinReport = 1;
22558    }
22559    if(not $LogMode)
22560    { # default
22561        $LogMode = "w";
22562    }
22563    if($UserLang)
22564    { # --lang=C++
22565        $UserLang = uc($UserLang);
22566        $COMMON_LANGUAGE{1}=$UserLang;
22567        $COMMON_LANGUAGE{2}=$UserLang;
22568    }
22569    if($LoggingPath)
22570    {
22571        $OutputLogPath{1} = $LoggingPath;
22572        $OutputLogPath{2} = $LoggingPath;
22573        if($Quiet) {
22574            $COMMON_LOG_PATH = $LoggingPath;
22575        }
22576    }
22577
22578    if($SkipInternalSymbols) {
22579        $SkipInternalSymbols=~s/\*/.*/g;
22580    }
22581
22582    if($SkipInternalTypes) {
22583        $SkipInternalTypes=~s/\*/.*/g;
22584    }
22585
22586    if($Quick) {
22587        $ADD_TMPL_INSTANCES = 0;
22588    }
22589    if($OutputDumpPath)
22590    { # validate
22591        if(not isDump($OutputDumpPath)) {
22592            exitStatus("Error", "the dump path should be a path to *.abi.$AR_EXT or *.abi file");
22593        }
22594    }
22595    if($BinaryOnly and $SourceOnly)
22596    { # both --binary and --source
22597      # is the default mode
22598        if(not $CmpSystems)
22599        {
22600            $BinaryOnly = 0;
22601            $SourceOnly = 0;
22602        }
22603
22604        $DoubleReport = 1;
22605        $JoinReport = 0;
22606
22607        if($OutputReportPath)
22608        { # --report-path
22609            $DoubleReport = 0;
22610            $JoinReport = 1;
22611        }
22612    }
22613    elsif($BinaryOnly or $SourceOnly)
22614    { # --binary or --source
22615        $DoubleReport = 0;
22616        $JoinReport = 0;
22617    }
22618    if($UseXML)
22619    { # --xml option
22620        $ReportFormat = "xml";
22621        $DumpFormat = "xml";
22622    }
22623    if($ReportFormat)
22624    { # validate
22625        $ReportFormat = lc($ReportFormat);
22626        if($ReportFormat!~/\A(xml|html|htm)\Z/) {
22627            exitStatus("Error", "unknown report format \'$ReportFormat\'");
22628        }
22629        if($ReportFormat eq "htm")
22630        { # HTM == HTML
22631            $ReportFormat = "html";
22632        }
22633        elsif($ReportFormat eq "xml")
22634        { # --report-format=XML equal to --xml
22635            $UseXML = 1;
22636        }
22637    }
22638    else
22639    { # default: HTML
22640        $ReportFormat = "html";
22641    }
22642    if($DumpFormat)
22643    { # validate
22644        $DumpFormat = lc($DumpFormat);
22645        if($DumpFormat!~/\A(xml|perl)\Z/) {
22646            exitStatus("Error", "unknown ABI dump format \'$DumpFormat\'");
22647        }
22648        if($DumpFormat eq "xml")
22649        { # --dump-format=XML equal to --xml
22650            $UseXML = 1;
22651        }
22652    }
22653    else
22654    { # default: Perl Data::Dumper
22655        $DumpFormat = "perl";
22656    }
22657    if($Quiet and $LogMode!~/a|n/)
22658    { # --quiet log
22659        if(-f $COMMON_LOG_PATH) {
22660            unlink($COMMON_LOG_PATH);
22661        }
22662    }
22663    if($ExtraInfo) {
22664        $CheckUndefined = 1;
22665    }
22666    if($TestTool and $UseDumps)
22667    { # --test && --use-dumps == --test-dump
22668        $TestDump = 1;
22669    }
22670    if($Tolerant)
22671    { # enable all
22672        $Tolerance = 1234;
22673    }
22674    if($Help)
22675    {
22676        HELP_MESSAGE();
22677        exit(0);
22678    }
22679    if($InfoMsg)
22680    {
22681        INFO_MESSAGE();
22682        exit(0);
22683    }
22684    if($ShowVersion)
22685    {
22686        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.");
22687        exit(0);
22688    }
22689    if($DumpVersion)
22690    {
22691        printMsg("INFO", $TOOL_VERSION);
22692        exit(0);
22693    }
22694    if($ExtendedCheck) {
22695        $CheckHeadersOnly = 1;
22696    }
22697    if($SystemRoot_Opt)
22698    { # user defined root
22699        if(not -e $SystemRoot_Opt) {
22700            exitStatus("Access_Error", "can't access \'$SystemRoot\'");
22701        }
22702        $SystemRoot = $SystemRoot_Opt;
22703        $SystemRoot=~s/[\/]+\Z//g;
22704        if($SystemRoot) {
22705            $SystemRoot = get_abs_path($SystemRoot);
22706        }
22707    }
22708    $Data::Dumper::Sortkeys = 1;
22709
22710    if($SortDump)
22711    {
22712        $Data::Dumper::Useperl = 1;
22713        $Data::Dumper::Sortkeys = \&dump_sorting;
22714    }
22715
22716    if($TargetLibsPath)
22717    {
22718        if(not -f $TargetLibsPath) {
22719            exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'");
22720        }
22721        foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) {
22722            $TargetLibs{$Lib} = 1;
22723        }
22724    }
22725    if($TargetHeadersPath)
22726    { # --headers-list
22727        if(not -f $TargetHeadersPath) {
22728            exitStatus("Access_Error", "can't access file \'$TargetHeadersPath\'");
22729        }
22730        foreach my $Header (split(/\s*\n\s*/, readFile($TargetHeadersPath)))
22731        {
22732            $TargetHeaders{1}{get_filename($Header)} = 1;
22733            $TargetHeaders{2}{get_filename($Header)} = 1;
22734        }
22735    }
22736    if($TargetHeader)
22737    { # --header
22738        $TargetHeaders{1}{get_filename($TargetHeader)} = 1;
22739        $TargetHeaders{2}{get_filename($TargetHeader)} = 1;
22740    }
22741    if($TestTool
22742    or $TestDump)
22743    { # --test, --test-dump
22744        detect_default_paths("bin|gcc"); # to compile libs
22745        loadModule("RegTests");
22746        testTool($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, $DumpFormat,
22747        $LIB_EXT, $GCC_PATH, $SortDump, $CheckHeadersOnly);
22748        exit(0);
22749    }
22750    if($DumpSystem)
22751    { # --dump-system
22752
22753        if(not $TargetSysInfo) {
22754            exitStatus("Error", "-sysinfo option should be specified to dump system ABI");
22755        }
22756
22757        if(not -d $TargetSysInfo) {
22758            exitStatus("Access_Error", "can't access \'$TargetSysInfo\'");
22759        }
22760
22761        loadModule("SysCheck");
22762        if($DumpSystem=~/\.(xml|desc)\Z/)
22763        { # system XML descriptor
22764            if(not -f $DumpSystem) {
22765                exitStatus("Access_Error", "can't access file \'$DumpSystem\'");
22766            }
22767
22768            my $SDesc = readFile($DumpSystem);
22769            if(my $RelDir = $RelativeDirectory{1}) {
22770                $SDesc =~ s/{RELPATH}/$RelDir/g;
22771            }
22772
22773            my $Ret = readSystemDescriptor($SDesc);
22774            foreach (@{$Ret->{"Tools"}})
22775            {
22776                push_U($SystemPaths{"bin"}, $_);
22777                $TargetTools{$_} = 1;
22778            }
22779            if($Ret->{"CrossPrefix"}) {
22780                $CrossPrefix = $Ret->{"CrossPrefix"};
22781            }
22782        }
22783        elsif($SystemRoot_Opt)
22784        { # -sysroot "/" option
22785          # default target: /usr/lib, /usr/include
22786          # search libs: /usr/lib and /lib
22787            if(not -e $SystemRoot."/usr/lib") {
22788                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'");
22789            }
22790            if(not -e $SystemRoot."/lib") {
22791                exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'");
22792            }
22793            if(not -e $SystemRoot."/usr/include") {
22794                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'");
22795            }
22796            readSystemDescriptor("
22797                <name>
22798                    $DumpSystem
22799                </name>
22800                <headers>
22801                    $SystemRoot/usr/include
22802                </headers>
22803                <libs>
22804                    $SystemRoot/usr/lib
22805                </libs>
22806                <search_libs>
22807                    $SystemRoot/lib
22808                </search_libs>");
22809        }
22810        else {
22811            exitStatus("Error", "-sysroot <dirpath> option should be specified, usually it's \"/\"");
22812        }
22813        detect_default_paths("bin|gcc"); # to check symbols
22814        if($OStarget eq "windows")
22815        { # to run dumpbin.exe
22816          # and undname.exe
22817            check_win32_env();
22818        }
22819        dumpSystem(getSysOpts());
22820        exit(0);
22821    }
22822    if($CmpSystems)
22823    { # --cmp-systems
22824        detect_default_paths("bin"); # to extract dumps
22825        loadModule("SysCheck");
22826        cmpSystems($Descriptor{1}{"Path"}, $Descriptor{2}{"Path"}, getSysOpts());
22827        exit(0);
22828    }
22829    if(not $TargetLibraryName) {
22830        exitStatus("Error", "library name is not selected (-l option)");
22831    }
22832    else
22833    { # validate library name
22834        if($TargetLibraryName=~/[\*\/\\]/) {
22835            exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
22836        }
22837    }
22838    if(not $TargetTitle) {
22839        $TargetTitle = $TargetLibraryName;
22840    }
22841
22842    if($SymbolsListPath)
22843    {
22844        if(not -f $SymbolsListPath) {
22845            exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
22846        }
22847        foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) {
22848            $SymbolsList{$Interface} = 1;
22849        }
22850    }
22851    if($TypesListPath)
22852    {
22853        if(not -f $TypesListPath) {
22854            exitStatus("Access_Error", "can't access file \'$TypesListPath\'");
22855        }
22856        foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) {
22857            $TypesList{$Type} = 1;
22858        }
22859    }
22860    if($SkipSymbolsListPath)
22861    {
22862        if(not -f $SkipSymbolsListPath) {
22863            exitStatus("Access_Error", "can't access file \'$SkipSymbolsListPath\'");
22864        }
22865        foreach my $Interface (split(/\s*\n\s*/, readFile($SkipSymbolsListPath)))
22866        {
22867            $SkipSymbols{1}{$Interface} = 1;
22868            $SkipSymbols{2}{$Interface} = 1;
22869        }
22870    }
22871    if($SkipTypesListPath)
22872    {
22873        if(not -f $SkipTypesListPath) {
22874            exitStatus("Access_Error", "can't access file \'$SkipTypesListPath\'");
22875        }
22876        foreach my $Type (split(/\s*\n\s*/, readFile($SkipTypesListPath)))
22877        {
22878            $SkipTypes{1}{$Type} = 1;
22879            $SkipTypes{2}{$Type} = 1;
22880        }
22881    }
22882    if($SkipHeadersPath)
22883    {
22884        if(not -f $SkipHeadersPath) {
22885            exitStatus("Access_Error", "can't access file \'$SkipHeadersPath\'");
22886        }
22887        foreach my $Path (split(/\s*\n\s*/, readFile($SkipHeadersPath)))
22888        { # register for both versions
22889            $SkipHeadersList{1}{$Path} = 1;
22890            $SkipHeadersList{2}{$Path} = 1;
22891            my ($CPath, $Type) = classifyPath($Path);
22892            $SkipHeaders{1}{$Type}{$CPath} = 1;
22893            $SkipHeaders{2}{$Type}{$CPath} = 1;
22894        }
22895    }
22896    if($ParamNamesPath)
22897    {
22898        if(not -f $ParamNamesPath) {
22899            exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'");
22900        }
22901        foreach my $Line (split(/\n/, readFile($ParamNamesPath)))
22902        {
22903            if($Line=~s/\A(\w+)\;//)
22904            {
22905                my $Interface = $1;
22906                if($Line=~/;(\d+);/)
22907                {
22908                    while($Line=~s/(\d+);(\w+)//) {
22909                        $AddIntParams{$Interface}{$1}=$2;
22910                    }
22911                }
22912                else
22913                {
22914                    my $Num = 0;
22915                    foreach my $Name (split(/;/, $Line)) {
22916                        $AddIntParams{$Interface}{$Num++}=$Name;
22917                    }
22918                }
22919            }
22920        }
22921    }
22922    if($AppPath)
22923    {
22924        if(not -f $AppPath) {
22925            exitStatus("Access_Error", "can't access file \'$AppPath\'");
22926        }
22927
22928        detect_default_paths("bin|gcc");
22929        foreach my $Interface (readSymbols_App($AppPath)) {
22930            $SymbolsList_App{$Interface} = 1;
22931        }
22932    }
22933    if($DumpAPI)
22934    { # --dump-abi
22935      # make an API dump
22936        create_ABI_Dump();
22937        exit($COMPILE_ERRORS);
22938    }
22939    # default: compare APIs
22940    #  -d1 <path>
22941    #  -d2 <path>
22942    compareInit();
22943    if($JoinReport or $DoubleReport)
22944    {
22945        compareAPIs("Binary");
22946        compareAPIs("Source");
22947    }
22948    elsif($BinaryOnly) {
22949        compareAPIs("Binary");
22950    }
22951    elsif($SourceOnly) {
22952        compareAPIs("Source");
22953    }
22954    exitReport();
22955}
22956
22957scenario();
22958