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