]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
Switch strlcpy test to use a test file too.
[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} = "true";
357 our $fail = 0;
358 open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
359 if ($config{HAS_STDINT} eq "true") {
360         close(STDINT);
361 }
362 print "yes\n" if $config{HAS_STDINT} eq "true";
363 print "no\n" if $config{HAS_STDINT} eq "false";
364
365 printf "Checking if strlcpy exists... ";
366 $config{HAS_STRLCPY} = test_compile('strlcpy');
367 print $config{HAS_STRLCPY} ? "yes\n" : "no\n";
368
369 printf "Checking if kqueue exists... ";
370 $has_kqueue = test_compile('kqueue');
371 print $has_kqueue ? "yes\n" : "no\n";
372
373 printf "Checking for epoll support... ";
374 $has_epoll = test_compile('epoll');
375 print $has_epoll ? "yes\n" : "no\n";
376
377 printf "Checking for eventfd support... ";
378 $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
379 print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
380
381 printf "Checking if Solaris I/O completion ports are available... ";
382 $has_ports = 0;
383 our $system = `uname -s`;
384 chomp ($system);
385 $has_ports = 1 if ($system eq "SunOS");
386
387 if ($has_ports) {
388         my $kernel = `uname -r`;
389         chomp($kernel);
390         if (($kernel !~ /^5\.1./)) {
391                 $has_ports = 0;
392         }
393 }
394 print "yes\n" if $has_ports == 1;
395 print "no\n" if $has_ports == 0;
396
397 $config{HAS_EPOLL} = $has_epoll;
398 $config{HAS_KQUEUE} = $has_kqueue;
399
400 printf "Checking for libgnutls... ";
401 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
402         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
403                 print "yes\n";
404                 $config{HAS_GNUTLS} = "y";
405         } else {
406                 print "no\n";
407                 $config{HAS_GNUTLS} = "n";
408         }
409 } else {
410         print "no\n";
411         $config{HAS_GNUTLS} = "n";
412 }
413
414 printf "Checking for openssl... ";
415 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
416         if (defined($openssl_ver) && ($openssl_ver ne "")) {
417                 print "yes\n";
418                 $config{HAS_OPENSSL} = "y";
419         } else {
420                 print "no\n";
421                 $config{HAS_OPENSSL} = "n";
422         }
423 } else {
424         print "no\n";
425         $config{HAS_OPENSSL} = "n";
426 }
427
428 printf "Checking if you are running an ancient, unsupported OS... ";
429 if ($config{OSNAME} =~ /FreeBSD/i)
430 {
431         my $version = `uname -r`;
432         if ($version =~ /^4\./)
433         {
434                 print "yes.\n";
435                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
436                 print "To build, you will need to add the following to CXXFLAGS:\n";
437                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
438         }
439         else
440         {
441                 print "no ($version)\n";
442         }
443 }
444 else
445 {
446         print "no ($config{OSNAME})\n";
447 }
448
449 ################################################################################
450 #                         BEGIN INTERACTIVE PART                              #
451 ################################################################################
452
453 # Clear the Screen..
454 if ($interactive)
455 {
456         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
457         my $wholeos = $^O;
458
459         my $rev = getrevision();
460         # Display Introduction Message..
461         print <<"STOP" ;
462 Welcome to the \e[1mInspIRCd\e[0m configuration program! (\e[1minteractive mode\e[0m)
463 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
464
465 *** If you are unsure of any of these values, leave it blank for    ***
466 *** standard settings that will work, and your server will run      ***
467 *** using them. Please consult your IRC network admin if in doubt.  ***
468
469 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
470 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
471 dir, otherwise you won't have a config file!
472
473 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
474 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
475 STOP
476         if ($rev eq "r0") {
477                 print " (Non-SVN build)";
478         }
479         print ".\n\n";
480
481         $config{CHANGE_COMPILER} = "n";
482         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";
483
484         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
485                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
486 should NOT be used. You should probably specify a newer compiler.\n\n";
487                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
488                 if ($config{CHANGE_COMPILER} =~ /y/i) {
489                         print "What command do you want to use to invoke your compiler?\n";
490                         print "[\e[1;32m$config{CC}\e[0m] -> ";
491                         chomp($config{CC} = <STDIN>);
492                         if ($config{CC} eq "") {
493                                 $config{CC} = "g++";
494                         }
495                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
496                         if ($foo ne "") {
497                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
498                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
499                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
500                                 if ($config{GCCVER} < 3) {
501                                         print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
502                                 }
503                         }
504                         else {
505                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
506                         }
507                 }
508         }
509
510         print "\n";
511
512         # Directory Settings..
513         my $tmpbase = $config{BASE_DIR};
514         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
515         if ($tmpbase ne $config{BASE_DIR}) {
516                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
517                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
518                 $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
519                 $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
520                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
521         }
522
523         dir_check("are the configuration files", "CONFIG_DIR");
524         dir_check("are the modules to be compiled to", "MODULE_DIR");
525         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
526         dir_check("are variable data files to be located in", "DATA_DIR");
527         dir_check("are the logs to be stored in", "LOG_DIR");
528         dir_check("do you want the build to take place", "BUILD_DIR");
529                 
530         my $chose_hiperf = 0;
531         if ($has_kqueue) {
532                 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?");
533                 print "\n";
534                 if ($config{USE_KQUEUE} eq "y") {
535                         $chose_hiperf = 1;
536                 }
537         }
538         if ($has_epoll) {
539                 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?");
540                 print "\n";
541                 if ($config{USE_EPOLL} eq "y") {
542                         $chose_hiperf = 1;
543                 }
544         }
545         if ($has_ports) {
546                 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?");
547                 print "\n";
548                 if ($config{USE_PORTS} eq "y") {
549                         $chose_hiperf = 1;
550                 }
551         }
552
553         if (!$chose_hiperf) {
554                 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?");
555                 if ($config{USE_POLL} ne "y")
556                 {
557                         print "No high-performance socket engines are available, or you chose\n";
558                         print "not to enable one. Defaulting to select() engine.\n\n";
559                 }
560         }
561
562         $config{USE_FREEBSD_BASE_SSL} = "n";
563         $config{USE_FREEBSD_PORTS_SSL} = "n";
564         if ($config{HAS_OPENSSL_PORT} ne "")
565         {
566                 $config{USE_FREEBSD_PORTS_SSL} = "y";
567                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
568                 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";
569                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
570                 print "\n";
571                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
572
573                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
574                 {
575                         # update to port version
576                         $openssl_ver = $config{HAS_OPENSSL_PORT};
577                 }
578         }
579         else
580         {
581                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
582         }
583
584         $config{USE_SSL} ||= "n";
585         $config{MODUPDATE} ||= 'n';
586
587         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
588         {
589                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
590                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
591
592                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
593                 if ($config{USE_SSL} eq "y")
594                 {
595                         if ($config{HAS_GNUTLS} eq "y")
596                         {
597                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
598                                 if ($config{USE_GNUTLS} eq "y")
599                                 {
600                                         print "\nUsing GnuTLS SSL module.\n";
601                                 }
602                         }
603
604                         if ($config{HAS_OPENSSL} eq "y")
605                         {
606                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
607                                 if ($config{USE_OPENSSL} eq "y")
608                                 {
609                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
610                                 }
611                         }
612                 }
613         }
614         else
615         {
616                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
617                 print "is in your path.\n\n";
618         }
619
620         yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
621         print "\n";
622         if ($config{MODUPDATE} eq "y") {
623                 print "Checking for upgrades to extra and third-party modules... ";
624                 system "./modulemanager upgrade";
625         }
626 }
627
628 # We are on a POSIX system, we can enable POSIX extras without asking
629 symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
630
631 dumphash();
632
633 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
634 {
635         print "Sorry, but I couldn't detect GnuTLS. Make sure pkg-config is in your path.\n";
636         exit(0);
637 }
638 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
639 {
640         print "Sorry, but I couldn't detect OpenSSL. Make sure pkg-config and openssl are in your path.\n";
641         exit(0);
642 }
643 our $failed = 0;
644
645 $config{CERTGEN} ||= 'y';
646 yesno('CERTGEN',"Would you like to generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
647
648 if ($config{USE_GNUTLS} eq "y") {
649         unless (-r "src/modules/m_ssl_gnutls.cpp") {
650                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
651                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
652         }
653         if ($interactive && $config{CERTGEN} eq 'y')
654         {
655                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
656                         print "SSL certificates not found, generating.. \n\n
657 *************************************************************
658 * Generating the private key may take some time, once done, *
659 * answer the questions which follow. If you are unsure,     *
660 * just hit enter!                                           *
661 *************************************************************\n\n";
662                         $failed = make_gnutls_cert();
663                         if ($failed) {
664                                 print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
665                         } else {
666                                 print "\nCertificate generation complete, copying to config directory... ";
667                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
668                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
669                                 print "Done.\n\n";
670                         }
671                 }
672                 else {
673                         print "SSL certificates found, skipping.\n\n";
674                 }
675         }
676         else
677         {
678                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
679         }
680 }
681
682 if ($config{USE_OPENSSL} eq "y") {
683         unless (-r "src/modules/m_ssl_openssl.cpp") {
684                 print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
685                 symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
686         }
687         $failed = 0;
688         if ($interactive && $config{CERTGEN} eq 'y')
689         {
690                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
691                         print "SSL certificates not found, generating.. \n\n
692 *************************************************************
693 * Generating the certificates may take some time, go grab a *
694 * coffee or something.                                      *
695 *************************************************************\n\n";
696                         make_openssl_cert();
697                         print "\nCertificate generation complete, copying to config directory... ";
698                         File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
699                         File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
700                         File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
701                         print "Done.\n\n";
702                 } else {
703                         print "SSL certificates found, skipping.\n\n"
704                 }
705         }
706         else
707         {
708                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
709         }
710 }
711 if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
712         print "Skipping SSL certificate generation as SSL support is not available.\n\n";
713 }
714
715 depcheck();
716 writefiles(1);
717 makecache();
718
719 print "\n\n";
720 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
721 if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
722         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
723         print "modules in your config. This configure script has added those modules to the\n";
724         print "build process. For more info, please refer to:\n";
725         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
726 }
727 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
728 if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
729         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";
730 }
731
732 if ($config{GCCVER} < "3") {
733         print <<FOO2;
734 \e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
735 GCC 2.x series compilers only had partial (read as broken) C++ support, and
736 your compile will most likely fail horribly! If you have any problems, do NOT
737 report them to the bugtracker or forums without first upgrading your compiler
738 to a newer 3.x or 4.x (or whatever is available currently) version.
739 FOO2
740 }
741
742 ################################################################################
743 #                             HELPER FUNCTIONS                          #
744 ################################################################################
745 sub getcache {
746         # Retrieves the .config.cache file, and loads values into the main config hash.
747         open(CACHE, ".config.cache") or return 0;
748         while (<CACHE>) {
749                 chomp;
750                 # Ignore Blank lines, and comments..
751                 next if /^\s*$/;
752                 next if /^\s*#/;
753                 my ($key, $value) = split("=", $_, 2);
754                 $value =~ /^\"(.*)\"$/;
755                 # Do something with data here!
756                 $config{$key} = $1;
757         }
758         close(CACHE);
759         return 1;
760 }
761
762 sub makecache {
763         # Dump the contents of %config
764         print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
765         open(FILEHANDLE, ">.config.cache");
766         foreach my $key (keys %config) {
767                 print FILEHANDLE "$key=\"$config{$key}\"\n";
768         }
769         close(FILEHANDLE);
770 }
771
772 sub dir_check {
773         my ($desc, $hash_key) = @_;
774         my $complete = 0;
775         while (!$complete) {
776                 print "In what directory $desc?\n";
777                 print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
778                 chomp(my $var = <STDIN>);
779                 if ($var eq "") {
780                         $var = $config{$hash_key};
781                 }
782                 if ($var =~ /^\~\/(.+)$/) {
783                         # Convert it to a full path..
784                         $var = resolve_directory($ENV{HOME} . "/" . $1);
785                 }
786                 elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
787                 {
788                         # Assume relative Path was given.. fill in the rest.
789                         $var = $this . "/$var";
790                 }
791
792                 $var = resolve_directory($var);
793                 if (! -e $var) {
794                         print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
795                         chomp(my $tmp = <STDIN>);
796                         if (($tmp eq "") || ($tmp =~ /^y/i)) {
797                                 # Attempt to Create the Dir..
798                                 my $chk = eval {
799                                         use File::Path ();
800                                         File::Path::mkpath($var, 0, 0777);
801                                         1;
802                                 };
803                                 unless (defined($chk) && -d $var) {
804                                         print "Unable to create directory. ($var)\n\n";
805                                         # Restart Loop..
806                                         next;
807                                 }
808                         } else {
809                                 # They said they don't want to create, and we can't install there.
810                                 print "\n\n";
811                                 next;
812                         }
813                 } else {
814                         if (!is_dir($var)) {
815                                 # Target exists, but is not a directory.
816                                 print "File $var exists, but is not a directory.\n\n";
817                                 next;
818                         }
819                 }
820                 # Either Dir Exists, or was created fine.
821                 $config{$hash_key} = $var;
822                 $complete = 1;
823                 print "\n";
824         }
825 }
826
827 our $SHARED = "";
828
829 my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
830
831 sub writefiles {
832         my($writeheader) = @_;
833         # First File.. inspircd_config.h
834         chomp(my $incos = `uname -n -s -r`);
835         chomp(my $version = `sh src/version.sh`);
836         chomp(my $revision2 = getrevision());
837         my $branch = "InspIRCd-0.0";
838         if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
839         {
840                 $branch = $1;
841         }
842         if ($writeheader == 1)
843         {
844                 print "Writing \e[1;32minspircd_config.h\e[0m\n";
845                 open(FILEHANDLE, ">include/inspircd_config.h.tmp");
846                 print FILEHANDLE <<EOF;
847 /* Auto generated by configure, do not modify! */
848 #ifndef __CONFIGURATION_AUTO__
849 #define __CONFIGURATION_AUTO__
850
851 /* this is for windows support. */
852 #define CoreExport /**/
853 #define DllExport /**/
854
855 #define CONFIG_PATH "$config{CONFIG_DIR}"
856 #define DATA_PATH "$config{DATA_DIR}"
857 #define LOG_PATH "$config{LOG_DIR}"
858 #define MOD_PATH "$config{MODULE_DIR}"
859 #define SOMAXCONN_S "$config{_SOMAXCONN}"
860 #define ENTRYPOINT int main(int argc, char** argv)
861
862 EOF
863 print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
864
865                 if ($config{OSNAME} =~ /SunOS/i) {
866                         print FILEHANDLE "#define IS_SOLARIS\n";
867                 }
868                 if ($config{OSNAME} =~ /MINGW32/i) {
869                         print FILEHANDLE "#define IS_MINGW\n";
870                 }
871                 if ($config{GCCVER} >= 3) {
872                         print FILEHANDLE "#define GCC3\n";
873                 }
874                 if ($config{HAS_STRLCPY} eq "true") {
875                         print FILEHANDLE "#define HAS_STRLCPY\n";
876                 }
877                 if ($config{HAS_STDINT} eq "true") {
878                         print FILEHANDLE "#define HAS_STDINT\n";
879                 }
880                 if ($config{HAS_EVENTFD} eq 'true') {
881                         print FILEHANDLE "#define HAS_EVENTFD\n";
882                 }
883                 if ($config{OSNAME} !~ /DARWIN/i) {
884                         print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
885                 }
886                 my $use_hiperf = 0;
887                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
888                         print FILEHANDLE "#define USE_KQUEUE\n";
889                         $config{SOCKETENGINE} = "socketengine_kqueue";
890                         $use_hiperf = 1;
891                 }
892                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
893                         print FILEHANDLE "#define USE_EPOLL\n";
894                         $config{SOCKETENGINE} = "socketengine_epoll";
895                         $use_hiperf = 1;
896                 }
897                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
898                         print FILEHANDLE "#define USE_PORTS\n";
899                         $config{SOCKETENGINE} = "socketengine_ports";
900                         $use_hiperf = 1;
901                 }
902                 # user didn't choose either epoll or select for their OS.
903                 # default them to USE_SELECT (ewwy puke puke)
904                 if (!$use_hiperf) {
905                         print "no hi-perf, " . $config{USE_POLL};
906                         if ($config{USE_POLL} eq "y")
907                         {
908                                 print FILEHANDLE "#define USE_POLL\n";
909                                 $config{SOCKETENGINE} = "socketengine_poll";
910                         }
911                         else
912                         {
913                                 print FILEHANDLE "#define USE_SELECT\n";
914                                 $config{SOCKETENGINE} = "socketengine_select";
915                         }
916                 }
917                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
918                 close(FILEHANDLE);
919
920                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
921                 print FILEHANDLE <<EOF;
922 #define BRANCH "$branch"
923 #define VERSION "$version"
924 #define REVISION "$revision2"
925 #define SYSTEM "$incos"
926 EOF
927                 close FILEHANDLE;
928
929                 for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
930                         my $diff = 0;
931                         open my $fh1, $file or $diff = 1;
932                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
933                         while (!$diff) {
934                                 my $line1 = <$fh1>;
935                                 my $line2 = <$fh2>;
936                                 if (defined($line1) != defined($line2)) {
937                                         $diff = 1;
938                                 } elsif (!defined $line1) {
939                                         last;
940                                 } else {
941                                         $diff = ($line1 ne $line2);
942                                 }
943                         }
944                         if ($diff) {
945                                 unlink $file;
946                                 rename "$file.tmp", $file;
947                         } else {
948                                 unlink "$file.tmp";
949                         }
950                 }
951         }
952
953         # Write all .in files.
954         my $tmp = "";
955         my $file = "";
956         my $exe = "inspircd";
957
958         # Do this once here, and cache it in the .*.inc files,
959         # rather than attempting to read src/version.sh from
960         # compiled code -- we might not have the source to hand.
961         # Fix for bug#177 by Brain.
962
963         chomp($version = `sh ./src/version.sh`);
964         chomp(my $revision = getrevision());
965         $version = "$version(r$revision)";
966
967         # We can actually parse any file starting with . and ending with .inc,
968         # but right now we only parse .inspircd.inc to form './inspircd'
969         prepare_dynamic_makefile();
970
971         my @dotfiles = qw(main.mk inspircd);
972         push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
973
974         foreach my $file (@dotfiles) {
975                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
976                 $_ = join '', <FILEHANDLE>;
977                 close(FILEHANDLE);
978
979                 $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
980
981                 for my $var (qw(
982                         CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
983                         STARTSCRIPT DESTINATION SOCKETENGINE LOG_DIR
984                 )) {
985                         s/\@$var\@/$config{$var}/g;
986                 }
987                 s/\@EXECUTABLE\@/$exe/ if defined $exe;
988                 s/\@VERSION\@/$version/ if defined $version;
989
990                 if ($file eq 'main.mk') {
991                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
992
993                         my $mk_tmp = $_;
994                         s/\@IFDEF (\S+)/ifdef $1/g;
995                         s/\@IFNDEF (\S+)/ifndef $1/g;
996                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
997                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
998                         s/\@ELSE/else/g;
999                         s/\@ENDIF/endif/g;
1000                         s/ *\@BSD_ONLY .*\n//g;
1001                         s/\@GNU_ONLY //g;
1002                         s/\@DO_EXPORT (.*)/export $1/g;
1003                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1004                         print MKF $_;
1005                         close MKF;
1006
1007                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
1008                         $_ = $mk_tmp;
1009                         s/\@IFDEF (\S+)/.if defined($1)/g;
1010                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
1011                         s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1012                         s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1013                         s/\@ELSE/.else/g;
1014                         s/\@ENDIF/.endif/g;
1015                         s/\@BSD_ONLY //g;
1016                         s/ *\@GNU_ONLY .*\n//g;
1017                         $mk_tmp = $_;
1018                         $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1019                         open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1020                         print MKF $mk_tmp;
1021                         close MKF;
1022                 } else {
1023                         print "Writing \e[1;32m$file\e[0m ...\n";
1024                         open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1025                         print FILEHANDLE $_;
1026                         close(FILEHANDLE);
1027                 }
1028         }
1029
1030         chmod 0755, 'inspircd';
1031 }
1032
1033 sub depcheck
1034 {
1035         getmodules();
1036         for my $mod (@modlist) {
1037                 getcompilerflags("src/modules/m_$mod.cpp");
1038                 getlinkerflags("src/modules/m_$mod.cpp");
1039         }
1040 }
1041
1042 sub prepare_dynamic_makefile
1043 {
1044         my $i = 0;
1045
1046         if (!$has_epoll)
1047         {
1048                 $config{USE_EPOLL} = 0;
1049         }
1050         if (!$has_kqueue)
1051         {
1052                 $config{USE_KQUEUE} = 0;
1053         }
1054         if (!$has_ports)
1055         {
1056                 $config{USE_PORTS} = 0;
1057         }
1058 }
1059
1060 # Routine to list out the extra/ modules that have been enabled.
1061 # Note: when getting any filenames out and comparing, it's important to lc it if the
1062 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1063 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1064 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1065 sub list_extras () {
1066         use File::Spec;
1067         # @_ not used
1068         my $srcdir = File::Spec->catdir("src", "modules");
1069         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1070         local $_;
1071         my $dd;
1072         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1073         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1074         closedir $dd;
1075         undef $dd;
1076         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1077         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1078         closedir $dd;
1079         undef $dd;
1080         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1081         my %extras = ();
1082 EXTRA:  for my $extra (@extras) {
1083                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1084                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1085                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1086                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1087                 if (-l $abs_source) {
1088                         # Symlink, is it in the right place?
1089                         my $targ = readlink($abs_source);
1090                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1091                         if ($abs_targ eq $abs_extra) {
1092                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1093                         } else {
1094                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1095                         }
1096                 } elsif (-e $abs_source) {
1097                         my ($devext, $inoext) = stat($abs_extra);
1098                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1099                         if ($lnksrc > 1) {
1100                                 if ($devsrc == $devext && $inosrc == $inoext) {
1101                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1102                                 } else {
1103                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1104                                 }
1105                         } else {
1106                                 open my $extfd, "<", $abs_extra;
1107                                 open my $srcfd, "<", $abs_source;
1108                                 local $/ = undef;
1109                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1110                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1111                                 } else {
1112                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1113                                 }
1114                         }
1115                 } else {
1116                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1117                 }
1118         }
1119         # Now let's add dependency info
1120         for my $extra (keys(%extras)) {
1121                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1122                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1123                 my @deps = split / +/, getdependencies($abs_extra);
1124                 for my $dep (@deps) {
1125                         if (exists($extras{$dep})) {
1126                                 my $ref = \$extras{$dep}; # Take reference.
1127                                 if ($$ref !~ m/needed by/) {
1128                                         # First dependency found.
1129                                         if ($$ref =~ m/enabled/) {
1130                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1131                                         } else {
1132                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1133                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1134                                         }
1135                                 } else {
1136                                         if ($$ref =~ m/enabled/) {
1137                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1138                                         } else {
1139                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1140                                         }
1141                                 }
1142                         }
1143                 }
1144         }
1145         for my $extra (sort {$a cmp $b} keys(%extras)) {
1146                 my $text = $extras{$extra};
1147                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1148                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1149                 } else {
1150                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1151                 }
1152         }
1153         return keys(%extras) if wantarray; # Can be used by manage_extras.
1154 }
1155
1156 sub enable_extras (@) {
1157         my (@extras) = @_;
1158         for my $extra (@extras) {
1159                 my $extrapath = "src/modules/extra/$extra";
1160                 if (!-e $extrapath) {
1161                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1162                         next;
1163                 }
1164                 my $source = "src/modules/$extra";
1165                 if (-e $source) {
1166                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1167                         next;
1168                 }
1169                 # Get dependencies, and add them to be processed.
1170                 my @deps = split / +/, getdependencies($extrapath);
1171                 for my $dep (@deps) {
1172                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1173                         if (!-e "src/modules/$dep") {
1174                                 if (-e "src/modules/extra/$dep") {
1175                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1176                                         push @extras, $dep;
1177                                 } else {
1178                                         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";
1179                                 }
1180                         }
1181                 }
1182                 print "Enabling $extra ... \n";
1183                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1184         }
1185 }
1186
1187 sub disable_extras (@)
1188 {
1189         opendir my $dd, "src/modules/extra/";
1190         my @files = readdir($dd);
1191         closedir $dd;
1192         my (@extras) = @_;
1193 EXTRA:  for my $extra (@extras) {
1194                 my $extrapath = "src/modules/extra/$extra";
1195                 my $source = "src/modules/$extra";
1196                 if (!-e $extrapath) {
1197                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1198                         next;
1199                 }
1200                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1201                         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";
1202                         next;
1203                 }
1204                 # Check if anything needs this.
1205                 for my $file (@files) {
1206                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1207                         # File depends on this extra...
1208                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1209                                 # And is both enabled and not about to be disabled.
1210                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1211                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1212                                         next EXTRA;
1213                                 }
1214                         }
1215                 }
1216                 # Now remove.
1217                 print "Disabling $extra ... \n";
1218                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1219         }
1220 }