1###########################################################################
2# A module to find system files and automatically generate include paths
3#
4# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory
5#
6# Written by Andrey Ponomarenko
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21# MA  02110-1301 USA
22###########################################################################
23use strict;
24
25loadModule("ElfTools");
26
27my %Cache;
28
29my %BinUtils = map {$_=>1} (
30    "c++filt",
31    "objdump",
32    "readelf"
33);
34
35# Header file extensions as described by gcc
36my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+|tcc|txx|x|inl|inc|ads|isph";
37
38my %GlibcHeader = map {$_=>1} (
39    "aliases.h",
40    "argp.h",
41    "argz.h",
42    "assert.h",
43    "cpio.h",
44    "ctype.h",
45    "dirent.h",
46    "envz.h",
47    "errno.h",
48    "error.h",
49    "execinfo.h",
50    "fcntl.h",
51    "fstab.h",
52    "ftw.h",
53    "glob.h",
54    "grp.h",
55    "iconv.h",
56    "ifaddrs.h",
57    "inttypes.h",
58    "langinfo.h",
59    "limits.h",
60    "link.h",
61    "locale.h",
62    "malloc.h",
63    "math.h",
64    "mntent.h",
65    "monetary.h",
66    "nl_types.h",
67    "obstack.h",
68    "printf.h",
69    "pwd.h",
70    "regex.h",
71    "sched.h",
72    "search.h",
73    "setjmp.h",
74    "shadow.h",
75    "signal.h",
76    "spawn.h",
77    "stdarg.h",
78    "stdint.h",
79    "stdio.h",
80    "stdlib.h",
81    "string.h",
82    "strings.h",
83    "tar.h",
84    "termios.h",
85    "time.h",
86    "ulimit.h",
87    "unistd.h",
88    "utime.h",
89    "wchar.h",
90    "wctype.h",
91    "wordexp.h" );
92
93my %GlibcDir = map {$_=>1} (
94    "arpa",
95    "bits",
96    "gnu",
97    "netinet",
98    "net",
99    "nfs",
100    "rpc",
101    "sys",
102    "linux" );
103
104my %WinHeaders = map {$_=>1} (
105    "dos.h",
106    "process.h",
107    "winsock.h",
108    "config-win.h",
109    "mem.h",
110    "windows.h",
111    "winsock2.h",
112    "crtdbg.h",
113    "ws2tcpip.h"
114);
115
116my %ObsoleteHeaders = map {$_=>1} (
117    "iostream.h",
118    "fstream.h"
119);
120
121my %AlienHeaders = map {$_=>1} (
122 # Solaris
123    "thread.h",
124    "sys/atomic.h",
125 # HPUX
126    "sys/stream.h",
127 # Symbian
128    "AknDoc.h",
129 # Atari ST
130    "ext.h",
131    "tos.h",
132 # MS-DOS
133    "alloc.h",
134 # Sparc
135    "sys/atomic.h"
136);
137
138my %ConfHeaders = map {$_=>1} (
139    "atomic",
140    "conf.h",
141    "config.h",
142    "configure.h",
143    "build.h",
144    "setup.h"
145);
146
147my %LocalIncludes = map {$_=>1} (
148    "/usr/local/include",
149    "/usr/local" );
150
151my %OS_AddPath=(
152# These paths are needed if the tool cannot detect them automatically
153    "macos"=>{
154        "include"=>[
155            "/Library",
156            "/Developer/usr/include"
157        ],
158        "lib"=>[
159            "/Library",
160            "/Developer/usr/lib"
161        ],
162        "bin"=>[
163            "/Developer/usr/bin"
164        ]
165    },
166    "beos"=>{
167    # Haiku has GCC 2.95.3 by default
168    # try to find GCC>=3.0 in /boot/develop/abi
169        "include"=>[
170            "/boot/common",
171            "/boot/develop"
172        ],
173        "lib"=>[
174            "/boot/common/lib",
175            "/boot/system/lib",
176            "/boot/apps"
177        ],
178        "bin"=>[
179            "/boot/common/bin",
180            "/boot/system/bin",
181            "/boot/develop/abi"
182        ]
183    }
184);
185
186my %RegisteredDirs;
187my %Header_ErrorRedirect;
188my %HeaderName_Paths;
189my %Header_Dependency;
190my @DefaultCppPaths;
191my @DefaultGccPaths;
192my @DefaultIncPaths;
193my @DefaultBinPaths;
194my %SystemHeaders;
195my %DefaultCppHeader;
196my %DefaultGccHeader;
197my @UsersIncPath;
198my %Header_Includes;
199my %Header_Includes_R;
200my %Header_ShouldNotBeUsed;
201my %RecursiveIncludes;
202my %Header_Include_Prefix;
203my @RecurInclude;
204
205my %Include_Paths = (
206    "1"=>[],
207    "2"=>[]
208);
209
210my %Add_Include_Paths = (
211    "1"=>[],
212    "2"=>[]
213);
214
215sub tryCmd($)
216{
217    my $Cmd = $_[0];
218
219    my @Options = (
220        "--version",
221        "-help"
222    );
223    foreach my $Opt (@Options)
224    {
225        my $TmpDir = $In::Opt{"Tmp"};
226        my $Info = `$Cmd $Opt 2>\"$TmpDir/null\"`;
227        if($Info) {
228            return 1;
229        }
230    }
231    return 0;
232}
233
234sub searchTool($)
235{
236    my $Name = $_[0];
237
238    if(my @Paths = keys(%{$In::Opt{"TargetTools"}}))
239    {
240        foreach my $Path (@Paths)
241        {
242            if(-f join_P($Path, $Name)) {
243                return join_P($Path, $Name);
244            }
245            if(my $CrossPrefix = $In::Opt{"CrossPrefix"})
246            { # user-defined prefix (arm-none-symbianelf, ...)
247                my $Candidate = join_P($Path, $CrossPrefix."-".$Name);
248                if(-f $Candidate) {
249                    return $Candidate;
250                }
251            }
252        }
253    }
254
255    return undef;
256}
257
258sub syncWithGcc($)
259{
260    my $Name = $_[0];
261    if(my $GccPath = $In::Opt{"GccPath"})
262    {
263        if($GccPath=~s/\bgcc(|\.\w+)\Z/$Name$1/) {
264            return $GccPath;
265        }
266    }
267
268    return undef;
269}
270
271sub getCmdPath($)
272{
273    my $Name = $_[0];
274
275    if(defined $Cache{"getCmdPath"}{$Name}) {
276        return $Cache{"getCmdPath"}{$Name};
277    }
278
279    my $Path = searchTool($Name);
280    if(not $Path and $In::Opt{"OS"} eq "windows") {
281        $Path = searchTool($Name.".exe");
282    }
283
284    if(not $Path and $BinUtils{$Name})
285    {
286        if(my $CrossPrefix = $In::Opt{"CrossPrefix"}) {
287            $Path = searchCommand($CrossPrefix."-".$Name);
288        }
289    }
290
291    if(not $Path and $BinUtils{$Name})
292    {
293        if(my $Cand = syncWithGcc($Name))
294        { # sync with GCC
295            if($Cand=~/[\/\\]/)
296            { # path
297                if(-f $Cand) {
298                    $Path = $Cand;
299                }
300            }
301            elsif($Cand = searchCommand($Cand))
302            { # name
303                $Path = $Cand;
304            }
305        }
306    }
307    if(not $Path) {
308        $Path = searchCommand($Name);
309    }
310    if(not $Path and $In::Opt{"OS"} eq "windows")
311    { # search for *.exe file
312        $Path = searchCommand($Name.".exe");
313    }
314    if($Path=~/\s/) {
315        $Path = "\"".$Path."\"";
316    }
317    return ($Cache{"getCmdPath"}{$Name} = $Path);
318}
319
320sub searchCommand($)
321{
322    my $Name = $_[0];
323
324    if(defined $Cache{"searchCommand"}{$Name}) {
325        return $Cache{"searchCommand"}{$Name};
326    }
327    if(my $DefaultPath = getCmdPath_Default($Name)) {
328        return ($Cache{"searchCommand"}{$Name} = $DefaultPath);
329    }
330    foreach my $Path (@{$In::Opt{"SysPaths"}{"bin"}})
331    {
332        my $CmdPath = join_P($Path,$Name);
333        if(-f $CmdPath)
334        {
335            if($Name=~/gcc/) {
336                next if(not checkGcc("3", $CmdPath));
337            }
338            return ($Cache{"searchCommand"}{$Name} = $CmdPath);
339        }
340    }
341    return ($Cache{"searchCommand"}{$Name} = "");
342}
343
344sub getCmdPath_Default($)
345{ # search in PATH
346    if(defined $Cache{"getCmdPath_Default"}{$_[0]}) {
347        return $Cache{"getCmdPath_Default"}{$_[0]};
348    }
349    return ($Cache{"getCmdPath_Default"}{$_[0]} = getCmdPath_Default_I($_[0]));
350}
351
352sub getCmdPath_Default_I($)
353{ # search in PATH
354    my $Name = $_[0];
355
356    my $TmpDir = $In::Opt{"Tmp"};
357
358    if($Name=~/find/)
359    { # special case: search for "find" utility
360        if(`find \"$TmpDir\" -maxdepth 0 2>\"$TmpDir/null\"`) {
361            return "find";
362        }
363    }
364    elsif($Name=~/gcc/) {
365        return checkGcc("3", $Name);
366    }
367    if(tryCmd($Name)) {
368        return $Name;
369    }
370    if($In::Opt{"OS"} eq "windows")
371    {
372        if(`$Name /? 2>\"$TmpDir/null\"`) {
373            return $Name;
374        }
375    }
376    foreach my $Path (@DefaultBinPaths)
377    {
378        if(-f $Path."/".$Name) {
379            return join_P($Path, $Name);
380        }
381    }
382    return "";
383}
384
385sub checkSystemFiles()
386{
387    if($Cache{"checkSystemFiles"})
388    { # run once
389        return;
390    }
391    $Cache{"checkSystemFiles"} = 1;
392
393    my $LibExt = $In::Opt{"Ext"};
394    my @SysHeaders = ();
395
396    foreach my $DevelPath (@{$In::Opt{"SysPaths"}{"lib"}})
397    {
398        if(not -d $DevelPath) {
399            next;
400        }
401
402        my @Files = cmdFind($DevelPath,"f");
403        foreach my $Link (cmdFind($DevelPath,"l"))
404        { # add symbolic links
405            if(-f $Link) {
406                push(@Files, $Link);
407            }
408        }
409
410        # search for headers in /usr/lib
411        my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files;
412        @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers;
413        push(@SysHeaders, @Headers);
414
415        # search for libraries in /usr/lib (including symbolic links)
416        my @Libs = grep { /\.$LibExt[0-9.]*\Z/ } @Files;
417        foreach my $Path (@Libs)
418        {
419            my $N = getFilename($Path);
420            $In::Opt{"SystemObjects"}{$N}{$Path} = 1;
421            $In::Opt{"SystemObjects"}{libPart($N, "name+ext")}{$Path} = 1;
422        }
423    }
424
425    foreach my $DevelPath (@{$In::Opt{"SysPaths"}{"include"}})
426    {
427        if(not -d $DevelPath) {
428            next;
429        }
430        # search for all header files in the /usr/include
431        # with or without extension (ncurses.h, QtCore, ...)
432        push(@SysHeaders, cmdFind($DevelPath,"f"));
433        foreach my $Link (cmdFind($DevelPath,"l"))
434        { # add symbolic links
435            if(-f $Link) {
436                push(@SysHeaders, $Link);
437            }
438        }
439    }
440    getPrefixes_I(\@SysHeaders, \%SystemHeaders);
441}
442
443sub libPart($$)
444{
445    my ($N, $T) = @_;
446    if(defined $Cache{"libPart"}{$T}{$N}) {
447        return $Cache{"libPart"}{$T}{$N};
448    }
449    return ($Cache{"libPart"}{$T}{$N} = libPart_I(@_));
450}
451
452sub libPart_I($$)
453{
454    my ($N, $T) = @_;
455
456    my $Ext = $In::Opt{"Ext"};
457    my $Target = $In::Opt{"Target"};
458
459    if($Target eq "symbian")
460    {
461        if($N=~/(((.+?)(\{.+\}|))\.$Ext)\Z/)
462        { # libpthread{00010001}.dso
463            if($T eq "name")
464            { # libpthread{00010001}
465                return $2;
466            }
467            elsif($T eq "name+ext")
468            { # libpthread{00010001}.dso
469                return $1;
470            }
471            elsif($T eq "version")
472            { # 00010001
473                my $V = $4;
474                $V=~s/\{(.+)\}/$1/;
475                return $V;
476            }
477            elsif($T eq "short")
478            { # libpthread
479                return $3;
480            }
481            elsif($T eq "shortest")
482            { # pthread
483                return shortestName($3);
484            }
485        }
486    }
487    elsif($Target eq "windows")
488    {
489        if($N=~/((.+?)\.$Ext)\Z/)
490        { # netapi32.dll
491            if($T eq "name")
492            { # netapi32
493                return $2;
494            }
495            elsif($T eq "name+ext")
496            { # netapi32.dll
497                return $1;
498            }
499            elsif($T eq "version")
500            { # DLL version embedded
501              # at binary-level
502                return "";
503            }
504            elsif($T eq "short")
505            { # netapi32
506                return $2;
507            }
508            elsif($T eq "shortest")
509            { # netapi
510                return shortestName($2);
511            }
512        }
513    }
514    else
515    { # unix
516        if($N=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/)
517        { # libSDL-1.2.so.0.7.1
518          # libwbxml2.so.0.0.18
519          # libopcodes-2.21.53-system.20110810.so
520            if($T eq "name")
521            { # libSDL-1.2
522              # libwbxml2
523                return $2;
524            }
525            elsif($T eq "name+ext")
526            { # libSDL-1.2.so
527              # libwbxml2.so
528                return $1;
529            }
530            elsif($T eq "version")
531            {
532                if(defined $7
533                and $7 ne "")
534                { # 0.7.1
535                    return $7;
536                }
537                else
538                { # libc-2.5.so (=>2.5 version)
539                    my $MV = $5;
540                    $MV=~s/\A[\-\_]+//g;
541                    return $MV;
542                }
543            }
544            elsif($T eq "short")
545            { # libSDL
546              # libwbxml2
547                return $3;
548            }
549            elsif($T eq "shortest")
550            { # SDL
551              # wbxml
552                return shortestName($3);
553            }
554        }
555    }
556
557    # error
558    return "";
559}
560
561sub shortestName($)
562{
563    my $Name = $_[0];
564    # remove prefix
565    $Name=~s/\A(lib|open)//;
566    # remove suffix
567    $Name=~s/[\W\d_]+\Z//i;
568    $Name=~s/([a-z]{2,})(lib)\Z/$1/i;
569    return $Name;
570}
571
572sub detectDefaultPaths($$$$)
573{
574    my ($HSearch, $LSearch, $BSearch, $GSearch) = (@_);
575
576    if($Cache{"detectDefaultPaths"}{$HSearch}{$LSearch}{$BSearch}{$GSearch})
577    { # enter once
578        return;
579    }
580    $Cache{"detectDefaultPaths"}{$HSearch}{$LSearch}{$BSearch}{$GSearch} = 1;
581
582    if(@{$In::Opt{"SysPaths"}{"include"}})
583    { # <search_headers> section of the XML descriptor
584      # do NOT search for systems headers
585        $HSearch = undef;
586    }
587    if(@{$In::Opt{"SysPaths"}{"lib"}})
588    { # <search_libs> section of the XML descriptor
589      # do NOT search for systems libraries
590        $LSearch = undef;
591    }
592
593    foreach my $Type (keys(%{$OS_AddPath{$In::Opt{"OS"}}}))
594    { # additional search paths
595        next if($Type eq "include" and not $HSearch);
596        next if($Type eq "lib" and not $LSearch);
597        next if($Type eq "bin" and not $BSearch);
598
599        push_U($In::Opt{"SysPaths"}{$Type}, grep { -d $_ } @{$OS_AddPath{$In::Opt{"OS"}}{$Type}});
600    }
601    if($In::Opt{"OS"} ne "windows")
602    { # unix-like
603        foreach my $Type ("include", "lib", "bin")
604        { # automatic detection of system "devel" directories
605            next if($Type eq "include" and not $HSearch);
606            next if($Type eq "lib" and not $LSearch);
607            next if($Type eq "bin" and not $BSearch);
608
609            my ($UsrDir, $RootDir) = ("/usr", "/");
610
611            if(my $SystemRoot = $In::Opt{"SystemRoot"}
612            and $Type ne "bin")
613            { # 1. search for target headers and libraries
614              # 2. use host commands: ldconfig, readelf, etc.
615                ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot);
616            }
617
618            push_U($In::Opt{"SysPaths"}{$Type}, cmdFind($RootDir,"d","*$Type*",1));
619
620            if(-d $RootDir."/".$Type)
621            { # if "/lib" is symbolic link
622                if($RootDir eq "/") {
623                    push_U($In::Opt{"SysPaths"}{$Type}, "/".$Type);
624                }
625                else {
626                    push_U($In::Opt{"SysPaths"}{$Type}, $RootDir."/".$Type);
627                }
628            }
629
630            if(-d $UsrDir)
631            {
632                push_U($In::Opt{"SysPaths"}{$Type}, cmdFind($UsrDir,"d","*$Type*",1));
633                if(-d $UsrDir."/".$Type)
634                { # if "/usr/lib" is symbolic link
635                    push_U($In::Opt{"SysPaths"}{$Type}, $UsrDir."/".$Type);
636                }
637            }
638        }
639    }
640    if($BSearch)
641    {
642        detectBinDefaultPaths();
643        push_U($In::Opt{"SysPaths"}{"bin"}, @DefaultBinPaths);
644    }
645
646    # check environment variables
647    if($In::Opt{"OS"} eq "beos")
648    {
649        foreach (my @Paths = @{$In::Opt{"SysPaths"}{"bin"}})
650        {
651            if($_ eq ".") {
652                next;
653            }
654            # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/
655            if(my @Dirs = sort cmdFind($_, "d", "bin")) {
656                push_U($In::Opt{"SysPaths"}{"bin"}, sort {getDepth($a)<=>getDepth($b)} @Dirs);
657            }
658        }
659
660        if($HSearch)
661        {
662            push_U(\@DefaultIncPaths, grep { isAbsPath($_) } (
663                split(/:|;/, $ENV{"BEINCLUDES"})
664                ));
665        }
666
667        if($LSearch)
668        {
669            push_U($In::Opt{"DefaultLibPaths"}, grep { isAbsPath($_) } (
670                split(/:|;/, $ENV{"BELIBRARIES"}),
671                split(/:|;/, $ENV{"LIBRARY_PATH"})
672                ));
673        }
674    }
675    if($LSearch)
676    { # using linker to get system paths
677        if(my $LPaths = detectLibDefaultPaths())
678        { # unix-like
679            my %Dirs = ();
680            foreach my $Name (keys(%{$LPaths}))
681            {
682                if(my $SystemRoot = $In::Opt{"SystemRoot"})
683                {
684                    if($LPaths->{$Name}!~/\A\Q$SystemRoot\E\//)
685                    { # wrong ldconfig configuration
686                      # check your <sysroot>/etc/ld.so.conf
687                        next;
688                    }
689                }
690
691                $In::Opt{"LibDefaultPath"}{$Name} = $LPaths->{$Name};
692                if(my $Dir = getDirname($LPaths->{$Name})) {
693                    $Dirs{$Dir} = 1;
694                }
695            }
696            push_U($In::Opt{"DefaultLibPaths"}, sort {getDepth($a)<=>getDepth($b)} sort keys(%Dirs));
697        }
698        push_U($In::Opt{"SysPaths"}{"lib"}, @{$In::Opt{"DefaultLibPaths"}});
699
700        if(my $EDir = $In::Opt{"ExtraInfo"}) {
701            writeFile($EDir."/default-libs", join("\n", @{$In::Opt{"DefaultLibPaths"}}));
702        }
703    }
704
705    if($BSearch)
706    {
707        if($In::Opt{"CrossPrefix"})
708        {
709            if(my $GccPath = getGccPath())
710            {
711                $In::Opt{"GccPath"} = $GccPath;
712                if(my $D = getDirname($GccPath)) {
713                    $In::Opt{"TargetTools"}{$D}=1;
714                }
715            }
716        }
717    }
718
719    if($GSearch and my $GccPath = getGccPath())
720    { # GCC path and default include dirs
721        $In::Opt{"GccPath"} = $GccPath;
722
723        my $GccVer = dumpVersion($GccPath);
724
725        if($GccVer=~/\A\d+\.\d+\Z/)
726        { # on Ubuntu -dumpversion returns 4.8 for gcc 4.8.4
727            my $Info = `$GccPath --version`;
728
729            if($Info=~/gcc\s+(|\([^()]+\)\s+)(\d+\.\d+\.\d+)/)
730            { # gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
731              # gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)
732                $GccVer = $2;
733            }
734        }
735
736        if($In::Opt{"OS"}=~/macos/)
737        {
738            my $Info = `$GccPath --version`;
739
740            if($Info=~/clang/i) {
741                printMsg("WARNING", "doesn't work with clang, please install GCC instead (and select it by -gcc-path option)");
742            }
743        }
744
745        if($GccVer)
746        {
747            my $Target = dumpMachine($GccPath);
748
749            if($Target=~/linux/) {
750                setTarget("linux");
751            }
752            elsif($Target=~/symbian/) {
753                setTarget("symbian");
754            }
755            elsif($Target=~/solaris/) {
756                setTarget("solaris");
757            }
758
759            $In::Opt{"GccTarget"} = $Target;
760            $In::Opt{"GccVer"} = $GccVer;
761
762            printMsg("INFO", "Using GCC $GccVer ($Target, target: ".getArch_GCC(1).")");
763
764            # check GCC version
765            if($GccVer=~/\A(4\.8(|\.[012])|[67](\..*)?)\Z/ or cmpVersions($GccVer, "8")>=0)
766            { # GCC 4.8.[0-2]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850
767              # GCC 6.[1-2].0: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78040
768              # GCC 7.1: still the same issue ...
769              # GCC 8: still the same issue ...
770              # ABICC 2.3: enable this for all future GCC versions
771                printMsg("WARNING", "May not work properly with GCC 4.8.[0-2], 6.* and higher due to bug #78040 in GCC. Please try other GCC versions with the help of --gcc-path=PATH option or create ABI dumps by ABI Dumper tool instead to avoid using GCC. Test selected GCC version first by -test option.");
772                $In::Opt{"GccMissedMangling"} = 1;
773            }
774        }
775        else {
776            exitStatus("Error", "something is going wrong with the GCC compiler");
777        }
778    }
779
780    if($HSearch)
781    {
782        # GCC standard paths
783        if($In::Opt{"GccPath"}
784        and not $In::Opt{"NoStdInc"})
785        {
786            my %DPaths = detectIncDefaultPaths();
787            @DefaultCppPaths = @{$DPaths{"Cpp"}};
788            @DefaultGccPaths = @{$DPaths{"Gcc"}};
789            @DefaultIncPaths = @{$DPaths{"Inc"}};
790            push_U($In::Opt{"SysPaths"}{"include"}, @DefaultIncPaths);
791        }
792
793        # users include paths
794        my $IncPath = "/usr/include";
795        if(my $SystemRoot = $In::Opt{"SystemRoot"}) {
796            $IncPath = $SystemRoot.$IncPath;
797        }
798        if(-d $IncPath) {
799            push_U(\@UsersIncPath, $IncPath);
800        }
801
802        if(my $EDir = $In::Opt{"ExtraInfo"}) {
803            writeFile($EDir."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths)));
804        }
805    }
806
807
808}
809
810sub detectLibDefaultPaths()
811{
812    my %LPaths = ();
813
814    my $TmpDir = $In::Opt{"Tmp"};
815
816    if($In::Opt{"OS"} eq "bsd")
817    {
818        if(my $LdConfig = getCmdPath("ldconfig"))
819        {
820            foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TmpDir/null\"`))
821            {
822                if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/)
823                {
824                    my $Name = "lib".$1;
825                    if(not defined $LPaths{$Name}) {
826                        $LPaths{$Name} = $2;
827                    }
828                }
829            }
830        }
831        else {
832            printMsg("WARNING", "can't find ldconfig");
833        }
834    }
835    else
836    {
837        if(my $LdConfig = getCmdPath("ldconfig"))
838        {
839            if(my $SystemRoot = $In::Opt{"SystemRoot"}
840            and $In::Opt{"OS"} eq "linux")
841            { # use host (x86) ldconfig with the target (arm) ld.so.conf
842                if(-e $SystemRoot."/etc/ld.so.conf") {
843                    $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf";
844                }
845            }
846            foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TmpDir/null\"`))
847            {
848                if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/)
849                {
850                    my ($Name, $Path) = ($1, $2);
851                    $Path=~s/[\/]{2,}/\//;
852                    if(not defined $LPaths{$Name})
853                    { # get first element from the list of available paths
854
855                      # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
856                      # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6
857                      # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6
858
859                        $LPaths{$Name} = $Path;
860                    }
861                }
862            }
863        }
864        elsif($In::Opt{"OS"} eq "linux") {
865            printMsg("WARNING", "can't find ldconfig");
866        }
867    }
868    return \%LPaths;
869}
870
871sub detectBinDefaultPaths()
872{
873    my $EnvPaths = $ENV{"PATH"};
874    if($In::Opt{"OS"} eq "beos") {
875        $EnvPaths.=":".$ENV{"BETOOLS"};
876    }
877    my $Sep = ":|;";
878    if($In::Opt{"OS"} eq "windows") {
879        $Sep = ";";
880    }
881
882    foreach my $Path (split(/$Sep/, $EnvPaths))
883    {
884        $Path = pathFmt($Path);
885        if(not $Path) {
886            next;
887        }
888        if(my $SystemRoot = $In::Opt{"SystemRoot"})
889        {
890            if($Path=~/\A\Q$SystemRoot\E\//)
891            { # do NOT use binaries from target system
892                next;
893            }
894        }
895        push_U(\@DefaultBinPaths, $Path);
896    }
897}
898
899sub detectIncDefaultPaths()
900{
901    my $GccPath = $In::Opt{"GccPath"};
902    my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]);
903
904    my $TmpDir = $In::Opt{"Tmp"};
905    writeFile("$TmpDir/empty.h", "");
906
907    foreach my $Line (split(/\n/, `$GccPath -v -x c++ -E \"$TmpDir/empty.h\" 2>&1`))
908    { # detecting GCC default include paths
909        if(index($Line, "/cc1plus ")!=-1) {
910            next;
911        }
912
913        if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/)
914        {
915            my $Path = realpath_F($1);
916            if(index($Path, "c++")!=-1
917            or index($Path, "/g++/")!=-1)
918            {
919                push_U($DPaths{"Cpp"}, $Path);
920                if(not defined $In::Opt{"MainCppDir"}
921                or getDepth($In::Opt{"MainCppDir"})>getDepth($Path)) {
922                    $In::Opt{"MainCppDir"} = $Path;
923                }
924            }
925            elsif(index($Path, "gcc")!=-1) {
926                push_U($DPaths{"Gcc"}, $Path);
927            }
928            else
929            {
930                if($Path=~/local[\/\\]+include/)
931                { # local paths
932                    next;
933                }
934                if(my $SystemRoot = $In::Opt{"SystemRoot"})
935                {
936                    if($Path!~/\A\Q$SystemRoot\E(\/|\Z)/)
937                    { # The GCC include path for user headers is not a part of the system root
938                      # The reason: you are not specified the --cross-gcc option or selected a wrong compiler
939                      # or it is the internal cross-GCC path like arm-linux-gnueabi/include
940                        next;
941                    }
942                }
943                push_U($DPaths{"Inc"}, $Path);
944            }
945        }
946    }
947    unlink("$TmpDir/empty.h");
948    return %DPaths;
949}
950
951sub registerGccHeaders()
952{
953    if($Cache{"registerGccHeaders"})
954    { # this function should be called once
955        return;
956    }
957
958    foreach my $Path (@DefaultGccPaths)
959    {
960        my @Headers = cmdFind($Path,"f");
961        @Headers = sort {getDepth($a)<=>getDepth($b)} @Headers;
962        foreach my $HPath (@Headers)
963        {
964            my $FileName = getFilename($HPath);
965            if(not defined $DefaultGccHeader{$FileName})
966            { # skip duplicated
967                $DefaultGccHeader{$FileName} = $HPath;
968            }
969        }
970    }
971    $Cache{"registerGccHeaders"} = 1;
972}
973
974sub registerCppHeaders()
975{
976    if($Cache{"registerCppHeaders"})
977    { # this function should be called once
978        return;
979    }
980
981    foreach my $CppDir (@DefaultCppPaths)
982    {
983        my @Headers = cmdFind($CppDir,"f");
984        @Headers = sort {getDepth($a)<=>getDepth($b)} @Headers;
985        foreach my $Path (@Headers)
986        {
987            my $FileName = getFilename($Path);
988            if(not defined $DefaultCppHeader{$FileName})
989            { # skip duplicated
990                $DefaultCppHeader{$FileName} = $Path;
991            }
992        }
993    }
994    $Cache{"registerCppHeaders"} = 1;
995}
996
997sub parseRedirect($$$)
998{
999    my ($Content, $Path, $LVer) = @_;
1000    my @Errors = ();
1001    while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) {
1002        push(@Errors, $1);
1003    }
1004    my $Redirect = "";
1005    foreach (@Errors)
1006    {
1007        s/\s{2,}/ /g;
1008        if(/(only|must\ include
1009        |update\ to\ include
1010        |replaced\ with
1011        |replaced\ by|renamed\ to
1012        |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix)
1013        {
1014            $Redirect = $2;
1015            last;
1016        }
1017        elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i)
1018        {
1019            $Redirect = $2;
1020            last;
1021        }
1022        elsif(/this\ header\ should\ not\ be\ used
1023         |programs\ should\ not\ directly\ include
1024         |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header))
1025         |is\ not\ supported\ API\ for\ general\ use
1026         |do\ not\ use
1027         |should\ not\ be\ (used|using)
1028         |cannot\ be\ included\ directly/ix and not /\ from\ /i) {
1029            $Header_ShouldNotBeUsed{$LVer}{$Path} = 1;
1030        }
1031    }
1032    if($Redirect)
1033    {
1034        $Redirect=~s/\A<//g;
1035        $Redirect=~s/>\Z//g;
1036    }
1037    return $Redirect;
1038}
1039
1040sub parseIncludes($$)
1041{
1042    my ($Content, $Path) = @_;
1043    my %Includes = ();
1044    while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m)
1045    { # C/C++: include, Objective C/C++: import directive
1046        my $Header = $2;
1047        my $Method = substr($Header, 0, 1, "");
1048        substr($Header, length($Header)-1, 1, "");
1049        $Header = pathFmt($Header);
1050        if($Method eq "\"" or isAbsPath($Header))
1051        {
1052            if(-e join_P(getDirname($Path), $Header))
1053            { # relative path exists
1054                $Includes{$Header} = -1;
1055            }
1056            else
1057            { # include "..." that doesn't exist is equal to include <...>
1058                $Includes{$Header} = 2;
1059            }
1060        }
1061        else {
1062            $Includes{$Header} = 1;
1063        }
1064    }
1065    if($In::Opt{"ExtraInfo"})
1066    {
1067        while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m)
1068        { # FT_FREETYPE_H
1069            $Includes{$2} = 0;
1070        }
1071    }
1072    return \%Includes;
1073}
1074
1075sub sortHeaders($$)
1076{
1077    my ($H1, $H2) = @_;
1078
1079    $H1=~s/\.[a-z]+\Z//ig;
1080    $H2=~s/\.[a-z]+\Z//ig;
1081
1082    my $Hname1 = getFilename($H1);
1083    my $Hname2 = getFilename($H2);
1084    my $HDir1 = getDirname($H1);
1085    my $HDir2 = getDirname($H2);
1086    my $Dirname1 = getFilename($HDir1);
1087    my $Dirname2 = getFilename($HDir2);
1088
1089    $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
1090    $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
1091
1092    if($_[0] eq $_[1]
1093    or $H1 eq $H2) {
1094        return 0;
1095    }
1096    elsif($H1=~/\A\Q$H2\E/) {
1097        return 1;
1098    }
1099    elsif($H2=~/\A\Q$H1\E/) {
1100        return -1;
1101    }
1102    elsif($HDir1=~/\Q$Hname1\E/i
1103    and $HDir2!~/\Q$Hname2\E/i)
1104    { # include/glib-2.0/glib.h
1105        return -1;
1106    }
1107    elsif($HDir2=~/\Q$Hname2\E/i
1108    and $HDir1!~/\Q$Hname1\E/i)
1109    { # include/glib-2.0/glib.h
1110        return 1;
1111    }
1112    elsif($Hname1=~/\Q$Dirname1\E/i
1113    and $Hname2!~/\Q$Dirname2\E/i)
1114    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
1115        return -1;
1116    }
1117    elsif($Hname2=~/\Q$Dirname2\E/i
1118    and $Hname1!~/\Q$Dirname1\E/i)
1119    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
1120        return 1;
1121    }
1122    elsif($Hname1=~/(config|lib|util)/i
1123    and $Hname2!~/(config|lib|util)/i)
1124    { # include/alsa/asoundlib.h
1125        return -1;
1126    }
1127    elsif($Hname2=~/(config|lib|util)/i
1128    and $Hname1!~/(config|lib|util)/i)
1129    { # include/alsa/asoundlib.h
1130        return 1;
1131    }
1132    else
1133    {
1134        my $R1 = checkRelevance($H1);
1135        my $R2 = checkRelevance($H2);
1136        if($R1 and not $R2)
1137        { # libebook/e-book.h
1138            return -1;
1139        }
1140        elsif($R2 and not $R1)
1141        { # libebook/e-book.h
1142            return 1;
1143        }
1144        else
1145        {
1146            return (lc($H1) cmp lc($H2));
1147        }
1148    }
1149}
1150
1151sub detectRealIncludes($$)
1152{
1153    my ($AbsPath, $LVer) = @_;
1154
1155    if($Cache{"detectRealIncludes"}{$LVer}{$AbsPath}
1156    or keys(%{$RecursiveIncludes{$LVer}{$AbsPath}})) {
1157        return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}});
1158    }
1159    $Cache{"detectRealIncludes"}{$LVer}{$AbsPath}=1;
1160
1161    my $Path = callPreprocessor($AbsPath, "", $LVer);
1162    if(not $Path) {
1163        return ();
1164    }
1165    open(PREPROC, $Path);
1166    while(<PREPROC>)
1167    {
1168        if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/)
1169        {
1170            my $Include = pathFmt($1);
1171            if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) {
1172                next;
1173            }
1174            if($Include eq $AbsPath) {
1175                next;
1176            }
1177            $RecursiveIncludes{$LVer}{$AbsPath}{$Include} = 1;
1178        }
1179    }
1180    close(PREPROC);
1181    return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}});
1182}
1183
1184sub detectHeaderIncludes($$)
1185{
1186    my ($Path, $LVer) = @_;
1187
1188    if(defined $Cache{"detectHeaderIncludes"}{$LVer}{$Path}) {
1189        return;
1190    }
1191    $Cache{"detectHeaderIncludes"}{$LVer}{$Path}=1;
1192
1193    if(not -e $Path) {
1194        return;
1195    }
1196
1197    my $Content = readFile($Path);
1198    if(my $Redirect = parseRedirect($Content, $Path, $LVer))
1199    { # detect error directive in headers
1200        if(my $RedirectPath = identifyHeader($Redirect, $LVer))
1201        {
1202            if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) {
1203                $RedirectPath = identifyHeader(getFilename($Redirect), $LVer);
1204            }
1205            if($RedirectPath ne $Path) {
1206                $Header_ErrorRedirect{$LVer}{$Path} = $RedirectPath;
1207            }
1208        }
1209        else
1210        { # can't find
1211            $Header_ShouldNotBeUsed{$LVer}{$Path} = 1;
1212        }
1213    }
1214    if(my $Inc = parseIncludes($Content, $Path))
1215    {
1216        foreach my $Include (keys(%{$Inc}))
1217        { # detect includes
1218            $Header_Includes{$LVer}{$Path}{$Include} = $Inc->{$Include};
1219
1220            if(defined $In::Opt{"Tolerance"}
1221            and $In::Opt{"Tolerance"}=~/4/)
1222            {
1223                if(my $HPath = identifyHeader($Include, $LVer))
1224                {
1225                    $Header_Includes_R{$LVer}{$HPath}{$Path} = 1;
1226                }
1227            }
1228        }
1229    }
1230}
1231
1232sub fromLibc($)
1233{ # system GLIBC header
1234    my $Path = $_[0];
1235    my ($Dir, $Name) = sepPath($Path);
1236    if($In::Opt{"Target"} eq "symbian")
1237    {
1238        if(getFilename($Dir) eq "libc" and $GlibcHeader{$Name})
1239        { # epoc32/include/libc/{stdio, ...}.h
1240            return 1;
1241        }
1242    }
1243    else
1244    {
1245        if($Dir eq "/usr/include" and $GlibcHeader{$Name})
1246        { # /usr/include/{stdio, ...}.h
1247            return 1;
1248        }
1249    }
1250    return 0;
1251}
1252
1253sub isLibcDir($)
1254{ # system GLIBC directory
1255    my $Dir = $_[0];
1256    my ($OutDir, $Name) = sepPath($Dir);
1257    if($In::Opt{"Target"} eq "symbian")
1258    {
1259        if(getFilename($OutDir) eq "libc"
1260        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
1261        { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h
1262            return 1;
1263        }
1264    }
1265    else
1266    { # linux
1267        if($OutDir eq "/usr/include"
1268        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
1269        { # /usr/include/{sys,bits,asm,asm-*}/*.h
1270            return 1;
1271        }
1272    }
1273    return 0;
1274}
1275
1276sub detectRecursiveIncludes($$)
1277{
1278    my ($AbsPath, $LVer) = @_;
1279    if(not $AbsPath) {
1280        return ();
1281    }
1282    if(isCyclical(\@RecurInclude, $AbsPath)) {
1283        return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}});
1284    }
1285    my ($AbsDir, $Name) = sepPath($AbsPath);
1286    if(isLibcDir($AbsDir))
1287    { # system GLIBC internals
1288        if(not $In::Opt{"ExtraInfo"}) {
1289            return ();
1290        }
1291    }
1292    if(keys(%{$RecursiveIncludes{$LVer}{$AbsPath}})) {
1293        return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}});
1294    }
1295    if($In::Opt{"OS"} ne "windows"
1296    and $Name=~/windows|win32|win64/i) {
1297        return ();
1298    }
1299
1300    if($In::Opt{"MainCppDir"} and $AbsPath=~/\A\Q$In::Opt{"MainCppDir"}\E/ and not $In::Opt{"StdcxxTesting"})
1301    { # skip /usr/include/c++/*/ headers
1302        if(not $In::Opt{"ExtraInfo"}) {
1303            return ();
1304        }
1305    }
1306
1307    push(@RecurInclude, $AbsPath);
1308    if(grep { $AbsDir eq $_ } @DefaultGccPaths
1309    or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath)))
1310    { # check "real" (non-"model") include paths
1311        my @Paths = detectRealIncludes($AbsPath, $LVer);
1312        pop(@RecurInclude);
1313        return @Paths;
1314    }
1315    if(not keys(%{$Header_Includes{$LVer}{$AbsPath}})) {
1316        detectHeaderIncludes($AbsPath, $LVer);
1317    }
1318    foreach my $Include (keys(%{$Header_Includes{$LVer}{$AbsPath}}))
1319    {
1320        my $IncType = $Header_Includes{$LVer}{$AbsPath}{$Include};
1321        my $HPath = "";
1322        if($IncType<0)
1323        { # for #include "..."
1324            my $Candidate = join_P($AbsDir, $Include);
1325            if(-f $Candidate) {
1326                $HPath = realpath_F($Candidate);
1327            }
1328        }
1329        elsif($IncType>0
1330        and $Include=~/[\/\\]/) # and not findInDefaults($Include)
1331        { # search for the nearest header
1332          # QtCore/qabstractanimation.h includes <QtCore/qobject.h>
1333            my $Candidate = join_P(getDirname($AbsDir), $Include);
1334            if(-f $Candidate) {
1335                $HPath = $Candidate;
1336            }
1337        }
1338        if(not $HPath) {
1339            $HPath = identifyHeader($Include, $LVer);
1340        }
1341        next if(not $HPath);
1342        if($HPath eq $AbsPath) {
1343            next;
1344        }
1345
1346        #if($In::Opt{"Debug"})
1347        #{ # boundary headers
1348        #    if($HPath=~/vtk/ and $AbsPath!~/vtk/)
1349        #    {
1350        #        print STDERR "$AbsPath -> $HPath\n";
1351        #    }
1352        #}
1353
1354        $RecursiveIncludes{$LVer}{$AbsPath}{$HPath} = $IncType;
1355        if($IncType>0)
1356        { # only include <...>, skip include "..." prefixes
1357            $Header_Include_Prefix{$LVer}{$AbsPath}{$HPath}{getDirname($Include)} = 1;
1358        }
1359        foreach my $IncPath (detectRecursiveIncludes($HPath, $LVer))
1360        {
1361            if($IncPath eq $AbsPath) {
1362                next;
1363            }
1364            my $RIncType = $RecursiveIncludes{$LVer}{$HPath}{$IncPath};
1365            if($RIncType==-1)
1366            { # include "..."
1367                $RIncType = $IncType;
1368            }
1369            elsif($RIncType==2)
1370            {
1371                if($IncType!=-1) {
1372                    $RIncType = $IncType;
1373                }
1374            }
1375            $RecursiveIncludes{$LVer}{$AbsPath}{$IncPath} = $RIncType;
1376            foreach my $Prefix (keys(%{$Header_Include_Prefix{$LVer}{$HPath}{$IncPath}})) {
1377                $Header_Include_Prefix{$LVer}{$AbsPath}{$IncPath}{$Prefix} = 1;
1378            }
1379        }
1380        foreach my $Dep (keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}}))
1381        {
1382            if($GlibcHeader{getFilename($Dep)} and keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}})>=2
1383            and defined $Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}{""})
1384            { # distinguish math.h from glibc and math.h from the tested library
1385                delete($Header_Include_Prefix{$LVer}{$AbsPath}{$Dep}{""});
1386                last;
1387            }
1388        }
1389    }
1390    pop(@RecurInclude);
1391    return keys(%{$RecursiveIncludes{$LVer}{$AbsPath}});
1392}
1393
1394sub findInFramework($$$)
1395{
1396    my ($Header, $Framework, $LVer) = @_;
1397
1398    if(defined $Cache{"findInFramework"}{$LVer}{$Framework}{$Header}) {
1399        return $Cache{"findInFramework"}{$LVer}{$Framework}{$Header};
1400    }
1401    foreach my $Dependency (sort {getDepth($a)<=>getDepth($b)} keys(%{$Header_Dependency{$LVer}}))
1402    {
1403        if(getFilename($Dependency) eq $Framework
1404        and -f getDirname($Dependency)."/".$Header) {
1405            return ($Cache{"findInFramework"}{$LVer}{$Framework}{$Header} = getDirname($Dependency));
1406        }
1407    }
1408    return ($Cache{"findInFramework"}{$LVer}{$Framework}{$Header} = "");
1409}
1410
1411sub findInDefaults($)
1412{
1413    my $Header = $_[0];
1414
1415    if(defined $Cache{"findInDefaults"}{$Header}) {
1416        return $Cache{"findInDefaults"}{$Header};
1417    }
1418    foreach my $Dir (@DefaultIncPaths,
1419                     @DefaultGccPaths,
1420                     @DefaultCppPaths,
1421                     @UsersIncPath)
1422    {
1423        if(not $Dir) {
1424            next;
1425        }
1426        if(-f $Dir."/".$Header) {
1427            return ($Cache{"findInDefaults"}{$Header}=$Dir);
1428        }
1429    }
1430    return ($Cache{"findInDefaults"}{$Header} = "");
1431}
1432
1433sub cmp_paths($$)
1434{
1435    my ($Path1, $Path2) = @_;
1436    my @Parts1 = split(/[\/\\]/, $Path1);
1437    my @Parts2 = split(/[\/\\]/, $Path2);
1438    foreach my $Num (0 .. $#Parts1)
1439    {
1440        my $Part1 = $Parts1[$Num];
1441        my $Part2 = $Parts2[$Num];
1442        if($GlibcDir{$Part1}
1443        and not $GlibcDir{$Part2}) {
1444            return 1;
1445        }
1446        elsif($GlibcDir{$Part2}
1447        and not $GlibcDir{$Part1}) {
1448            return -1;
1449        }
1450        elsif($Part1=~/glib/
1451        and $Part2!~/glib/) {
1452            return 1;
1453        }
1454        elsif($Part1!~/glib/
1455        and $Part2=~/glib/) {
1456            return -1;
1457        }
1458        elsif(my $CmpRes = ($Part1 cmp $Part2)) {
1459            return $CmpRes;
1460        }
1461    }
1462    return 0;
1463}
1464
1465sub checkRelevance($)
1466{
1467    my $Path = $_[0];
1468
1469    if(my $SystemRoot = $In::Opt{"SystemRoot"}) {
1470        $Path = cutPrefix($Path, $SystemRoot);
1471    }
1472
1473    my $Name = lc(getFilename($Path));
1474    my $Dir = lc(getDirname($Path));
1475
1476    $Name=~s/\.\w+\Z//g; # remove extension (.h)
1477
1478    foreach my $Token (split(/[_\d\W]+/, $Name))
1479    {
1480        my $Len = length($Token);
1481        next if($Len<=1);
1482        if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/)
1483        { # include/evolution-data-server-1.4/libebook/e-book.h
1484            return 1;
1485        }
1486        if($Len>=4 and index($Dir, $Token)!=-1)
1487        { # include/gupnp-1.0/libgupnp/gupnp-context.h
1488            return 1;
1489        }
1490    }
1491    return 0;
1492}
1493
1494sub checkFamily(@)
1495{
1496    my @Paths = @_;
1497    if($#Paths<=0) {
1498        return 1;
1499    }
1500    my %Prefix = ();
1501    foreach my $Path (@Paths)
1502    {
1503        if(my $SystemRoot = $In::Opt{"SystemRoot"}) {
1504            $Path = cutPrefix($Path, $SystemRoot);
1505        }
1506        if(my $Dir = getDirname($Path))
1507        {
1508            $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix
1509            $Prefix{$Dir} += 1;
1510            $Prefix{getDirname($Dir)} += 1;
1511        }
1512    }
1513    foreach (sort keys(%Prefix))
1514    {
1515        if(getDepth($_)>=3
1516        and $Prefix{$_}==$#Paths+1) {
1517            return 1;
1518        }
1519    }
1520    return 0;
1521}
1522
1523sub isAcceptable($$$)
1524{
1525    my ($Header, $Candidate, $LVer) = @_;
1526    my $HName = getFilename($Header);
1527    if(getDirname($Header))
1528    { # with prefix
1529        return 1;
1530    }
1531    if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/)
1532    { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/
1533        return 1;
1534    }
1535    if(checkRelevance($Candidate))
1536    { # allow to search for atk.h in /usr/include/atk-1.0/atk/
1537        return 1;
1538    }
1539    if(checkFamily(getSystemHeaders($HName, $LVer)))
1540    { # /usr/include/qt4/QtNetwork/qsslconfiguration.h
1541      # /usr/include/qt4/Qt/qsslconfiguration.h
1542        return 1;
1543    }
1544    if($In::Opt{"Target"} eq "symbian")
1545    {
1546        if($Candidate=~/[\/\\]stdapis[\/\\]/) {
1547            return 1;
1548        }
1549    }
1550    return 0;
1551}
1552
1553sub isRelevant($$$)
1554{ # disallow to search for "abstract" headers in too deep directories
1555    my ($Header, $Candidate, $LVer) = @_;
1556    my $HName = getFilename($Header);
1557    if($In::Opt{"Target"} eq "symbian")
1558    {
1559        if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) {
1560            return 0;
1561        }
1562    }
1563    if($In::Opt{"Target"} ne "bsd")
1564    {
1565        if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/)
1566        { # openssh: skip /usr/lib/bcc/include/bsd/signal.h
1567            return 0;
1568        }
1569    }
1570    if($In::Opt{"Target"} ne "windows")
1571    {
1572        if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/)
1573        { # skip /usr/include/wine/msvcrt
1574            return 0;
1575        }
1576    }
1577    if(not getDirname($Header)
1578    and $Candidate=~/[\/\\]wx[\/\\]/)
1579    { # do NOT search in system /wx/ directory
1580      # for headers without a prefix: sstream.h
1581        return 0;
1582    }
1583    if($Candidate=~/c\+\+[\/\\]\d+/ and $In::Opt{"MainCppDir"}
1584    and $Candidate!~/\A\Q$In::Opt{"MainCppDir"}\E/)
1585    { # skip ../c++/3.3.3/ if using ../c++/4.5/
1586        return 0;
1587    }
1588    if($Candidate=~/[\/\\]asm-/
1589    and (my $Arch = getArch_GCC($LVer)) ne "unknown")
1590    { # arch-specific header files
1591        if($Candidate!~/[\/\\]asm-\Q$Arch\E/)
1592        {# skip ../asm-arm/ if using x86 architecture
1593            return 0;
1594        }
1595    }
1596    my @Candidates = getSystemHeaders($HName, $LVer);
1597    if($#Candidates==1)
1598    { # unique header
1599        return 1;
1600    }
1601    my @SCandidates = getSystemHeaders($Header, $LVer);
1602    if($#SCandidates==1)
1603    { # unique name
1604        return 1;
1605    }
1606    my $SystemDepth = 0;
1607
1608    if(my $SystemRoot = $In::Opt{"SystemRoot"}) {
1609        $SystemDepth = getDepth($SystemRoot);
1610    }
1611
1612    if(getDepth($Candidate)-$SystemDepth>=5)
1613    { # abstract headers in too deep directories
1614      # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/
1615        if(not isAcceptable($Header, $Candidate, $LVer)) {
1616            return 0;
1617        }
1618    }
1619    if($Header eq "parser.h"
1620    and $Candidate!~/\/libxml2\//)
1621    { # select parser.h from xml2 library
1622        return 0;
1623    }
1624    if(not getDirname($Header)
1625    and keys(%{$SystemHeaders{$HName}})>=3)
1626    { # many headers with the same name
1627      # like thread.h included without a prefix
1628        if(not checkFamily(@Candidates)) {
1629            return 0;
1630        }
1631    }
1632    return 1;
1633}
1634
1635sub selectSystemHeader($$)
1636{ # cache function
1637    if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) {
1638        return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]};
1639    }
1640    return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_));
1641}
1642
1643sub selectSystemHeader_I($$)
1644{
1645    my ($Header, $LVer) = @_;
1646    if(-f $Header) {
1647        return $Header;
1648    }
1649    if(isAbsPath($Header) and not -f $Header)
1650    { # incorrect absolute path
1651        return "";
1652    }
1653    if(defined $ConfHeaders{lc($Header)})
1654    { # too abstract configuration headers
1655        return "";
1656    }
1657    my $HName = getFilename($Header);
1658    if($In::Opt{"OS"} ne "windows")
1659    {
1660        if(defined $WinHeaders{lc($HName)}
1661        or $HName=~/windows|win32|win64/i)
1662        { # windows headers
1663            return "";
1664        }
1665    }
1666    if($In::Opt{"OS"} ne "macos")
1667    {
1668        if($HName eq "fp.h")
1669        { # pngconf.h includes fp.h in Mac OS
1670            return "";
1671        }
1672    }
1673
1674    if(defined $ObsoleteHeaders{$HName})
1675    { # obsolete headers
1676        return "";
1677    }
1678    if($In::Opt{"OS"} eq "linux"
1679    or $In::Opt{"OS"} eq "bsd")
1680    {
1681        if(defined $AlienHeaders{$HName}
1682        or defined $AlienHeaders{$Header})
1683        { # alien headers from other systems
1684            return "";
1685        }
1686    }
1687
1688    foreach my $Path (@{$In::Opt{"SysPaths"}{"include"}})
1689    { # search in default paths
1690        if(-f $Path."/".$Header) {
1691            return join_P($Path,$Header);
1692        }
1693    }
1694
1695    # register all headers in system include dirs
1696    checkSystemFiles();
1697
1698    foreach my $Candidate (sort {getDepth($a)<=>getDepth($b)}
1699    sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LVer))
1700    {
1701        if(isRelevant($Header, $Candidate, $LVer)) {
1702            return $Candidate;
1703        }
1704    }
1705    # error
1706    return "";
1707}
1708
1709sub getSystemHeaders($$)
1710{
1711    my ($Header, $LVer) = @_;
1712    my @Candidates = ();
1713    foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}}))
1714    {
1715        if(skipHeader($Candidate, $LVer)) {
1716            next;
1717        }
1718        push(@Candidates, $Candidate);
1719    }
1720    return @Candidates;
1721}
1722
1723sub isDefaultIncludeDir($)
1724{
1725    my $Dir = $_[0];
1726    $Dir=~s/[\/\\]+\Z//;
1727    return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths);
1728}
1729
1730sub identifyHeader($$)
1731{ # cache function
1732    my ($Header, $LVer) = @_;
1733    if(not $Header) {
1734        return "";
1735    }
1736    $Header=~s/\A(\.\.[\\\/])+//g;
1737    if(defined $Cache{"identifyHeader"}{$LVer}{$Header}) {
1738        return $Cache{"identifyHeader"}{$LVer}{$Header};
1739    }
1740    return ($Cache{"identifyHeader"}{$LVer}{$Header} = identifyHeader_I($Header, $LVer));
1741}
1742
1743sub identifyHeader_I($$)
1744{ # search for header by absolute path, relative path or name
1745    my ($Header, $LVer) = @_;
1746    if(-f $Header)
1747    { # it's relative or absolute path
1748        return getAbsPath($Header);
1749    }
1750    elsif($GlibcHeader{$Header} and not $In::Opt{"GlibcTesting"}
1751    and my $HeaderDir = findInDefaults($Header))
1752    { # search for libc headers in the /usr/include
1753      # for non-libc target library before searching
1754      # in the library paths
1755        return join_P($HeaderDir,$Header);
1756    }
1757    elsif(my $Path = $In::Desc{$LVer}{"IncludeNeighbors"}{$Header})
1758    { # search in the target library paths
1759        return $Path;
1760    }
1761    elsif(defined $DefaultGccHeader{$Header})
1762    { # search in the internal GCC include paths
1763        return $DefaultGccHeader{$Header};
1764    }
1765    elsif(my $DefaultDir = findInDefaults($Header))
1766    { # search in the default GCC include paths
1767        return join_P($DefaultDir,$Header);
1768    }
1769    elsif(defined $DefaultCppHeader{$Header})
1770    { # search in the default G++ include paths
1771        return $DefaultCppHeader{$Header};
1772    }
1773    elsif(my $AnyPath = selectSystemHeader($Header, $LVer))
1774    { # search everywhere in the system
1775        return $AnyPath;
1776    }
1777    elsif($In::Opt{"OS"} eq "macos")
1778    { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h"
1779        if(my $Dir = getDirname($Header))
1780        {
1781            my $RelPath = "Headers\/".getFilename($Header);
1782            if(my $HeaderDir = findInFramework($RelPath, $Dir.".framework", $LVer)) {
1783                return join_P($HeaderDir, $RelPath);
1784            }
1785        }
1786    }
1787    # cannot find anything
1788    return "";
1789}
1790
1791sub cmdFile($)
1792{
1793    my $Path = $_[0];
1794
1795    if(my $CmdPath = getCmdPath("file")) {
1796        return `$CmdPath -b \"$Path\"`;
1797    }
1798    return "";
1799}
1800
1801sub getHeaderDeps($$)
1802{
1803    my ($AbsPath, $LVer) = @_;
1804
1805    if(defined $Cache{"getHeaderDeps"}{$LVer}{$AbsPath}) {
1806        return @{$Cache{"getHeaderDeps"}{$LVer}{$AbsPath}};
1807    }
1808    my %IncDir = ();
1809    detectRecursiveIncludes($AbsPath, $LVer);
1810    foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LVer}{$AbsPath}}))
1811    {
1812        if(not $HeaderPath) {
1813            next;
1814        }
1815        if($In::Opt{"MainCppDir"} and $HeaderPath=~/\A\Q$In::Opt{"MainCppDir"}\E([\/\\]|\Z)/) {
1816            next;
1817        }
1818        my $Dir = getDirname($HeaderPath);
1819        foreach my $Prefix (keys(%{$Header_Include_Prefix{$LVer}{$AbsPath}{$HeaderPath}}))
1820        {
1821            my $Dep = $Dir;
1822            if($Prefix)
1823            {
1824                if($In::Opt{"OS"} eq "windows")
1825                { # case insensitive seach on windows
1826                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) {
1827                        next;
1828                    }
1829                }
1830                elsif($In::Opt{"OS"} eq "macos")
1831                { # seach in frameworks
1832                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
1833                    {
1834                        if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/)
1835                        {# frameworks
1836                            my ($HFramework, $HName) = ($1, $2);
1837                            $Dep = $HFramework;
1838                        }
1839                        else
1840                        {# mismatch
1841                            next;
1842                        }
1843                    }
1844                }
1845                elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
1846                { # Linux, FreeBSD
1847                    next;
1848                }
1849            }
1850            if(not $Dep)
1851            { # nothing to include
1852                next;
1853            }
1854            if(isDefaultIncludeDir($Dep))
1855            { # included by the compiler
1856                next;
1857            }
1858            if(getDepth($Dep)==1)
1859            { # too short
1860                next;
1861            }
1862            if(isLibcDir($Dep))
1863            { # do NOT include /usr/include/{sys,bits}
1864                next;
1865            }
1866            $IncDir{$Dep} = 1;
1867        }
1868    }
1869    $Cache{"getHeaderDeps"}{$LVer}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LVer);
1870    return @{$Cache{"getHeaderDeps"}{$LVer}{$AbsPath}};
1871}
1872
1873sub sortIncPaths($$)
1874{
1875    my ($ArrRef, $LVer) = @_;
1876    if(not $ArrRef or $#{$ArrRef}<0) {
1877        return $ArrRef;
1878    }
1879    @{$ArrRef} = sort {$b cmp $a} @{$ArrRef};
1880    @{$ArrRef} = sort {getDepth($a)<=>getDepth($b)} @{$ArrRef};
1881    @{$ArrRef} = sort {sortDeps($b, $a, $LVer)} @{$ArrRef};
1882    return $ArrRef;
1883}
1884
1885sub sortDeps($$$)
1886{
1887    if($Header_Dependency{$_[2]}{$_[0]}
1888    and not $Header_Dependency{$_[2]}{$_[1]}) {
1889        return 1;
1890    }
1891    elsif(not $Header_Dependency{$_[2]}{$_[0]}
1892    and $Header_Dependency{$_[2]}{$_[1]}) {
1893        return -1;
1894    }
1895    return 0;
1896}
1897
1898sub registerHeader($$)
1899{ # input: absolute path of header, relative path or name
1900    my ($Header, $LVer) = @_;
1901    if(not $Header) {
1902        return "";
1903    }
1904    if(isAbsPath($Header) and not -f $Header)
1905    { # incorrect absolute path
1906        exitStatus("Access_Error", "can't access \'$Header\'");
1907    }
1908    if(skipHeader($Header, $LVer))
1909    { # skip
1910        return "";
1911    }
1912    if(my $Header_Path = identifyHeader($Header, $LVer))
1913    {
1914        detectHeaderIncludes($Header_Path, $LVer);
1915
1916        if(defined $In::Opt{"Tolerance"}
1917        and $In::Opt{"Tolerance"}=~/3/)
1918        { # 3 - skip headers that include non-Linux headers
1919            if($In::Opt{"OS"} ne "windows")
1920            {
1921                foreach my $Inc (keys(%{$Header_Includes{$LVer}{$Header_Path}}))
1922                {
1923                    if(specificHeader($Inc, "windows")) {
1924                        return "";
1925                    }
1926                }
1927            }
1928        }
1929
1930        if(my $RHeader_Path = $Header_ErrorRedirect{$LVer}{$Header_Path})
1931        { # redirect
1932            if($In::Desc{$LVer}{"RegHeader"}{$RHeader_Path}{"Identity"}
1933            or skipHeader($RHeader_Path, $LVer))
1934            { # skip
1935                return "";
1936            }
1937            $Header_Path = $RHeader_Path;
1938        }
1939        elsif($Header_ShouldNotBeUsed{$LVer}{$Header_Path})
1940        { # skip
1941            return "";
1942        }
1943
1944        if(my $HName = getFilename($Header_Path))
1945        { # register
1946            $In::Desc{$LVer}{"RegHeader"}{$Header_Path}{"Identity"} = $HName;
1947            $HeaderName_Paths{$LVer}{$HName}{$Header_Path} = 1;
1948        }
1949
1950        if(($Header=~/\.(\w+)\Z/ and $1 ne "h")
1951        or $Header!~/\.(\w+)\Z/)
1952        { # hpp, hh, etc.
1953            $In::ABI{$LVer}{"Language"} = "C++";
1954            $In::Opt{"CppHeaders"} = 1;
1955        }
1956
1957        if($Header=~/(\A|\/)c\+\+(\/|\Z)/)
1958        { # /usr/include/c++/4.6.1/...
1959            $In::Opt{"StdcxxTesting"} = 1;
1960        }
1961
1962        return $Header_Path;
1963    }
1964    return "";
1965}
1966
1967sub registerDir($$$)
1968{
1969    my ($Dir, $WithDeps, $LVer) = @_;
1970    $Dir=~s/[\/\\]+\Z//g;
1971    if(not $Dir) {
1972        return;
1973    }
1974    $Dir = getAbsPath($Dir);
1975
1976    my $Mode = "All";
1977    if($WithDeps)
1978    {
1979        if($RegisteredDirs{$LVer}{$Dir}{1}) {
1980            return;
1981        }
1982        elsif($RegisteredDirs{$LVer}{$Dir}{0}) {
1983            $Mode = "DepsOnly";
1984        }
1985    }
1986    else
1987    {
1988        if($RegisteredDirs{$LVer}{$Dir}{1}
1989        or $RegisteredDirs{$LVer}{$Dir}{0}) {
1990            return;
1991        }
1992    }
1993    $Header_Dependency{$LVer}{$Dir} = 1;
1994    $RegisteredDirs{$LVer}{$Dir}{$WithDeps} = 1;
1995    if($Mode eq "DepsOnly")
1996    {
1997        foreach my $Path (cmdFind($Dir,"d")) {
1998            $Header_Dependency{$LVer}{$Path} = 1;
1999        }
2000        return;
2001    }
2002    foreach my $Path (sort {length($b)<=>length($a)} cmdFind($Dir,"f"))
2003    {
2004        if($WithDeps)
2005        {
2006            my $SubDir = $Path;
2007            while(($SubDir = getDirname($SubDir)) ne $Dir)
2008            { # register all sub directories
2009                $Header_Dependency{$LVer}{$SubDir} = 1;
2010            }
2011        }
2012        if(isNotHeader($Path)) {
2013            next;
2014        }
2015        if(ignorePath($Path)) {
2016            next;
2017        }
2018        # Neighbors
2019        foreach my $Part (getPrefixes($Path)) {
2020            $In::Desc{$LVer}{"IncludeNeighbors"}{$Part} = $Path;
2021        }
2022    }
2023    if(getFilename($Dir) eq "include")
2024    { # search for "lib/include/" directory
2025        my $LibDir = $Dir;
2026        if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) {
2027            registerDir($LibDir, $WithDeps, $LVer);
2028        }
2029    }
2030}
2031
2032sub getIncString($$)
2033{
2034    my ($ArrRef, $Style) = @_;
2035    if(not $ArrRef or $#{$ArrRef}<0) {
2036        return "";
2037    }
2038
2039    my $Str = "";
2040    foreach (@{$ArrRef}) {
2041        $Str .= " ".includeOpt($_, $Style);
2042    }
2043    return $Str;
2044}
2045
2046sub getIncPaths($$)
2047{
2048    my ($HRef, $LVer) = @_;
2049
2050    my @IncPaths = @{$Add_Include_Paths{$LVer}};
2051    if($In::Desc{$LVer}{"AutoIncludePaths"})
2052    { # auto-detecting dependencies
2053        my %Includes = ();
2054        foreach my $HPath (@{$HRef})
2055        {
2056            foreach my $Dir (getHeaderDeps($HPath, $LVer))
2057            {
2058                if($In::Desc{$LVer}{"SkipIncludePaths"}{$Dir}) {
2059                    next;
2060                }
2061                if(my $SystemRoot = $In::Opt{"SystemRoot"})
2062                {
2063                    if($In::Desc{$LVer}{"SkipIncludePaths"}{$SystemRoot.$Dir}) {
2064                        next;
2065                    }
2066                }
2067                $Includes{$Dir} = 1;
2068            }
2069        }
2070        foreach my $Dir (@{sortIncPaths([keys(%Includes)], $LVer)}) {
2071            push_U(\@IncPaths, $Dir);
2072        }
2073    }
2074    else
2075    { # user-defined paths
2076        @IncPaths = @{$Include_Paths{$LVer}};
2077    }
2078    return \@IncPaths;
2079}
2080
2081sub searchForHeaders($)
2082{
2083    my $LVer = $_[0];
2084
2085    my $DescRef = $In::Desc{$LVer};
2086
2087    # gcc standard include paths
2088    registerGccHeaders();
2089
2090    if($In::ABI{$LVer}{"Language"} eq "C++" and not $In::Opt{"StdcxxTesting"})
2091    { # c++ standard include paths
2092        registerCppHeaders();
2093    }
2094
2095    # processing header paths
2096    my @HPaths = ();
2097
2098    if($DescRef->{"IncludePaths"}) {
2099        @HPaths = @{$DescRef->{"IncludePaths"}};
2100    }
2101
2102    if($DescRef->{"AddIncludePaths"}) {
2103        @HPaths = (@HPaths, @{$DescRef->{"AddIncludePaths"}});
2104    }
2105
2106    foreach my $Path (@HPaths)
2107    {
2108        my $IPath = $Path;
2109        if(my $SystemRoot = $In::Opt{"SystemRoot"})
2110        {
2111            if(isAbsPath($Path)) {
2112                $Path = $SystemRoot.$Path;
2113            }
2114        }
2115        if(not -e $Path) {
2116            exitStatus("Access_Error", "can't access \'$Path\'");
2117        }
2118        elsif(-f $Path) {
2119            exitStatus("Access_Error", "\'$Path\' - not a directory");
2120        }
2121        elsif(-d $Path)
2122        {
2123            $Path = getAbsPath($Path);
2124            registerDir($Path, 0, $LVer);
2125
2126            if($DescRef->{"AddIncludePaths"}
2127            and grep {$IPath eq $_} @{$DescRef->{"AddIncludePaths"}}) {
2128                push(@{$Add_Include_Paths{$LVer}}, $Path);
2129            }
2130            else {
2131                push(@{$Include_Paths{$LVer}}, $Path);
2132            }
2133        }
2134    }
2135
2136    # registering directories
2137    my @Headers = keys(%{$DescRef->{"Headers"}});
2138    @Headers = sort {$DescRef->{"Headers"}{$a}<=>$DescRef->{"Headers"}{$b}} @Headers;
2139    foreach my $Path (@Headers)
2140    {
2141        if(not -e $Path) {
2142            next;
2143        }
2144        $Path = getAbsPath($Path);
2145        if(-d $Path) {
2146            registerDir($Path, 1, $LVer);
2147        }
2148        elsif(-f $Path)
2149        {
2150            my $Dir = getDirname($Path);
2151            if(not grep { $Dir eq $_ } (@{$In::Opt{"SysPaths"}{"include"}})
2152            and not $LocalIncludes{$Dir}) {
2153                registerDir($Dir, 1, $LVer);
2154            }
2155        }
2156    }
2157
2158    # clean memory
2159    %RegisteredDirs = ();
2160
2161    # registering headers
2162    my $Position = 0;
2163    foreach my $Path (@Headers)
2164    {
2165        if(isAbsPath($Path) and not -e $Path) {
2166            exitStatus("Access_Error", "can't access \'$Path\'");
2167        }
2168        $Path = pathFmt($Path);
2169        if(isHeader($Path, 1, $LVer))
2170        {
2171            if(my $HPath = registerHeader($Path, $LVer)) {
2172                $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"} = $Position++;
2173            }
2174        }
2175        elsif(-d $Path)
2176        {
2177            my @Registered = ();
2178            foreach my $P (cmdFind($Path,"f"))
2179            {
2180                if(ignorePath($P)) {
2181                    next;
2182                }
2183                if(not isHeader($P, 0, $LVer)) {
2184                    next;
2185                }
2186                if(my $HPath = registerHeader($P, $LVer)) {
2187                    push(@Registered, $HPath);
2188                }
2189            }
2190            @Registered = sort {sortHeaders($a, $b)} @Registered;
2191            sortByWord(\@Registered, $In::Opt{"TargetLibShort"});
2192            foreach my $P (@Registered) {
2193                $In::Desc{$LVer}{"RegHeader"}{$P}{"Pos"} = $Position++;
2194            }
2195        }
2196        elsif(not defined $In::Opt{"SkipUnidentified"}) {
2197            exitStatus("Access_Error", "can't identify \'$Path\' as a header file");
2198        }
2199    }
2200
2201    if(defined $In::Opt{"Tolerance"}
2202    and $In::Opt{"Tolerance"}=~/4/)
2203    { # 4 - skip headers included by others
2204        foreach my $Path (keys(%{$In::Desc{$LVer}{"RegHeader"}}))
2205        {
2206            if(defined $Header_Includes_R{$LVer}{$Path}) {
2207                delete($In::Desc{$LVer}{"RegHeader"}{$Path});
2208            }
2209        }
2210    }
2211
2212    if(not defined $In::Desc{$LVer}{"Include_Preamble"}) {
2213        $In::Desc{$LVer}{"Include_Preamble"} = [];
2214    }
2215
2216    if(my $HList = $DescRef->{"IncludePreamble"})
2217    { # preparing preamble headers
2218        foreach my $Header (split(/\s*\n\s*/, $HList))
2219        {
2220            if(isAbsPath($Header) and not -f $Header) {
2221                exitStatus("Access_Error", "can't access file \'$Header\'");
2222            }
2223            $Header = pathFmt($Header);
2224            if(my $Header_Path = isHeader($Header, 1, $LVer))
2225            {
2226                if(skipHeader($Header_Path, $LVer)) {
2227                    next;
2228                }
2229                push_U($In::Desc{$LVer}{"Include_Preamble"}, $Header_Path);
2230            }
2231            elsif(not defined $In::Opt{"SkipUnidentified"}) {
2232                exitStatus("Access_Error", "can't identify \'$Header\' as a header file");
2233            }
2234        }
2235    }
2236
2237    foreach my $Header_Name (keys(%{$HeaderName_Paths{$LVer}}))
2238    { # set relative paths (for duplicates)
2239        if(keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})>=2)
2240        { # search for duplicates
2241            my $FirstPath = (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}}))[0];
2242            my $Prefix = getDirname($FirstPath);
2243            while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/)
2244            { # detect a shortest distinguishing prefix
2245                my $NewPrefix = $1;
2246                my %Identity = ();
2247                foreach my $Path (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}}))
2248                {
2249                    if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) {
2250                        $Identity{$Path} = $1;
2251                    }
2252                }
2253                if(keys(%Identity)==keys(%{$HeaderName_Paths{$LVer}{$Header_Name}}))
2254                { # all names are different with current prefix
2255                    foreach my $Path (keys(%{$HeaderName_Paths{$LVer}{$Header_Name}})) {
2256                        $In::Desc{$LVer}{"RegHeader"}{$Path}{"Identity"} = $Identity{$Path};
2257                    }
2258                    last;
2259                }
2260                $Prefix = $NewPrefix; # increase prefix
2261            }
2262        }
2263    }
2264
2265    # clean memory
2266    %HeaderName_Paths = ();
2267
2268    foreach my $HName (keys(%{$In::Desc{$LVer}{"IncludeOrder"}}))
2269    { # ordering headers according to the descriptor
2270        my $PairName = $In::Desc{$LVer}{"IncludeOrder"}{$HName};
2271        my ($Pos, $PairPos, $Path, $PairPath) = (-1, -1, undef, undef);
2272
2273        my @Paths = keys(%{$In::Desc{$LVer}{"RegHeader"}});
2274        @Paths = sort {$In::Desc{$LVer}{"RegHeader"}{$a}{"Pos"}<=>$In::Desc{$LVer}{"RegHeader"}{$b}{"Pos"}} @Paths;
2275
2276        foreach my $HPath (@Paths)
2277        {
2278            if(getFilename($HPath) eq $PairName)
2279            {
2280                $PairPos = $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"};
2281                $PairPath = $HPath;
2282            }
2283            if(getFilename($HPath) eq $HName)
2284            {
2285                $Pos = $In::Desc{$LVer}{"RegHeader"}{$HPath}{"Pos"};
2286                $Path = $HPath;
2287            }
2288        }
2289        if($PairPos!=-1 and $Pos!=-1
2290        and int($PairPos)<int($Pos))
2291        {
2292            my %Tmp = %{$In::Desc{$LVer}{"RegHeader"}{$Path}};
2293            %{$In::Desc{$LVer}{"RegHeader"}{$Path}} = %{$In::Desc{$LVer}{"RegHeader"}{$PairPath}};
2294            %{$In::Desc{$LVer}{"RegHeader"}{$PairPath}} = %Tmp;
2295        }
2296    }
2297    if(not keys(%{$In::Desc{$LVer}{"RegHeader"}})) {
2298        exitStatus("Error", "header files are not found in the ".$DescRef->{"Version"});
2299    }
2300}
2301
2302sub addTargetHeaders($)
2303{
2304    my $LVer = $_[0];
2305
2306    foreach my $RegHeader (keys(%{$In::Desc{$LVer}{"RegHeader"}}))
2307    {
2308        my $RegDir = getDirname($RegHeader);
2309        $In::Desc{$LVer}{"TargetHeader"}{getFilename($RegHeader)} = 1;
2310
2311        if(not $In::Desc{$LVer}{"AutoIncludePaths"}) {
2312            detectRecursiveIncludes($RegHeader, $LVer);
2313        }
2314
2315        foreach my $RecInc (keys(%{$RecursiveIncludes{$LVer}{$RegHeader}}))
2316        {
2317            my $Dir = getDirname($RecInc);
2318
2319            if(familiarDirs($RegDir, $Dir)
2320            or $RecursiveIncludes{$LVer}{$RegHeader}{$RecInc}!=1)
2321            { # in the same directory or included by #include "..."
2322                $In::Desc{$LVer}{"TargetHeader"}{getFilename($RecInc)} = 1;
2323            }
2324        }
2325    }
2326}
2327
2328sub familiarDirs($$)
2329{
2330    my ($D1, $D2) = @_;
2331    if($D1 eq $D2) {
2332        return 1;
2333    }
2334
2335    my $U1 = index($D1, "/usr/");
2336    my $U2 = index($D2, "/usr/");
2337
2338    if($U1==0 and $U2!=0) {
2339        return 0;
2340    }
2341
2342    if($U2==0 and $U1!=0) {
2343        return 0;
2344    }
2345
2346    if(index($D2, $D1."/")==0) {
2347        return 1;
2348    }
2349
2350    # /usr/include/DIR
2351    # /home/user/DIR
2352
2353    my $DL = getDepth($D1);
2354
2355    my @Dirs1 = ($D1);
2356    while($DL - getDepth($D1)<=2
2357    and getDepth($D1)>=4
2358    and $D1=~s/[\/\\]+[^\/\\]*?\Z//) {
2359        push(@Dirs1, $D1);
2360    }
2361
2362    my @Dirs2 = ($D2);
2363    while(getDepth($D2)>=4
2364    and $D2=~s/[\/\\]+[^\/\\]*?\Z//) {
2365        push(@Dirs2, $D2);
2366    }
2367
2368    foreach my $P1 (@Dirs1)
2369    {
2370        foreach my $P2 (@Dirs2)
2371        {
2372            if($P1 eq $P2) {
2373                return 1;
2374            }
2375        }
2376    }
2377    return 0;
2378}
2379
2380sub isHeaderFile($)
2381{
2382    if($_[0]=~/\.($HEADER_EXT)\Z/i) {
2383        return $_[0];
2384    }
2385    return 0;
2386}
2387
2388sub isNotHeader($)
2389{
2390    if($_[0]=~/\.\w+\Z/
2391    and $_[0]!~/\.($HEADER_EXT)\Z/i) {
2392        return 1;
2393    }
2394    return 0;
2395}
2396
2397sub getGccPath()
2398{
2399    if(defined $In::Opt{"GccPath"}) {
2400        return $In::Opt{"GccPath"};
2401    }
2402
2403    my $Path = undef;
2404
2405    if(my $CrossGcc = $In::Opt{"CrossGcc"})
2406    { # --cross-gcc=arm-linux-gcc
2407        if(-e $CrossGcc)
2408        { # absolute or relative path
2409            $Path = getAbsPath($CrossGcc);
2410        }
2411        elsif($CrossGcc!~/\// and getCmdPath($CrossGcc))
2412        { # command name
2413            $Path = $CrossGcc;
2414        }
2415        else {
2416            exitStatus("Access_Error", "can't access \'$CrossGcc\'");
2417        }
2418
2419        if($Path=~/\s/) {
2420            $Path = "\"".$Path."\"";
2421        }
2422    }
2423    else
2424    { # try default gcc
2425        $Path = getCmdPath("gcc");
2426
2427        if(not $Path)
2428        { # try to find gcc-X.Y
2429            foreach my $P (@{$In::Opt{"SysPaths"}{"bin"}})
2430            {
2431                if(my @GCCs = cmdFind($P, "", '/gcc-[0-9.]*\Z', 1, 1))
2432                { # select the latest version
2433                    @GCCs = sort {$b cmp $a} @GCCs;
2434                    if(checkGcc("3", $GCCs[0]))
2435                    {
2436                        $Path = $GCCs[0];
2437                        last;
2438                    }
2439                }
2440            }
2441        }
2442        if(not $Path) {
2443            exitStatus("Not_Found", "can't find GCC>=3.0 in PATH");
2444        }
2445    }
2446
2447    return ($In::Opt{"GccPath"} = $Path);
2448}
2449
2450sub clearSysFilesCache($)
2451{
2452    my $LVer = $_[0];
2453
2454    %Cache = ();
2455
2456    delete($RecursiveIncludes{$LVer});
2457    delete($Header_Include_Prefix{$LVer});
2458    delete($Header_Includes{$LVer});
2459    delete($Header_ErrorRedirect{$LVer});
2460}
2461
2462sub dumpFilesInfo($)
2463{ # extra information for other tools
2464    my $LVer = $_[0];
2465    my $EInfo = $In::Opt{"ExtraInfo"};
2466
2467    writeFile($EInfo."/recursive-includes", Dumper($RecursiveIncludes{$LVer}));
2468    writeFile($EInfo."/direct-includes", Dumper($Header_Includes{$LVer}));
2469
2470    if(my @Redirects = keys(%{$Header_ErrorRedirect{$LVer}}))
2471    {
2472        my $REDIR = "";
2473        foreach my $P1 (sort @Redirects) {
2474            $REDIR .= $P1.";".$Header_ErrorRedirect{$LVer}{$P1}."\n";
2475        }
2476        writeFile($EInfo."/include-redirect", $REDIR);
2477    }
2478}
2479
2480sub callPreprocessor($$$)
2481{
2482    my ($Path, $Inc, $LVer) = @_;
2483
2484    my $IncludeString=$Inc;
2485    if(not $Inc) {
2486        $IncludeString = getIncString(getIncPaths([$Path], $LVer), "GCC");
2487    }
2488
2489    my $TmpDir = $In::Opt{"Tmp"};
2490    my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString, $LVer);
2491    my $Out = $TmpDir."/preprocessed.h";
2492    system($Cmd." >\"$Out\" 2>\"$TmpDir/null\"");
2493
2494    return $Out;
2495}
2496
2497sub isHeader($$$)
2498{
2499    my ($Header, $UserDefined, $LVer) = @_;
2500    if(-d $Header) {
2501        return 0;
2502    }
2503    if(-f $Header) {
2504        $Header = getAbsPath($Header);
2505    }
2506    else
2507    {
2508        if(isAbsPath($Header))
2509        { # incorrect absolute path
2510            return 0;
2511        }
2512        if(my $HPath = identifyHeader($Header, $LVer)) {
2513            $Header = $HPath;
2514        }
2515        else
2516        { # can't find header
2517            return 0;
2518        }
2519    }
2520    if($Header=~/\.\w+\Z/)
2521    { # have an extension
2522        return isHeaderFile($Header);
2523    }
2524    else
2525    {
2526        if($UserDefined==2)
2527        { # specified on the command line
2528            if(cmdFile($Header)!~/HTML|XML/i) {
2529                return $Header;
2530            }
2531        }
2532        elsif($UserDefined)
2533        { # specified in the XML-descriptor
2534          # header file without an extension
2535            return $Header;
2536        }
2537        else
2538        {
2539            if(index($Header, "/include/")!=-1
2540            or cmdFile($Header)=~/C[\+]*\s+program/i)
2541            { # !~/HTML|XML|shared|dynamic/i
2542                return $Header;
2543            }
2544        }
2545    }
2546    return 0;
2547}
2548
2549return 1;
2550