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