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