]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
Move ugly .*.inc files into make/template/ where they can be more easily found
[user/henk/code/inspircd.git] / configure
1 #!/usr/bin/perl
2 ###################################################
3 # InspIRCd Configuration Script
4 #
5 # Copyright 2002-2009 The InspIRCd Development Team
6 #  http://wiki.inspircd.org/Credits
7 #
8 # Licensed under GPL, please see the COPYING file
9 # for more information
10 #
11 # $Id$
12 #
13 ###################################################
14
15 BEGIN {
16         require 5.8.0;
17 }
18
19 use strict;
20 use warnings FATAL => qw(all);
21
22 use Data::Dumper;
23 BEGIN {
24         $Data::Dumper::Sortkeys = 1;
25         $Data::Dumper::Useqq = 1;
26 };
27
28 use File::Copy ();
29 use Socket;
30 use Cwd;
31 use Getopt::Long;
32
33 # Utility functions for our buildsystem
34 use make::utilities;
35 use make::configure;
36 use make::gnutlscert;
37 use make::opensslcert;
38
39 ###############################################################################################
40 #
41 #                                 NON-EDITABLE VARIABLES
42 #
43 ###############################################################################################
44
45 our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
46     $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
47     $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port);
48
49 our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir);
50
51 sub list_extras ();
52
53 sub enable_extras (@);
54
55 sub disable_extras (@);
56
57 my @opt_enableextras;
58 my @opt_disableextras;
59
60 GetOptions (
61         'enable-gnutls' => \$opt_use_gnutls,
62         'rebuild' => \$opt_rebuild,
63         'enable-openssl' => \$opt_use_openssl,
64         'disable-interactive' => \$opt_nointeractive,
65         'enable-ports' => \$opt_ports,
66         'enable-epoll' => \$opt_epoll,
67         'enable-kqueue' => \$opt_kqueue,
68         'disable-ports' => \$opt_noports,
69         'disable-epoll' => \$opt_noepoll,
70         'disable-kqueue' => \$opt_nokqueue,
71         'disable-ipv6' => \$opt_noipv6,
72         'with-cc=s' => \$opt_cc,
73         'with-maxbuf=i' => \$opt_maxbuf,
74         'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
75         'prefix=s' => \$opt_base_dir,
76         'config-dir=s' => \$opt_config_dir,
77         'module-dir=s' => \$opt_module_dir,
78         'binary-dir=s' => \$opt_binary_dir,
79         'disable-debuginfo' => sub { $opt_disable_debug = 1 },
80         'help'  => sub { showhelp(); },
81         'update' => sub { update(); },
82         'svnupdate' => sub { svnupdate(); },
83         'clean' => sub { clean(); },
84         'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
85         'enable-extras=s@' => \@opt_enableextras, # ^
86         'disable-extras=s@' => \@opt_disableextras, # ^
87         'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
88         'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
89 );
90
91 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
92         @opt_enableextras = split /,/, join(',', @opt_enableextras);
93         @opt_disableextras = split /,/, join(',', @opt_disableextras);
94         enable_extras(@opt_enableextras);
95         disable_extras(@opt_disableextras);
96         list_extras;
97         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
98         exit 0;
99 }
100
101 our $non_interactive = (
102         (defined $opt_base_dir) ||
103         (defined $opt_config_dir) ||
104         (defined $opt_module_dir) ||
105         (defined $opt_base_dir) ||
106         (defined $opt_binary_dir) ||
107         (defined $opt_nointeractive) ||
108         (defined $opt_cc) ||
109         (defined $opt_noipv6) ||
110         (defined $opt_kqueue) ||
111         (defined $opt_epoll) ||
112         (defined $opt_ports) ||
113         (defined $opt_use_openssl) ||
114         (defined $opt_nokqueue) ||
115         (defined $opt_noepoll) ||
116         (defined $opt_noports) ||
117         (defined $opt_maxbuf) ||
118         (defined $opt_use_gnutls) ||
119         (defined $opt_freebsd_port)
120 );
121 our $interactive = !$non_interactive;
122
123 chomp(our $topdir = getcwd());
124 our $this = resolve_directory($topdir);                                         # PWD, Regardless.
125 our @modlist = ();                                                                      # Declare for Module List..
126 our %config = ();                                                                       # Initiate Configuration Hash..
127 $config{ME} = resolve_directory($topdir);                               # Present Working Directory
128
129 $config{BASE_DIR} = $config{ME}."/run";
130
131 if (defined $opt_base_dir)
132 {
133         $config{BASE_DIR} = $opt_base_dir;
134 }
135
136 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");        # Configuration Directory
137 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
138 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");         # Binary Directory
139 $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
140
141 if (defined $opt_config_dir)
142 {
143         $config{CONFIG_DIR} = $opt_config_dir;
144 }
145 if (defined $opt_module_dir)
146 {
147         $config{MODULE_DIR} = $opt_module_dir;
148 }
149 if (defined $opt_binary_dir)
150 {
151         $config{BINARY_DIR} = $opt_binary_dir;
152 }
153 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version.
154
155 if (defined $opt_freebsd_port)
156 {
157         chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
158         chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
159         $config{USE_FREEBSD_BASE_SSL} = "n";
160 }
161 else
162 {
163         if ($^O eq "freebsd")
164         {
165                 # default: use base ssl
166                 chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
167                 chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
168         }
169         else
170         {
171                 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
172                 $config{HAS_OPENSSL_PORT} = "";
173         }
174 }
175
176 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
177 chomp(our $openssl_ver = $config{HAS_OPENSSL});
178 $config{USE_GNUTLS}         = "n";
179 if (defined $opt_use_gnutls)
180 {
181         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
182 }
183 $config{USE_OPENSSL}    = "n";                                          # Use openssl.
184 if (defined $opt_use_openssl)
185 {
186         $config{USE_OPENSSL} = "y";
187 }
188
189 # no, let's not change these.
190 $config{OPTIMITEMP}      = "0";                                         # Default Optimisation Value
191 if (!defined $opt_disable_debug)
192 {
193         $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
194 }
195 else
196 {
197         $config{OPTIMISATI}      = "-O2";                               # DEBUGGING OFF!
198 }
199
200 $config{HAS_STRLCPY}    = "false";                                      # strlcpy Check.
201 $config{HAS_STDINT}      = "false";                                     # stdint.h check
202 $config{USE_KQUEUE}      = "y";                                         # kqueue enabled
203 if (defined $opt_kqueue)
204 {
205         $config{USE_KQUEUE} = "y";
206 }
207 if (defined $opt_nokqueue)
208 {
209         $config{USE_KQUEUE} = "n";
210 }
211 $config{USE_POLL}     = "y";                                    # poll enabled
212 $config{USE_EPOLL}        = "y";                                        # epoll enabled
213 if (defined $opt_epoll)
214 {
215         $config{USE_EPOLL} = "y";
216 }
217 if (defined $opt_noepoll)
218 {
219         $config{USE_EPOLL} = "n";
220 }
221 $config{USE_PORTS}        = "y";                                        # epoll enabled
222 if (defined $opt_ports)
223 {
224         $config{USE_PORTS} = "y";
225 }
226 if (defined $opt_noports)
227 {
228         $config{USE_PORTS} = "n";
229 }
230 $config{IPV6}          = "y";                                   # IPv6 support
231 if (defined $opt_noipv6)
232 {
233         $config{IPV6} = "n";
234 }
235 chomp($config{GCCVER}       = `g++ -dumpversion | cut -c 1`);           # Major GCC Version
236 chomp($config{GCCMINOR}     = `g++ -dumpversion | cut -c 3`);
237 $config{_SOMAXCONN} = SOMAXCONN;                                        # Max connections in accept queue
238 $config{OSNAME}             = $^O;                                      # Operating System Name
239 $config{IS_DARWIN}        = "NO";                                       # Is OSX?
240 $config{STARTSCRIPT}      = "inspircd";                 # start script?
241 $config{DESTINATION}      = "BASE";                             # Is target path.
242 $config{EXTRA_DIR}        = "";                                         # Is empty.
243 if ($config{OSNAME} =~ /darwin/i)
244 {
245         $config{IS_DARWIN} = "YES";
246         $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
247         $config{DESTINATION}      = "LAUNCHDPATH";                              # Is OSX target.
248         $config{EXTRA_DIR}          = " launchd_dir";                           # Is OSX specific path.
249 }
250 $config{CC}                 = "g++";                                            # C++ compiler
251 if (defined $opt_cc)
252 {
253         $config{CC} = $opt_cc;
254 }
255 our $exec = $config{CC} . " -dumpversion | cut -c 1";
256 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
257 $exec = $config{CC} . " -dumpversion | cut -c 3";
258 chomp($config{GCCMINOR}         = `$exec`);
259 $config{MAXBUF}                 = "512";                                # Max buffer size
260
261 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/) {
262         $config{HAS_OPENSSL} = $1;
263 } else {
264         $config{HAS_OPENSSL} = "";
265 }
266
267 if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
268         print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";
269         exit;
270 }
271
272 # Get and Set some important vars..
273 getmodules();
274
275 sub clean
276 {
277         unlink(".config.cache");
278 }
279
280 our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
281
282 sub update
283 {
284         eval {
285                 chomp($topdir = getcwd());
286                 $this = resolve_directory($topdir);                                          # PWD, Regardless.
287                 getmodules();
288                 # Does the cache file exist?
289                 if (!getcache()) {
290                         # No, No it doesn't.. *BASH*
291                         print "You have not run ./configure before. Please do this before trying to run the update script.\n";
292                         exit 0;
293                 } else {
294                         # We've Loaded the cache file and all our variables..
295                         print "Updating files...\n";
296                         if (defined($opt_disable_debug) && $opt_disable_debug == 1)
297                         {
298                                 print "Disabling debug information (-g).\n";
299                                 $config{OPTIMISATI} = "";
300                         }
301                         $has_epoll = $config{HAS_EPOLL};
302                         $has_ports = $config{HAS_PORTS};
303                         $has_kqueue = $config{HAS_KQUEUE};
304                         writefiles(1);
305                         makecache();
306                         print "Complete.\n";
307                         exit;
308                 }
309         };
310         if ($@)
311         {
312                 print "Configure update failed: $@\n";
313         }
314         exit;
315 }
316
317 sub svnupdate
318 {
319         my $fail = 0;
320         open(FH,"<.svn/entries") or $fail = 1;
321         if ($fail) {
322                 print "This is not an SVN copy of InspIRCd.\n";
323                 exit 1;
324         }
325         else
326         {
327                 close(FH);
328         }
329         open my $fd, "-|", "svn update";
330         my $configurechanged = 0; # Needs ./configure -update
331         my $rootincchanged = 0;
332         my @conflicted = ();
333         while (defined(my $line = <$fd>))
334         {
335                 my ($action, $file);
336                 print $line;
337                 $line =~ m/^([ADUCG])\s+(.*)$/ or next;
338                 ($action, $file) = ($1, $2);
339                 if ($action eq "C")
340                 {
341                         push @conflicted, $file;
342                         if ($file eq "configure")
343                         {
344                                 $configurechanged = 1;
345                         }
346                         elsif ($file =~ m/^\..*\.inc$/)
347                         {
348                                 $rootincchanged = 1;
349                         }
350                 }
351                 elsif ($action eq "U" || $action eq "G")
352                 {
353                         if ($file eq "configure")
354                         {
355                                 $configurechanged = 1;
356                         }
357                         elsif ($file =~ m/^\..*\.inc$/)
358                         {
359                                 $rootincchanged = 1;
360                         }
361                 }
362         }
363         unless (close $fd) # close() waits for exit and returns false if the command failed
364         {
365                 if ($! == 0)
366                 {
367                         print STDERR "Problem updating from SVN, please check above for errors\n";
368                 }
369                 else
370                 {
371                         print STDERR "Failed to run SVN: $!\n";
372                 }
373                 exit 1;
374         }
375         if (scalar(@conflicted) > 0)
376         {
377                 print STDERR "\e[0;33;1mERROR:\e[0m You have local modifications which conflicted with the updates from SVN\n";
378                 print STDERR "Configure is not able to complete the update. Please resolve these conflicts, then run ./configure -update\n";
379                 print "Conflicted files: " . join ", ", @conflicted . "\n";
380                 exit 1;
381         }
382         if ($configurechanged)
383         {
384                 system("perl configure -update");
385         }
386         else
387         {
388                 print "No need to update Makefiles.\n";
389         }
390         if (defined $opt_rebuild) {
391                 system("make install");
392         }
393         exit;
394 }
395
396 sub test_compile {
397         my $feature = shift;
398         my $fail = 0;
399         $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
400         $fail ||= system "./test_$feature";
401         unlink "test_$feature";
402         return !$fail;
403 }
404
405 print "Running non-interactive configure...\n" unless $interactive;
406 print "Checking for cache from previous configure... ";
407 print ((!getcache()) ? "not found\n" : "found\n");
408 $config{SYSTEM} = lc $^O;
409 print "Checking operating system version... $config{SYSTEM}\n";
410
411 printf "Checking if stdint.h exists... ";
412 $config{HAS_STDINT} = "true";
413 our $fail = 0;
414 open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
415 if ($config{HAS_STDINT} eq "true") {
416         close(STDINT);
417 }
418 print "yes\n" if $config{HAS_STDINT} eq "true";
419 print "no\n" if $config{HAS_STDINT} eq "false";
420
421 printf "Checking if strlcpy exists... ";
422 # Perform the strlcpy() test..
423 $config{HAS_STRLCPY} = "false";
424 $fail = 0;
425 open(STRLCPY, "</usr/include/string.h") or $fail = 1;
426 if (!$fail) {
427         while (defined(my $line = <STRLCPY>)) {
428                 chomp($line);
429                 # try and find the delcaration of:
430                 # size_t strlcpy(...)
431                 if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
432                         $config{HAS_STRLCPY} = "true";
433                 }
434         }
435         close(STRLCPY);
436 }
437 print "yes\n" if $config{HAS_STRLCPY} eq "true";
438 print "no\n" if $config{HAS_STRLCPY} eq "false";
439
440 printf "Checking if kqueue exists... ";
441 $has_kqueue = 0;
442 $fail = 0;
443 open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
444 if (!$fail) {
445         while (defined(my $line = <KQUEUE>)) {
446                 chomp($line);
447                 # try and find the delcaration of:
448                 # int kqueue(void);
449                 if ($line =~ /int(\0x9|\s)+kqueue/) {
450                         $has_kqueue = 1;
451                 }
452         }
453         close(KQUEUE);
454 }
455 print "yes\n" if $has_kqueue == 1;
456 print "no\n" if $has_kqueue == 0;
457
458 printf "Checking for epoll support... ";
459 $has_epoll = test_compile('epoll');
460 print $has_epoll ? "yes\n" : "no\n";
461
462 printf "Checking for eventfd support... ";
463 $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
464 print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
465
466 printf "Checking if Solaris I/O completion ports are available... ";
467 $has_ports = 0;
468 our $system = `uname -s`;
469 chomp ($system);
470 $has_ports = 1 if ($system eq "SunOS");
471
472 if ($has_ports) {
473         my $kernel = `uname -r`;
474         chomp($kernel);
475         if (($kernel !~ /^5\.1./)) {
476                 $has_ports = 0;
477         }
478 }
479 print "yes\n" if $has_ports == 1;
480 print "no\n" if $has_ports == 0;
481
482 $config{HAS_EPOLL} = $has_epoll;
483 $config{HAS_KQUEUE} = $has_kqueue;
484
485 printf "Checking for libgnutls... ";
486 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
487         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
488                 print "yes\n";
489                 $config{HAS_GNUTLS} = "y";
490         } else {
491                 print "no\n";
492                 $config{HAS_GNUTLS} = "n";
493         }
494 } else {
495         print "no\n";
496         $config{HAS_GNUTLS} = "n";
497 }
498
499 printf "Checking for openssl... ";
500 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
501         if (defined($openssl_ver) && ($openssl_ver ne "")) {
502                 print "yes\n";
503                 $config{HAS_OPENSSL} = "y";
504         } else {
505                 print "no\n";
506                 $config{HAS_OPENSSL} = "n";
507         }
508 } else {
509         print "no\n";
510         $config{HAS_OPENSSL} = "n";
511 }
512
513 printf "Checking if you are running an ancient, unsupported OS... ";
514 if ($config{OSNAME} =~ /FreeBSD/i)
515 {
516         my $version = `uname -r`;
517         if ($version =~ /^4\./)
518         {
519                 print "yes.\n";
520                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
521                 print "To build, you will need to add the following to CXXFLAGS:\n";
522                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
523         }
524         else
525         {
526                 print "no ($version)\n";
527         }
528 }
529 else
530 {
531         print "no ($config{OSNAME})\n";
532 }
533
534 print "Checking for upgrades to extra and third party modules... ";
535 system "./modulemanager upgrade";
536
537 ################################################################################
538 #                         BEGIN INTERACTIVE PART                              #
539 ################################################################################
540
541 # Clear the Screen..
542 if ($interactive)
543 {
544         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
545         my $wholeos = $^O;
546
547         my $rev = getrevision();
548         # Display Introduction Message..
549         print <<"STOP" ;
550 Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
551 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
552
553 *** If you are unsure of any of these values, leave it blank for    ***
554 *** standard settings that will work, and your server will run      ***
555 *** using them. Please consult your IRC network admin if in doubt.  ***
556
557 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
558 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
559 dir, otherwise you won't have a config file!
560
561 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
562 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
563 STOP
564         if ($rev eq "r0") {
565                 print " (Non-SVN build)";
566         }
567         print ".\n\n";
568
569         $config{CHANGE_COMPILER} = "n";
570         print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
571
572         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
573                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
574 should NOT be used. You should probably specify a newer compiler.\n\n";
575                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
576                 if ($config{CHANGE_COMPILER} =~ /y/i) {
577                         print "What command do you want to use to invoke your compiler?\n";
578                         print "[\e[1;32m$config{CC}\e[0m] -> ";
579                         chomp($config{CC} = <STDIN>);
580                         if ($config{CC} eq "") {
581                                 $config{CC} = "g++";
582                         }
583                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
584                         if ($foo ne "") {
585                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
586                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
587                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
588                                 if ($config{GCCVER} < 3) {
589                                         print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
590                                 }
591                         }
592                         else {
593                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
594                         }
595                 }
596         }
597
598         print "\n";
599
600         # Directory Settings..
601         my $tmpbase = $config{BASE_DIR};
602         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
603         if ($tmpbase ne $config{BASE_DIR}) {
604                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
605                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
606                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
607         }
608
609         dir_check("are the configuration files", "CONFIG_DIR");
610         dir_check("are the modules to be compiled to", "MODULE_DIR");
611         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
612         dir_check("do you want the build to take place", "BUILD_DIR");
613
614         my $chose_hiperf = 0;
615         if ($has_kqueue) {
616                 yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
617                 print "\n";
618                 if ($config{USE_KQUEUE} eq "y") {
619                         $chose_hiperf = 1;
620                 }
621         }
622         if ($has_epoll) {
623                 yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
624                 print "\n";
625                 if ($config{USE_EPOLL} eq "y") {
626                         $chose_hiperf = 1;
627                 }
628         }
629         if ($has_ports) {
630                 yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
631                 print "\n";
632                 if ($config{USE_PORTS} eq "y") {
633                         $chose_hiperf = 1;
634                 }
635         }
636
637         if (!$chose_hiperf) {
638                 yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
639                 if ($config{USE_POLL} ne "y")
640                 {
641                         print "No high-performance socket engines are available, or you chose\n";
642                         print "not to enable one. Defaulting to select() engine.\n\n";
643                 }
644         }
645
646         yesno('IPV6',"Would you like to build InspIRCd with IPv6 support?\nYou can still use IPv4 addresses in your port bindings.\n\nEnable IPv6?");
647         print "\n";
648
649         $config{USE_FREEBSD_BASE_SSL} = "n";
650         $config{USE_FREEBSD_PORTS_SSL} = "n";
651         if ($config{HAS_OPENSSL_PORT} ne "")
652         {
653                 $config{USE_FREEBSD_PORTS_SSL} = "y";
654                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
655                 print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
656                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
657                 print "\n";
658                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
659
660                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
661                 {
662                         # update to port version
663                         $openssl_ver = $config{HAS_OPENSSL_PORT};
664                 }
665         }
666         else
667         {
668                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
669         }
670
671         $config{USE_SSL} = "n";
672
673         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
674         {
675                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
676                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
677
678                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
679                 if ($config{USE_SSL} eq "y")
680                 {
681                         if ($config{HAS_GNUTLS} eq "y")
682                         {
683                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
684                                 if ($config{USE_GNUTLS} eq "y")
685                                 {
686                                         print "\nUsing GnuTLS SSL module.\n";
687                                 }
688                         }
689
690                         if ($config{HAS_OPENSSL} eq "y")
691                         {
692                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
693                                 if ($config{USE_OPENSSL} eq "y")
694                                 {
695                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
696                                 }
697                         }
698                 }
699         }
700         else
701         {
702                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed if\n";
703                 print "you intend to use OpenSSL, or that GnuTLS is in your path if you intend\nto use GnuTLS.\n\n";
704         }
705 }
706
707 dumphash();
708
709 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
710 {
711         print "Sorry, but i couldn't detect gnutls. Make sure gnutls-config is in your path.\n";
712         exit(0);
713 }
714 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
715 {
716         print "Sorry, but i couldn't detect openssl. Make sure openssl is in your path.\n";
717         exit(0);
718 }
719 our $failed = 0;
720
721 $config{CERTGEN} ||= 'y';
722 yesno('CERTGEN',"Would you like generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
723
724 if ($config{USE_GNUTLS} eq "y") {
725         unless (-r "src/modules/m_ssl_gnutls.cpp") {
726                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
727                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
728         }
729         if ($interactive && $config{CERTGEN} eq 'y')
730         {
731                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
732                         print "SSL Certificates Not found, Generating.. \n\n
733 *************************************************************
734 * Generating the Private Key may take some time, go grab a  *
735 * Coffee. Even better, to generate some more entropy if it  *
736 * is taking a while, open another console and type du / a   *
737 * few times and get that HD going :) Then answer the        *
738 * Questions which follow. If you are unsure, just hit enter *
739 *************************************************************\n\n";
740                         $failed = make_gnutls_cert();
741                         if ($failed) {
742                                 print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
743                         } else {
744                                 print "\nCertificate generation complete, copying to config directory... ";
745                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
746                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
747                                 print "Done.\n\n";
748                         }
749                 }
750                 else {
751                         print "SSL Certificates found, skipping.\n\n";
752                 }
753         }
754         else
755         {
756                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
757         }
758 }
759
760 if ($config{USE_OPENSSL} eq "y") {
761         unless (-r "src/modules/m_ssl_openssl.cpp") {
762                 print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
763                 symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
764         }
765         $failed = 0;
766         if ($interactive && $config{CERTGEN} eq 'y')
767         {
768                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
769                         print "SSL Certificates Not found, Generating.. \n\n
770 *************************************************************
771 * Generating the certificates may take some time, go grab a *
772 * coffee, or something.                                     *
773 *************************************************************\n\n";
774                         make_openssl_cert();
775                         print "\nCertificate generation complete, copying to config directory... ";
776                         File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
777                         File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
778                         File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
779                         print "Done.\n\n";
780                 } else {
781                         print "SSL Certificates found, skipping.\n\n"
782                 }
783         }
784         else
785         {
786                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
787         }
788 }
789 if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
790         print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
791 }
792
793 depcheck();
794 writefiles(1);
795 makecache();
796
797 print "\n\n";
798 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
799 if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
800         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
801         print "modules in your config. This configure script has added those modules to the\n";
802         print "build process. For more info please refer to:\n";
803         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
804 }
805 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
806 if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
807         print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but i'm letting you\ngo ahead with it anyway, just in case i'm wrong :-)\n";
808 }
809
810 if ($config{GCCVER} < "3") {
811         print <<FOO2;
812 \e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
813 GCC 2.x series compilers only had partial (read as broken) C++ support, and
814 your compile will most likely fail horribly! If you have any problems, do NOT
815 report them to the bugtracker or forums without first upgrading your compiler
816 to a newer 3.x or 4.x (or whatever is available currently) version.
817 FOO2
818 }
819
820 ################################################################################
821 #                             HELPER FUNCTIONS                          #
822 ################################################################################
823 sub getcache {
824         # Retrieves the .config.cache file, and loads values into the main config hash.
825         open(CACHE, ".config.cache") or return 0;
826         while (<CACHE>) {
827                 chomp;
828                 # Ignore Blank lines, and comments..
829                 next if /^\s*$/;
830                 next if /^\s*#/;
831                 my ($key, $value) = split("=", $_, 2);
832                 $value =~ /^\"(.*)\"$/;
833                 # Do something with data here!
834                 $config{$key} = $1;
835         }
836         close(CACHE);
837         return 1;
838 }
839
840 sub makecache {
841         # Dump the contents of %config
842         print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
843         open(FILEHANDLE, ">.config.cache");
844         foreach my $key (keys %config) {
845                 print FILEHANDLE "$key=\"$config{$key}\"\n";
846         }
847         close(FILEHANDLE);
848 }
849
850 sub dir_check {
851         my ($desc, $hash_key) = @_;
852         my $complete = 0;
853         while (!$complete) {
854                 print "In what directory $desc?\n";
855                 print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
856                 chomp(my $var = <STDIN>);
857                 if ($var eq "") {
858                         $var = $config{$hash_key};
859                 }
860                 if ($var =~ /^\~\/(.+)$/) {
861                         # Convert it to a full path..
862                         $var = resolve_directory($ENV{HOME} . "/" . $1);
863                 }
864                 elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
865                 {
866                         # Assume relative Path was given.. fill in the rest.
867                         $var = $this . "/$var";
868                 }
869
870                 $var = resolve_directory($var);
871                 if (! -e $var) {
872                         print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
873                         chomp(my $tmp = <STDIN>);
874                         if (($tmp eq "") || ($tmp =~ /^y/i)) {
875                                 # Attempt to Create the Dir..
876                                 my $chk = eval {
877                                         use File::Path ();
878                                         File::Path::mkpath($var, 0, 0777);
879                                         1;
880                                 };
881                                 unless (defined($chk) && -d $var) {
882                                         print "Unable to create directory. ($var)\n\n";
883                                         # Restart Loop..
884                                         next;
885                                 }
886                         } else {
887                                 # They said they don't want to create, and we can't install there.
888                                 print "\n\n";
889                                 next;
890                         }
891                 } else {
892                         if (!is_dir($var)) {
893                                 # Target exists, but is not a directory.
894                                 print "File $var exists, but is not a directory.\n\n";
895                                 next;
896                         }
897                 }
898                 # Either Dir Exists, or was created fine.
899                 $config{$hash_key} = $var;
900                 $complete = 1;
901                 print "\n";
902         }
903 }
904
905 our $SHARED = "";
906
907 my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
908
909 sub writefiles {
910         my($writeheader) = @_;
911         # First File.. inspircd_config.h
912         chomp(my $incos = `uname -n -s -r`);
913         chomp(my $version = `sh src/version.sh`);
914         chomp(my $revision2 = getrevision());
915         if ($writeheader == 1)
916         {
917                 print "Writing \e[1;32minspircd_config.h\e[0m\n";
918                 open(FILEHANDLE, ">include/inspircd_config.h.tmp");
919                 print FILEHANDLE <<EOF;
920 /* Auto generated by configure, do not modify! */
921 #ifndef __CONFIGURATION_AUTO__
922 #define __CONFIGURATION_AUTO__
923
924 /* this is for windows support. */
925 #define CoreExport /**/
926 #define DllExport /**/
927
928 #define MOD_PATH "$config{MODULE_DIR}"
929 #define SOMAXCONN_S "$config{_SOMAXCONN}"
930 #define ENTRYPOINT int main(int argc, char** argv)
931
932 EOF
933 print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
934
935                 if ($config{OSNAME} =~ /SunOS/i) {
936                         print FILEHANDLE "#define IS_SOLARIS\n";
937                 }
938                 if ($config{OSNAME} =~ /MINGW32/i) {
939                         print FILEHANDLE "#define IS_MINGW\n";
940                 }
941                 if ($config{GCCVER} >= 3) {
942                         print FILEHANDLE "#define GCC3\n";
943                 }
944                 if (
945                         (($config{GCCVER} == 4) && ($config{GCCMINOR} >= 3))
946                                 ||
947                         ($config{GCCVER} > 4)
948                 ) {
949                         print FILEHANDLE "#define HASHMAP_DEPRECATED\n";
950                 }
951                 if ($config{HAS_STRLCPY} eq "true") {
952                         print FILEHANDLE "#define HAS_STRLCPY\n";
953                 }
954                 if ($config{HAS_STDINT} eq "true") {
955                         print FILEHANDLE "#define HAS_STDINT\n";
956                 }
957                 if ($config{IPV6} =~ /y/i) {
958                         print FILEHANDLE "#define IPV6\n";
959                 }
960                 if ($config{HAS_EVENTFD} eq 'true') {
961                         print FILEHANDLE "#define HAS_EVENTFD\n";
962                 }
963                 my $use_hiperf = 0;
964                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
965                         print FILEHANDLE "#define USE_KQUEUE\n";
966                         $config{SOCKETENGINE} = "socketengine_kqueue";
967                         $use_hiperf = 1;
968                 }
969                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
970                         print FILEHANDLE "#define USE_EPOLL\n";
971                         $config{SOCKETENGINE} = "socketengine_epoll";
972                         $use_hiperf = 1;
973                 }
974                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
975                         print FILEHANDLE "#define USE_PORTS\n";
976                         $config{SOCKETENGINE} = "socketengine_ports";
977                         $use_hiperf = 1;
978                 }
979                 # user didn't choose either epoll or select for their OS.
980                 # default them to USE_SELECT (ewwy puke puke)
981                 if (!$use_hiperf) {
982                         print "no hi-perf, " . $config{USE_POLL};
983                         if ($config{USE_POLL} eq "y")
984                         {
985                                 print FILEHANDLE "#define USE_POLL\n";
986                                 $config{SOCKETENGINE} = "socketengine_poll";
987                         }
988                         else
989                         {
990                                 print FILEHANDLE "#define USE_SELECT\n";
991                                 $config{SOCKETENGINE} = "socketengine_select";
992                         }
993                 }
994                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
995                 close(FILEHANDLE);
996
997                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
998                 print FILEHANDLE <<EOF;
999 #define VERSION "$version"
1000 #define REVISION "$revision2"
1001 #define SYSTEM "$incos"
1002 EOF
1003                 close FILEHANDLE;
1004
1005                 for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
1006                         my $diff = 0;
1007                         open my $fh1, $file or $diff = 1;
1008                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
1009                         while (!$diff) {
1010                                 my $line1 = <$fh1>;
1011                                 my $line2 = <$fh2>;
1012                                 if (defined($line1) != defined($line2)) {
1013                                         $diff = 1;
1014                                 } elsif (!defined $line1) {
1015                                         last;
1016                                 } else {
1017                                         $diff = ($line1 ne $line2);
1018                                 }
1019                         }
1020                         if ($diff) {
1021                                 unlink $file;
1022                                 rename "$file.tmp", $file;
1023                         } else {
1024                                 unlink "$file.tmp";
1025                         }
1026                 }
1027         }
1028
1029         # Write all .in files.
1030         my $tmp = "";
1031         my $file = "";
1032         my $exe = "inspircd";
1033
1034         # Do this once here, and cache it in the .*.inc files,
1035         # rather than attempting to read src/version.sh from
1036         # compiled code -- we might not have the source to hand.
1037         # Fix for bug#177 by Brain.
1038
1039         chomp($version = `sh ./src/version.sh`);
1040         chomp(my $revision = getrevision());
1041         $version = "$version(r$revision)";
1042
1043         # We can actually parse any file starting with . and ending with .inc,
1044         # but right now we only parse .inspircd.inc to form './inspircd'
1045         prepare_dynamic_makefile();
1046
1047         my @dotfiles = qw(main.mk inspircd);
1048         push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
1049
1050         foreach my $file (@dotfiles) {
1051                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
1052                 $_ = join '', <FILEHANDLE>;
1053                 close(FILEHANDLE);
1054
1055                 $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
1056
1057                 for my $var (qw(
1058                         CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR
1059                         STARTSCRIPT DESTINATION EXTRA_DIR SOCKETENGINE
1060                 )) {
1061                         s/\@$var\@/$config{$var}/g;
1062                 }
1063                 s/\@EXECUTABLE\@/$exe/ if defined $exe;
1064                 s/\@VERSION\@/$version/ if defined $version;
1065
1066                 if ($file eq 'main.mk') {
1067                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
1068
1069                         my $mk_tmp = $_;
1070                         s/\@IFDEF (\S+)/ifdef $1/g;
1071                         s/\@IFNDEF (\S+)/ifndef $1/g;
1072                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
1073                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
1074                         s/\@ELSE/else/g;
1075                         s/\@ENDIF/endif/g;
1076                         s/ *\@BSD_ONLY .*\n//g;
1077                         s/\@GNU_ONLY //g;
1078                         s/\@DO_EXPORT (.*)/export $1/g;
1079                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1080                         print MKF $_;
1081                         close MKF;
1082
1083                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
1084                         $_ = $mk_tmp;
1085                         s/\@IFDEF (\S+)/.if defined($1)/g;
1086                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
1087                         s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1088                         s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1089                         s/\@ELSE/.else/g;
1090                         s/\@ENDIF/.endif/g;
1091                         s/\@BSD_ONLY //g;
1092                         s/ *\@GNU_ONLY .*\n//g;
1093                         $mk_tmp = $_;
1094                         $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1095                         open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1096                         print MKF $mk_tmp;
1097                         close MKF;
1098                 } else {
1099                         print "Writing \e[1;32m$file\e[0m ...\n";
1100                         open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1101                         print FILEHANDLE $_;
1102                         close(FILEHANDLE);
1103                 }
1104         }
1105
1106         chmod 0755, 'inspircd';
1107 }
1108
1109 sub depcheck
1110 {
1111         getmodules();
1112         for my $mod (@modlist) {
1113                 getcompilerflags("src/modules/m_$mod.cpp");
1114                 getlinkerflags("src/modules/m_$mod.cpp");
1115         }
1116 }
1117
1118 sub prepare_dynamic_makefile
1119 {
1120         my $i = 0;
1121
1122         if (!$has_epoll)
1123         {
1124                 $config{USE_EPOLL} = 0;
1125         }
1126         if (!$has_kqueue)
1127         {
1128                 $config{USE_KQUEUE} = 0;
1129         }
1130         if (!$has_ports)
1131         {
1132                 $config{USE_PORTS} = 0;
1133         }
1134 }
1135
1136 # Routine to list out the extra/ modules that have been enabled.
1137 # Note: when getting any filenames out and comparing, it's important to lc it if the
1138 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1139 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1140 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1141 sub list_extras () {
1142         use File::Spec;
1143         # @_ not used
1144         my $srcdir = File::Spec->catdir("src", "modules");
1145         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1146         local $_;
1147         my $dd;
1148         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1149         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1150         closedir $dd;
1151         undef $dd;
1152         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1153         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1154         closedir $dd;
1155         undef $dd;
1156         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1157         my %extras = ();
1158 EXTRA:  for my $extra (@extras) {
1159                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1160                 next if ($extra eq '.svn');
1161                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1162                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1163                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1164                 if (-l $abs_source) {
1165                         # Symlink, is it in the right place?
1166                         my $targ = readlink($abs_source);
1167                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1168                         if ($abs_targ eq $abs_extra) {
1169                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1170                         } else {
1171                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1172                         }
1173                 } elsif (-e $abs_source) {
1174                         my ($devext, $inoext) = stat($abs_extra);
1175                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1176                         if ($lnksrc > 1) {
1177                                 if ($devsrc == $devext && $inosrc == $inoext) {
1178                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1179                                 } else {
1180                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1181                                 }
1182                         } else {
1183                                 open my $extfd, "<", $abs_extra;
1184                                 open my $srcfd, "<", $abs_source;
1185                                 local $/ = undef;
1186                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1187                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1188                                 } else {
1189                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1190                                 }
1191                         }
1192                 } else {
1193                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1194                 }
1195         }
1196         # Now let's add dependency info
1197         for my $extra (keys(%extras)) {
1198                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1199                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1200                 my @deps = split / +/, getdependencies($abs_extra);
1201                 for my $dep (@deps) {
1202                         if (exists($extras{$dep})) {
1203                                 my $ref = \$extras{$dep}; # Take reference.
1204                                 if ($$ref !~ m/needed by/) {
1205                                         # First dependency found.
1206                                         if ($$ref =~ m/enabled/) {
1207                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1208                                         } else {
1209                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1210                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1211                                         }
1212                                 } else {
1213                                         if ($$ref =~ m/enabled/) {
1214                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1215                                         } else {
1216                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1217                                         }
1218                                 }
1219                         }
1220                 }
1221         }
1222         for my $extra (sort {$a cmp $b} keys(%extras)) {
1223                 my $text = $extras{$extra};
1224                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1225                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1226                 } else {
1227                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1228                 }
1229         }
1230         return keys(%extras) if wantarray; # Can be used by manage_extras.
1231 }
1232
1233 sub enable_extras (@) {
1234         my (@extras) = @_;
1235         for my $extra (@extras) {
1236                 my $extrapath = "src/modules/extra/$extra";
1237                 if (!-e $extrapath) {
1238                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1239                         next;
1240                 }
1241                 my $source = "src/modules/$extra";
1242                 if (-e $source) {
1243                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1244                         next;
1245                 }
1246                 # Get dependencies, and add them to be processed.
1247                 my @deps = split / +/, getdependencies($extrapath);
1248                 for my $dep (@deps) {
1249                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1250                         if (!-e "src/modules/$dep") {
1251                                 if (-e "src/modules/extra/$dep") {
1252                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1253                                         push @extras, $dep;
1254                                 } else {
1255                                         print STDERR "\e[33;1mWARNING:\e[0m module \e[32;1m$extra\e[0m might be missing dependency \e[32;1m$dep\e[0m - YOU are responsible for satisfying it!\n";
1256                                 }
1257                         }
1258                 }
1259                 print "Enabling $extra ... \n";
1260                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1261         }
1262 }
1263
1264 sub disable_extras (@)
1265 {
1266         opendir my $dd, "src/modules/extra/";
1267         my @files = readdir($dd);
1268         closedir $dd;
1269         my (@extras) = @_;
1270 EXTRA:  for my $extra (@extras) {
1271                 my $extrapath = "src/modules/extra/$extra";
1272                 my $source = "src/modules/$extra";
1273                 if (!-e $extrapath) {
1274                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1275                         next;
1276                 }
1277                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1278                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Source is not a link or doesn't refer to the right file. Remove manually if this is in error.\n";
1279                         next;
1280                 }
1281                 # Check if anything needs this.
1282                 for my $file (@files) {
1283                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1284                         # File depends on this extra...
1285                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1286                                 # And is both enabled and not about to be disabled.
1287                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1288                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1289                                         next EXTRA;
1290                                 }
1291                         }
1292                 }
1293                 # Now remove.
1294                 print "Disabling $extra ... \n";
1295                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1296         }
1297 }