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