]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
Remove hardcoded MAXBUF constant.
[user/henk/code/inspircd.git] / configure
1 #!/usr/bin/env perl
2
3 #
4 # InspIRCd -- Internet Relay Chat Daemon
5 #
6 #   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
7 #   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
8 #   Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
9 #   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
10 #   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
11 #   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
12 #   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
13 #   Copyright (C) 2003-2006 Craig McLure <craig@chatspike.net>
14 #
15 # This file is part of InspIRCd.  InspIRCd is free software: you can
16 # redistribute it and/or modify it under the terms of the GNU General Public
17 # License as published by the Free Software Foundation, version 2.
18 #
19 # This program is distributed in the hope that it will be useful, but WITHOUT
20 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22 # details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 #
27
28
29 BEGIN {
30         require 5.8.0;
31 }
32
33 use strict;
34 use warnings FATAL => qw(all);
35
36 use Data::Dumper;
37 BEGIN {
38         $Data::Dumper::Sortkeys = 1;
39         $Data::Dumper::Useqq = 1;
40 };
41
42 use File::Copy ();
43 use Cwd;
44 use Getopt::Long;
45
46 # Utility functions for our buildsystem
47 use make::utilities;
48 use make::configure;
49
50 ###############################################################################################
51 #
52 #                                 NON-EDITABLE VARIABLES
53 #
54 ###############################################################################################
55
56 our ($opt_use_gnutls, $opt_use_openssl, $opt_nointeractive, $opt_ports, $opt_epoll, $opt_kqueue,
57      $opt_noports, $opt_noepoll, $opt_nokqueue, $opt_freebsd_port, $opt_system, $opt_uid);
58
59 our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir, $opt_data_dir, $opt_log_dir);
60
61 sub list_extras ();
62
63 sub enable_extras (@);
64
65 sub disable_extras (@);
66
67 my @opt_enableextras;
68 my @opt_disableextras;
69
70 GetOptions (
71         'enable-gnutls' => \$opt_use_gnutls,
72         'system' => \$opt_system,
73         'uid=s' => \$opt_uid,
74         'enable-openssl' => \$opt_use_openssl,
75         'disable-interactive' => \$opt_nointeractive,
76         'enable-ports' => \$opt_ports,
77         'enable-epoll' => \$opt_epoll,
78         'enable-kqueue' => \$opt_kqueue,
79         'disable-ports' => \$opt_noports,
80         'disable-epoll' => \$opt_noepoll,
81         'disable-kqueue' => \$opt_nokqueue,
82         'with-cc=s' => \$opt_cc,
83         'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
84         'prefix=s' => \$opt_base_dir,
85         'config-dir=s' => \$opt_config_dir,
86         'module-dir=s' => \$opt_module_dir,
87         'binary-dir=s' => \$opt_binary_dir,
88         'data-dir=s' => \$opt_data_dir,
89         'log-dir=s' => \$opt_log_dir,
90         'help'  => sub { showhelp(); },
91         'update' => sub { update(); },
92         'clean' => sub { clean(); },
93         'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
94         'enable-extras=s@' => \@opt_enableextras, # ^
95         'disable-extras=s@' => \@opt_disableextras, # ^
96 );
97
98 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
99         @opt_enableextras = split /,/, join(',', @opt_enableextras);
100         @opt_disableextras = split /,/, join(',', @opt_disableextras);
101         enable_extras(@opt_enableextras);
102         disable_extras(@opt_disableextras);
103         list_extras;
104         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
105         exit 0;
106 }
107
108 our $interactive = !(
109         (defined $opt_base_dir) ||
110         (defined $opt_config_dir) ||
111         (defined $opt_module_dir) ||
112         (defined $opt_base_dir) ||
113         (defined $opt_binary_dir) ||
114         (defined $opt_data_dir) ||
115         (defined $opt_log_dir) ||
116         (defined $opt_nointeractive) ||
117         (defined $opt_cc) ||
118         (defined $opt_kqueue) ||
119         (defined $opt_epoll) ||
120         (defined $opt_ports) ||
121         (defined $opt_use_openssl) ||
122         (defined $opt_nokqueue) ||
123         (defined $opt_noepoll) ||
124         (defined $opt_noports) ||
125         (defined $opt_system) ||
126         (defined $opt_uid) ||
127         (defined $opt_use_gnutls) ||
128         (defined $opt_freebsd_port)
129 );
130
131 chomp(our $topdir = getcwd());
132 our $this = resolve_directory($topdir);                                         # PWD, Regardless.
133 our @modlist = ();                                                                      # Declare for Module List..
134 our %config = ();                                                                       # Initiate Configuration Hash..
135 our $cache_loaded = getcache();
136 $config{ME} = resolve_directory($topdir);                               # Present Working Directory
137
138 $config{BASE_DIR} = $config{ME}."/run";
139
140 if (defined $opt_base_dir) {
141         $config{BASE_DIR} = $opt_base_dir;
142 } elsif (defined $opt_system) {
143         $config{BASE_DIR} = '/var/lib/inspircd';
144 }
145
146 if (defined $opt_system) {
147         $config{UID} = $opt_uid || 'ircd';
148         $config{CONFIG_DIR}      = '/etc/inspircd';
149         $config{MODULE_DIR}      = '/usr/lib/inspircd';
150         $config{BINARY_DIR}      = '/usr/sbin/';
151         $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
152         $config{DATA_DIR}        = '/var/inspircd';
153         $config{LOG_DIR}         = '/var/log/inspircd';
154 } else {
155         $config{UID} = $opt_uid || $<;
156         $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");        # Configuration Directory
157         $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
158         $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");         # Binary Directory
159         $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
160         $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data directory
161         $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log directory
162 }
163
164 if (defined $opt_config_dir) {
165         $config{CONFIG_DIR} = $opt_config_dir;
166 }
167 if (defined $opt_module_dir) {
168         $config{MODULE_DIR} = $opt_module_dir;
169 }
170 if (defined $opt_binary_dir) {
171         $config{BINARY_DIR} = $opt_binary_dir;
172 }
173 if (defined $opt_data_dir) {
174         $config{DATA_DIR} = $opt_data_dir;
175 }
176 if (defined $opt_log_dir) {
177         $config{LOG_DIR} = $opt_log_dir;
178 }
179 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`); # GNUTLS Version.
180
181 if (defined $opt_freebsd_port)
182 {
183         chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
184         chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
185         $config{USE_FREEBSD_BASE_SSL} = "n";
186 }
187 else
188 {
189         if ($^O eq "freebsd")
190         {
191                 # default: use base ssl
192                 chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
193                 chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
194         }
195         else
196         {
197                 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
198                 $config{HAS_OPENSSL_PORT} = "";
199         }
200 }
201
202 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
203 chomp(our $openssl_ver = $config{HAS_OPENSSL});
204 $config{USE_GNUTLS}         = "n";
205 if (defined $opt_use_gnutls)
206 {
207         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
208 }
209 $config{USE_OPENSSL}    = "n";                                          # Use openssl.
210 if (defined $opt_use_openssl)
211 {
212         $config{USE_OPENSSL} = "y";
213 }
214
215 $config{HAS_STDINT}      = "false";                                     # stdint.h check
216 $config{USE_KQUEUE}      = "y";                                         # kqueue enabled
217 if (defined $opt_nokqueue) {
218         $config{USE_KQUEUE} = "n";
219 }
220 $config{USE_POLL}     = "y";                                    # poll enabled
221 $config{USE_EPOLL}        = "y";                                        # epoll enabled
222 if (defined $opt_noepoll)
223 {
224         $config{USE_EPOLL} = "n";
225 }
226 $config{USE_PORTS}        = "y";                                        # epoll enabled
227 if (defined $opt_noports)
228 {
229         $config{USE_PORTS} = "n";
230 }
231 $config{OSNAME}             = $^O;                                      # Operating System Name
232 $config{STARTSCRIPT}      = "inspircd";                 # start script?
233 $config{DESTINATION}      = "BASE";                             # Is target path.
234 if ($config{OSNAME} =~ /darwin/i)
235 {
236         $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
237         $config{CC}                 = "xcrun clang++";                                  # C++ compiler for OSX.
238 }
239 else
240 {
241         $config{CC}                 = "g++";                                            # C++ compiler
242 }
243 if (defined $opt_cc)
244 {
245         $config{CC} = $opt_cc;
246 }
247 our $exec = $config{CC} . " -dumpversion | cut -c 1";
248 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
249 $exec = $config{CC} . " -dumpversion | cut -c 3";
250 chomp($config{GCCMINOR}         = `$exec`);
251
252 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
253         $config{HAS_OPENSSL} = $1;
254 } else {
255         $config{HAS_OPENSSL} = "";
256 }
257
258 if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
259         if ($config{OSNAME} eq 'darwin') {
260                 print $config{CC} . " was not found! You require clang++ (the LLVM C++ compiler, part of the OSX developer tools) to build InspIRCd!\n";
261         } else {
262                 print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";         
263         }
264         exit;
265 }
266
267 # Get and Set some important vars..
268 getmodules();
269
270 sub clean
271 {
272         unlink(".config.cache");
273 }
274
275 our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
276
277 sub update
278 {
279         eval {
280                 chomp($topdir = getcwd());
281                 $this = resolve_directory($topdir);                                          # PWD, Regardless.
282                 getmodules();
283                 # Does the cache file exist?
284                 if (!getcache()) {
285                         # No, No it doesn't.. *BASH*
286                         print "You have not run ./configure before. Please do this before trying to run the update script.\n";
287                         exit 0;
288                 } else {
289                         # We've Loaded the cache file and all our variables..
290                         print "Updating files...\n";
291                         $has_epoll = $config{HAS_EPOLL};
292                         $has_ports = $config{HAS_PORTS};
293                         $has_kqueue = $config{HAS_KQUEUE};
294                         writefiles(1);
295                         makecache();
296                         print "Complete.\n";
297                         exit;
298                 }
299         };
300         if ($@)
301         {
302                 print "Configure update failed: $@\n";
303         }
304         exit;
305 }
306
307 print "Running non-interactive configure...\n" unless $interactive;
308 print "Checking for cache from previous configure... ";
309 print ($cache_loaded ? "found\n" : "not found\n");
310 $config{SYSTEM} = lc $^O;
311 print "Checking operating system version... $config{SYSTEM}\n";
312
313 $exec = $config{CC} . " -dumpversion | cut -c 1";
314 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
315 $exec = $config{CC} . " -dumpversion | cut -c 3";
316 chomp($config{GCCMINOR}         = `$exec`);
317
318
319 print "Checking whether <stdint.h> exists... ";
320 if (test_header($config{CC}, "stdint.h")) {
321         $config{HAS_STDINT} = "true";
322         print "yes\n";
323 } else {
324         $config{HAS_STDINT} = "false";
325         print "no\n";
326 }
327
328 printf "Checking whether clock_gettime() exists... ";
329 if (test_file($config{CC}, "clock_gettime.cpp", "-lrt")) {
330         $config{HAS_CLOCK_GETTIME} = "true";
331         print "yes\n";
332 } else {
333         $config{HAS_CLOCK_GETTIME} = "false";
334         print "no\n";
335 }
336
337 printf "Checking whether eventfd() exists... ";
338 if (test_file($config{CC}, "eventfd.cpp")) {
339         $config{HAS_EVENTFD} = "true";
340         print "yes\n";
341 } else {
342         $config{HAS_EVENTFD} = "false";
343         print "no\n";
344 }
345
346 printf "Checking if a TCP deferring mechanism is available... ";
347 if (test_file($config{CC}, "tcp_defer_accept.cpp")) {
348         $config{HAS_DEFER} = "TCP_DEFER_ACCEPT";
349         print "yes (TCP_DEFER_ACCEPT)\n";
350 } elsif (test_file($config{CC}, "so_acceptfilter.cpp")) {
351         $config{HAS_DEFER} = "SO_ACCEPTFILTER";
352         print "yes (SO_ACCEPTFILTER)\n";
353 } else {
354         $config{HAS_DEFER} = "false";
355         print "no\n";
356 }
357
358 print "Checking whether epoll is available... ";
359 $has_epoll = test_header($config{CC}, "sys/epoll.h");
360 print $has_epoll ? "yes\n" : "no\n";
361
362 print "Checking whether Kqueue is available... ";
363 $has_kqueue = test_file($config{CC}, "kqueue.cpp");
364 print $has_kqueue ? "yes\n" : "no\n";
365
366 print 'Checking whether Solaris IOCP is available... ';
367 $has_ports = test_header($config{CC}, 'port.h');
368 print $has_ports ? "yes\n" : "no\n";
369
370 $config{HAS_EPOLL} = $has_epoll;
371 $config{HAS_KQUEUE} = $has_kqueue;
372
373 printf "Checking for libgnutls... ";
374 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
375         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
376                 print "yes\n";
377                 $config{HAS_GNUTLS} = "y";
378         } else {
379                 print "no\n";
380                 $config{HAS_GNUTLS} = "n";
381         }
382 } else {
383         print "no\n";
384         $config{HAS_GNUTLS} = "n";
385 }
386
387 printf "Checking for openssl... ";
388 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
389         if (defined($openssl_ver) && ($openssl_ver ne "")) {
390                 print "yes\n";
391                 $config{HAS_OPENSSL} = "y";
392         } else {
393                 print "no\n";
394                 $config{HAS_OPENSSL} = "n";
395         }
396 } else {
397         print "no\n";
398         $config{HAS_OPENSSL} = "n";
399 }
400
401 printf "Checking if you are running an ancient, unsupported OS... ";
402 if ($config{OSNAME} =~ /FreeBSD/i)
403 {
404         my $version = `uname -r`;
405         if ($version =~ /^4\./)
406         {
407                 print "yes.\n";
408                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
409                 print "To build, you will need to add the following to CXXFLAGS:\n";
410                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
411         }
412         else
413         {
414                 print "no ($version)\n";
415         }
416 }
417 else
418 {
419         print "no ($config{OSNAME})\n";
420 }
421
422 ################################################################################
423 #                         BEGIN INTERACTIVE PART                              #
424 ################################################################################
425
426 # Clear the Screen..
427 if ($interactive)
428 {
429         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
430         my $wholeos = $^O;
431
432         my $rev = getrevision();
433         # Display Introduction Message..
434         print <<"STOP" ;
435 Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
436 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
437
438 *** If you are unsure of any of these values, leave it blank for    ***
439 *** standard settings that will work, and your server will run      ***
440 *** using them. Please consult your IRC network admin if in doubt.  ***
441
442 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
443 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
444 dir, otherwise you won't have a config file!
445
446 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
447 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
448 STOP
449         if ($rev eq "r0") {
450                 print " (Non-SVN build)";
451         }
452         print ".\n\n";
453
454         $config{CHANGE_COMPILER} = "n";
455         print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
456
457         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
458                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
459 should NOT be used. You should probably specify a newer compiler.\n\n";
460                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
461                 if ($config{CHANGE_COMPILER} =~ /y/i) {
462                         print "What command do you want to use to invoke your compiler?\n";
463                         print "[\e[1;32m$config{CC}\e[0m] -> ";
464                         chomp($config{CC} = <STDIN>);
465                         if ($config{CC} eq "") {
466                                 $config{CC} = "g++";
467                         }
468                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
469                         if ($foo ne "") {
470                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
471                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
472                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
473                                 if ($config{GCCVER} < 4 || $config{GCCVER} == 4 && $config{GCCMINOR} < 1) {
474                                         print "\e[1;32mGCC 4.1 and earlier WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
475                                 }
476                         }
477                         else {
478                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
479                         }
480                 }
481         }
482
483         print "\n";
484
485         # Directory Settings..
486         my $tmpbase = $config{BASE_DIR};
487         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
488         if ($tmpbase ne $config{BASE_DIR}) {
489                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
490                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
491                 $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
492                 $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
493                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
494         }
495
496         dir_check("are the configuration files", "CONFIG_DIR");
497         dir_check("are the modules to be compiled to", "MODULE_DIR");
498         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
499         dir_check("are variable data files to be located in", "DATA_DIR");
500         dir_check("are the logs to be stored in", "LOG_DIR");
501         dir_check("do you want the build to take place", "BUILD_DIR");
502                 
503         my $chose_hiperf = 0;
504         if ($has_kqueue) {
505                 yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
506                 print "\n";
507                 if ($config{USE_KQUEUE} eq "y") {
508                         $chose_hiperf = 1;
509                 }
510         }
511         if ($has_epoll) {
512                 yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
513                 print "\n";
514                 if ($config{USE_EPOLL} eq "y") {
515                         $chose_hiperf = 1;
516                 }
517         }
518         if ($has_ports) {
519                 yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
520                 print "\n";
521                 if ($config{USE_PORTS} eq "y") {
522                         $chose_hiperf = 1;
523                 }
524         }
525
526         if (!$chose_hiperf) {
527                 yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
528                 if ($config{USE_POLL} ne "y")
529                 {
530                         print "No high-performance socket engines are available, or you chose\n";
531                         print "not to enable one. Defaulting to select() engine.\n\n";
532                 }
533         }
534
535         $config{USE_FREEBSD_BASE_SSL} = "n";
536         $config{USE_FREEBSD_PORTS_SSL} = "n";
537         if ($config{HAS_OPENSSL_PORT} ne "")
538         {
539                 $config{USE_FREEBSD_PORTS_SSL} = "y";
540                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
541                 print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
542                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
543                 print "\n";
544                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
545
546                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
547                 {
548                         # update to port version
549                         $openssl_ver = $config{HAS_OPENSSL_PORT};
550                 }
551         }
552         else
553         {
554                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
555         }
556
557         $config{USE_SSL} = "n";
558         $config{MODUPDATE} = 'n';
559
560         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
561         {
562                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
563                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
564
565                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
566                 if ($config{USE_SSL} eq "y")
567                 {
568                         if ($config{HAS_GNUTLS} eq "y")
569                         {
570                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
571                                 if ($config{USE_GNUTLS} eq "y")
572                                 {
573                                         print "\nUsing GnuTLS SSL module.\n";
574                                 }
575                         }
576
577                         if ($config{HAS_OPENSSL} eq "y")
578                         {
579                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
580                                 if ($config{USE_OPENSSL} eq "y")
581                                 {
582                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
583                                 }
584                         }
585                 }
586         }
587         else
588         {
589                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
590                 print "is in your path.\n\n";
591         }
592
593         yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
594         print "\n";
595         if ($config{MODUPDATE} eq "y") {
596                 print "Checking for upgrades to extra and third party modules... ";
597                 system "./modulemanager upgrade";
598         }
599 }
600
601 # We are on a POSIX system, we can enable POSIX extras without asking
602 symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
603
604 dumphash();
605
606 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
607 {
608         print "Sorry, but i couldn't detect gnutls. Make sure gnutls-config is in your path.\n";
609         exit(0);
610 }
611 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
612 {
613         print "Sorry, but i couldn't detect openssl. Make sure openssl is in your path.\n";
614         exit(0);
615 }
616 our $failed = 0;
617
618 $config{CERTGEN} ||= 'y';
619 yesno('CERTGEN',"Would you like generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
620
621 if ($config{USE_GNUTLS} eq "y") {
622         unless (-r "src/modules/m_ssl_gnutls.cpp") {
623                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
624                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
625         }
626         if ($interactive && $config{CERTGEN} eq 'y')
627         {
628                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
629                         print "SSL Certificates Not found, Generating.. \n\n
630 *************************************************************
631 * Generating the Private Key may take some time, go grab a  *
632 * Coffee. Even better, to generate some more entropy if it  *
633 * is taking a while, open another console and type du / a   *
634 * few times and get that HD going :) Then answer the        *
635 * Questions which follow. If you are unsure, just hit enter *
636 *************************************************************\n\n";
637                         $failed = system "./tools/genssl gnutls";
638                         if ($failed) {
639                                 print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
640                         } else {
641                                 print "\nCertificate generation complete, copying to config directory... ";
642                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
643                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
644                                 print "Done.\n\n";
645                         }
646                 }
647                 else {
648                         print "SSL Certificates found, skipping.\n\n";
649                 }
650         }
651         else
652         {
653                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
654         }
655 }
656
657 if ($config{USE_OPENSSL} eq "y") {
658         unless (-r "src/modules/m_ssl_openssl.cpp") {
659                 print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
660                 symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
661         }
662         $failed = 0;
663         if ($interactive && $config{CERTGEN} eq 'y')
664         {
665                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
666                         print "SSL Certificates Not found, Generating.. \n\n
667 *************************************************************
668 * Generating the certificates may take some time, go grab a *
669 * coffee, or something.                                     *
670 *************************************************************\n\n";
671                         system "./tools/genssl openssl";
672                         print "\nCertificate generation complete, copying to config directory... ";
673                         File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
674                         File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
675                         File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
676                         print "Done.\n\n";
677                 } else {
678                         print "SSL Certificates found, skipping.\n\n"
679                 }
680         }
681         else
682         {
683                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
684         }
685 }
686 if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
687         print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
688 }
689
690 depcheck();
691 writefiles(1);
692 makecache();
693
694 print "\n\n";
695 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
696 if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
697         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
698         print "modules in your config. This configure script has added those modules to the\n";
699         print "build process. For more info please refer to:\n";
700         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
701 }
702 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
703 if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
704         print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but i'm letting you\ngo ahead with it anyway, just in case i'm wrong :-)\n";
705 }
706
707 if ($config{GCCVER} < "3") {
708         print <<FOO2;
709 \e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
710 GCC 2.x series compilers only had partial (read as broken) C++ support, and
711 your compile will most likely fail horribly! If you have any problems, do NOT
712 report them to the bugtracker or forums without first upgrading your compiler
713 to a newer 3.x or 4.x (or whatever is available currently) version.
714 FOO2
715 }
716
717 ################################################################################
718 #                             HELPER FUNCTIONS                          #
719 ################################################################################
720 sub getcache {
721         # Retrieves the .config.cache file, and loads values into the main config hash.
722         open(CACHE, ".config.cache") or return 0;
723         while (<CACHE>) {
724                 chomp;
725                 # Ignore Blank lines, and comments..
726                 next if /^\s*$/;
727                 next if /^\s*#/;
728                 my ($key, $value) = split("=", $_, 2);
729                 $value =~ /^\"(.*)\"$/;
730                 # Do something with data here!
731                 $config{$key} = $1;
732         }
733         close(CACHE);
734         return 1;
735 }
736
737 sub makecache {
738         # Dump the contents of %config
739         print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
740         open(FILEHANDLE, ">.config.cache");
741         foreach my $key (keys %config) {
742                 print FILEHANDLE "$key=\"$config{$key}\"\n";
743         }
744         close(FILEHANDLE);
745 }
746
747 sub dir_check {
748         my ($desc, $hash_key) = @_;
749         my $complete = 0;
750         while (!$complete) {
751                 print "In what directory $desc?\n";
752                 print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
753                 chomp(my $var = <STDIN>);
754                 if ($var eq "") {
755                         $var = $config{$hash_key};
756                 }
757                 if ($var =~ /^\~\/(.+)$/) {
758                         # Convert it to a full path..
759                         $var = resolve_directory($ENV{HOME} . "/" . $1);
760                 }
761                 elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
762                 {
763                         # Assume relative Path was given.. fill in the rest.
764                         $var = $this . "/$var";
765                 }
766
767                 $var = resolve_directory($var);
768                 if (! -e $var) {
769                         print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
770                         chomp(my $tmp = <STDIN>);
771                         if (($tmp eq "") || ($tmp =~ /^y/i)) {
772                                 # Attempt to Create the Dir..
773                                 my $chk = eval {
774                                         use File::Path ();
775                                         File::Path::mkpath($var, 0, 0777);
776                                         1;
777                                 };
778                                 unless (defined($chk) && -d $var) {
779                                         print "Unable to create directory. ($var)\n\n";
780                                         # Restart Loop..
781                                         next;
782                                 }
783                         } else {
784                                 # They said they don't want to create, and we can't install there.
785                                 print "\n\n";
786                                 next;
787                         }
788                 } else {
789                         if (!is_dir($var)) {
790                                 # Target exists, but is not a directory.
791                                 print "File $var exists, but is not a directory.\n\n";
792                                 next;
793                         }
794                 }
795                 # Either Dir Exists, or was created fine.
796                 $config{$hash_key} = $var;
797                 $complete = 1;
798                 print "\n";
799         }
800 }
801
802 our $SHARED = "";
803
804 my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
805
806 sub writefiles {
807         my($writeheader) = @_;
808         # First File.. config.h
809         chomp(my $incos = `uname -n -s -r`);
810         chomp(my $version = `sh src/version.sh`);
811         chomp(my $revision2 = getrevision());
812         my $branch = "InspIRCd-0.0";
813         if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
814         {
815                 $branch = $1;
816         }
817         if ($writeheader == 1)
818         {
819                 print "Writing \e[1;32mconfig.h\e[0m\n";
820                 open(FILEHANDLE, ">include/config.h.tmp");
821                 print FILEHANDLE <<EOF;
822 /* Auto generated by configure, do not modify! */
823 #pragma once
824
825 #define BRANCH "$branch"
826 #define VERSION "$version"
827 #define REVISION "$revision2"
828 #define SYSTEM "$incos"
829
830 #define CONFIG_PATH "$config{CONFIG_DIR}"
831 #define DATA_PATH "$config{DATA_DIR}"
832 #define LOG_PATH "$config{LOG_DIR}"
833 #define MOD_PATH "$config{MODULE_DIR}"
834
835 EOF
836
837                 if ($config{OSNAME} =~ /SunOS/i) {
838                         print FILEHANDLE "#define IS_SOLARIS\n";
839                 }
840                 if ($config{OSNAME} =~ /MINGW32/i) {
841                         print FILEHANDLE "#define IS_MINGW\n";
842                 }
843                 if ($config{GCCVER} >= 3) {
844                         print FILEHANDLE "#define GCC3\n";
845                 }
846                 if ($config{HAS_STDINT} eq "true") {
847                         print FILEHANDLE "#define HAS_STDINT\n";
848                 }
849                 if ($config{HAS_EVENTFD} eq 'true') {
850                         print FILEHANDLE "#define HAS_EVENTFD\n";
851                 }
852                 if ($config{HAS_CLOCK_GETTIME} eq 'true') {
853                         print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
854                 }
855                 my $use_hiperf = 0;
856                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
857                         print FILEHANDLE "#define USE_KQUEUE\n";
858                         $config{SOCKETENGINE} = "socketengine_kqueue";
859                         $use_hiperf = 1;
860                 }
861                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
862                         print FILEHANDLE "#define USE_EPOLL\n";
863                         $config{SOCKETENGINE} = "socketengine_epoll";
864                         $use_hiperf = 1;
865                 }
866                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
867                         print FILEHANDLE "#define USE_PORTS\n";
868                         $config{SOCKETENGINE} = "socketengine_ports";
869                         $use_hiperf = 1;
870                 }
871                 # user didn't choose either epoll or select for their OS.
872                 # default them to USE_SELECT (ewwy puke puke)
873                 if (!$use_hiperf) {
874                         print "no hi-perf, " . $config{USE_POLL};
875                         if ($config{USE_POLL} eq "y")
876                         {
877                                 print FILEHANDLE "#define USE_POLL\n";
878                                 $config{SOCKETENGINE} = "socketengine_poll";
879                         }
880                         else
881                         {
882                                 print FILEHANDLE "#define USE_SELECT\n";
883                                 $config{SOCKETENGINE} = "socketengine_select";
884                         }
885                 }
886
887                 if ($config{HAS_DEFER} eq "TCP_DEFER_ACCEPT") {
888                         print FILEHANDLE "#define USE_TCP_DEFER_ACCEPT";
889                 } elsif ($config{HAS_DEFER} eq "SO_ACCEPTFILTER") {
890                         print FILEHANDLE "#define USE_SO_ACCEPTFILTER"
891                 }
892
893                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n";
894                 close(FILEHANDLE);
895                 
896                 my $file = 'include/config.h';
897                 my $diff = 0;
898                 open my $fh1, $file or $diff = 1;
899                 open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
900                 while (!$diff) {
901                         my $line1 = <$fh1>;
902                         my $line2 = <$fh2>;
903                         if (defined($line1) != defined($line2)) {
904                                 $diff = 1;
905                         } elsif (!defined $line1) {
906                                 last;
907                         } else {
908                                 $diff = ($line1 ne $line2);
909                         }
910                 }
911                 if ($diff) {
912                         unlink $file;
913                         rename "$file.tmp", $file;
914                 } else {
915                         unlink "$file.tmp";
916                 }
917         }
918
919         # Write all .in files.
920         my $tmp = "";
921         my $file = "";
922         my $exe = "inspircd";
923
924         # Do this once here, and cache it in the .*.inc files,
925         # rather than attempting to read src/version.sh from
926         # compiled code -- we might not have the source to hand.
927         # Fix for bug#177 by Brain.
928
929         chomp($version = `sh ./src/version.sh`);
930         chomp(my $revision = getrevision());
931         $version = "$version(r$revision)";
932
933         # We can actually parse any file starting with . and ending with .inc,
934         # but right now we only parse .inspircd.inc to form './inspircd'
935         prepare_dynamic_makefile();
936
937         my @dotfiles = qw(main.mk inspircd);
938         push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
939
940         foreach my $file (@dotfiles) {
941                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
942                 $_ = join '', <FILEHANDLE>;
943                 close(FILEHANDLE);
944
945                 $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
946
947                 for my $var (qw(
948                         CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
949                         STARTSCRIPT DESTINATION SOCKETENGINE
950                 )) {
951                         s/\@$var\@/$config{$var}/g;
952                 }
953                 s/\@EXECUTABLE\@/$exe/ if defined $exe;
954                 s/\@VERSION\@/$version/ if defined $version;
955
956                 if ($file eq 'main.mk') {
957                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
958
959                         my $mk_tmp = $_;
960                         s/\@IFDEF (\S+)/ifdef $1/g;
961                         s/\@IFNDEF (\S+)/ifndef $1/g;
962                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
963                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
964                         s/\@ELSE/else/g;
965                         s/\@ENDIF/endif/g;
966                         s/ *\@BSD_ONLY .*\n//g;
967                         s/\@GNU_ONLY //g;
968                         s/\@DO_EXPORT (.*)/export $1/g;
969                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
970                         print MKF $_;
971                         close MKF;
972
973                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
974                         $_ = $mk_tmp;
975                         s/\@IFDEF (\S+)/.if defined($1)/g;
976                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
977                         s/\@IFEQ (\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") {
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 }