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