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