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