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