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