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