]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
m_filter Remove redundant flags field from FilterResult
[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 $config{ME} = resolve_directory($topdir);                               # Present Working Directory
149
150 $config{BASE_DIR} = $config{ME}."/run";
151
152 if (defined $opt_base_dir) {
153         $config{BASE_DIR} = $opt_base_dir;
154 } elsif (defined $opt_system || defined $opt_uid) {
155         $config{BASE_DIR} = '/var/lib/inspircd';
156 }
157
158 if (defined $opt_system || defined $opt_uid) {
159         $config{UID} = $opt_uid || 'ircd';
160         $config{CONFIG_DIR}      = '/etc/inspircd';
161         $config{MODULE_DIR}      = '/usr/lib/inspircd';
162         $config{BINARY_DIR}      = '/usr/sbin/';
163         $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
164         $config{DATA_DIR}        = '/var/inspircd';
165         $config{LOG_DIR}         = '/var/log/inspircd';
166 } else {
167         $config{UID} = $<;
168         $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");        # Configuration Directory
169         $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
170         $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");         # Binary Directory
171         $config{BUILD_DIR}       = resolve_directory($config{ME}."/build");         # Build Directory
172         $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data directory
173         $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log directory
174 }
175
176 if (defined $opt_config_dir) {
177         $config{CONFIG_DIR} = $opt_config_dir;
178 }
179 if (defined $opt_module_dir) {
180         $config{MODULE_DIR} = $opt_module_dir;
181 }
182 if (defined $opt_binary_dir) {
183         $config{BINARY_DIR} = $opt_binary_dir;
184 }
185 if (defined $opt_data_dir) {
186         $config{DATA_DIR} = $opt_data_dir;
187 }
188 if (defined $opt_log_dir) {
189         $config{LOG_DIR} = $opt_log_dir;
190 }
191 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version.
192
193 if (defined $opt_freebsd_port)
194 {
195         chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
196         chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
197         $config{USE_FREEBSD_BASE_SSL} = "n";
198 }
199 else
200 {
201         if ($^O eq "freebsd")
202         {
203                 # default: use base ssl
204                 chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
205                 chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
206         }
207         else
208         {
209                 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
210                 $config{HAS_OPENSSL_PORT} = "";
211         }
212 }
213
214 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
215 chomp(our $openssl_ver = $config{HAS_OPENSSL});
216 $config{USE_GNUTLS}         = "n";
217 if (defined $opt_use_gnutls)
218 {
219         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
220 }
221 $config{USE_OPENSSL}    = "n";                                          # Use openssl.
222 if (defined $opt_use_openssl)
223 {
224         $config{USE_OPENSSL} = "y";
225 }
226
227 if (!defined $opt_disable_debug) {
228         $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
229 } else {
230         $config{OPTIMISATI}      = "-O2";
231 }
232
233 $config{HAS_STRLCPY}    = "false";                                      # strlcpy Check.
234 $config{HAS_STDINT}      = "false";                                     # stdint.h check
235 $config{USE_KQUEUE}      = "y";                                         # kqueue enabled
236 if (defined $opt_nokqueue) {
237         $config{USE_KQUEUE} = "n";
238 }
239 $config{USE_POLL}     = "y";                                    # poll enabled
240 $config{USE_EPOLL}        = "y";                                        # epoll enabled
241 if (defined $opt_noepoll)
242 {
243         $config{USE_EPOLL} = "n";
244 }
245 $config{USE_PORTS}        = "y";                                        # epoll enabled
246 if (defined $opt_noports)
247 {
248         $config{USE_PORTS} = "n";
249 }
250 $config{_SOMAXCONN} = SOMAXCONN;                                        # Max connections in accept queue
251 $config{OSNAME}             = $^O;                                      # Operating System Name
252 $config{IS_DARWIN}        = "NO";                                       # Is OSX?
253 $config{STARTSCRIPT}      = "inspircd";                 # start script?
254 $config{DESTINATION}      = "BASE";                             # Is target path.
255 if ($config{OSNAME} =~ /darwin/i)
256 {
257         $config{IS_DARWIN} = "YES";
258         $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
259         $config{CC}                 = "xcrun clang++";                                  # C++ compiler for OSX.
260 }
261 else
262 {
263         $config{CC}                 = "g++";                                            # C++ compiler
264 }
265 if (defined $opt_cc)
266 {
267         $config{CC} = $opt_cc;
268 }
269 our $exec = $config{CC} . " -dumpversion | cut -c 1";
270 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
271 $exec = $config{CC} . " -dumpversion | cut -c 3";
272 chomp($config{GCCMINOR}         = `$exec`);
273 $config{MAXBUF}                 = "512";                                # Max buffer size
274
275 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/) {
276         $config{HAS_OPENSSL} = $1;
277 } else {
278         $config{HAS_OPENSSL} = "";
279 }
280
281 if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
282         if ($config{IS_DARWIN} eq "YES") {
283                 print $config{CC} . " was not found! You require clang++ (the LLVM C++ compiler, part of the OSX developer tools) to build InspIRCd!\n";
284         } else {
285                 print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";         
286         }
287         exit;
288 }
289
290 # Get and Set some important vars..
291 getmodules();
292
293 sub clean
294 {
295         unlink(".config.cache");
296 }
297
298 our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
299
300 sub update
301 {
302         eval {
303                 chomp($topdir = getcwd());
304                 $this = resolve_directory($topdir);                                          # PWD, Regardless.
305                 getmodules();
306                 # Does the cache file exist?
307                 if (!getcache()) {
308                         # No, No it doesn't.. *BASH*
309                         print "You have not run ./configure before. Please do this before trying to run the update script.\n";
310                         exit 0;
311                 } else {
312                         # We've Loaded the cache file and all our variables..
313                         print "Updating files...\n";
314                         if (defined($opt_disable_debug) && $opt_disable_debug == 1)
315                         {
316                                 print "Disabling debug information (-g).\n";
317                                 $config{OPTIMISATI} = "";
318                         }
319                         $has_epoll = $config{HAS_EPOLL};
320                         $has_ports = $config{HAS_PORTS};
321                         $has_kqueue = $config{HAS_KQUEUE};
322                         writefiles(1);
323                         makecache();
324                         print "Complete.\n";
325                         exit;
326                 }
327         };
328         if ($@)
329         {
330                 print "Configure update failed: $@\n";
331         }
332         exit;
333 }
334
335
336 sub test_compile {
337         my $feature = shift;
338         my $fail = 0;
339         $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
340         $fail ||= system "./test_$feature";
341         unlink "test_$feature";
342         return !$fail;
343 }
344
345 print "Running non-interactive configure...\n" unless $interactive;
346 print "Checking for cache from previous configure... ";
347 print ((!getcache()) ? "not found\n" : "found\n");
348 $config{SYSTEM} = lc $^O;
349 print "Checking operating system version... $config{SYSTEM}\n";
350
351 $exec = $config{CC} . " -dumpversion | cut -c 1";
352 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
353 $exec = $config{CC} . " -dumpversion | cut -c 3";
354 chomp($config{GCCMINOR}         = `$exec`);
355
356 printf "Checking if stdint.h exists... ";
357 $config{HAS_STDINT} = "true";
358 our $fail = 0;
359 open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
360 if ($config{HAS_STDINT} eq "true") {
361         close(STDINT);
362 }
363 print "yes\n" if $config{HAS_STDINT} eq "true";
364 print "no\n" if $config{HAS_STDINT} eq "false";
365
366 printf "Checking if strlcpy exists... ";
367 # Perform the strlcpy() test..
368 $config{HAS_STRLCPY} = "false";
369 $fail = 0;
370 open(STRLCPY, "</usr/include/string.h") or $fail = 1;
371 if (!$fail) {
372         while (defined(my $line = <STRLCPY>)) {
373                 chomp($line);
374                 # try and find the delcaration of:
375                 # size_t strlcpy(...)
376                 if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
377                         $config{HAS_STRLCPY} = "true";
378                 }
379         }
380         close(STRLCPY);
381 }
382 print "yes\n" if $config{HAS_STRLCPY} eq "true";
383 print "no\n" if $config{HAS_STRLCPY} eq "false";
384
385 printf "Checking if kqueue exists... ";
386 $has_kqueue = 0;
387 $fail = 0;
388 open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
389 if (!$fail) {
390         while (defined(my $line = <KQUEUE>)) {
391                 chomp($line);
392                 # try and find the delcaration of:
393                 # int kqueue(void);
394                 if ($line =~ /int(\0x9|\s)+kqueue/) {
395                         $has_kqueue = 1;
396                 }
397         }
398         close(KQUEUE);
399 }
400 print "yes\n" if $has_kqueue == 1;
401 print "no\n" if $has_kqueue == 0;
402
403 printf "Checking for epoll support... ";
404 $has_epoll = test_compile('epoll');
405 print $has_epoll ? "yes\n" : "no\n";
406
407 printf "Checking for eventfd support... ";
408 $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
409 print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
410
411 printf "Checking if Solaris I/O completion ports are available... ";
412 $has_ports = 0;
413 our $system = `uname -s`;
414 chomp ($system);
415 $has_ports = 1 if ($system eq "SunOS");
416
417 if ($has_ports) {
418         my $kernel = `uname -r`;
419         chomp($kernel);
420         if (($kernel !~ /^5\.1./)) {
421                 $has_ports = 0;
422         }
423 }
424 print "yes\n" if $has_ports == 1;
425 print "no\n" if $has_ports == 0;
426
427 $config{HAS_EPOLL} = $has_epoll;
428 $config{HAS_KQUEUE} = $has_kqueue;
429
430 printf "Checking for libgnutls... ";
431 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
432         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
433                 print "yes\n";
434                 $config{HAS_GNUTLS} = "y";
435         } else {
436                 print "no\n";
437                 $config{HAS_GNUTLS} = "n";
438         }
439 } else {
440         print "no\n";
441         $config{HAS_GNUTLS} = "n";
442 }
443
444 printf "Checking for openssl... ";
445 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
446         if (defined($openssl_ver) && ($openssl_ver ne "")) {
447                 print "yes\n";
448                 $config{HAS_OPENSSL} = "y";
449         } else {
450                 print "no\n";
451                 $config{HAS_OPENSSL} = "n";
452         }
453 } else {
454         print "no\n";
455         $config{HAS_OPENSSL} = "n";
456 }
457
458 printf "Checking if you are running an ancient, unsupported OS... ";
459 if ($config{OSNAME} =~ /FreeBSD/i)
460 {
461         my $version = `uname -r`;
462         if ($version =~ /^4\./)
463         {
464                 print "yes.\n";
465                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
466                 print "To build, you will need to add the following to CXXFLAGS:\n";
467                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
468         }
469         else
470         {
471                 print "no ($version)\n";
472         }
473 }
474 else
475 {
476         print "no ($config{OSNAME})\n";
477 }
478
479 ################################################################################
480 #                         BEGIN INTERACTIVE PART                              #
481 ################################################################################
482
483 # Clear the Screen..
484 if ($interactive)
485 {
486         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
487         my $wholeos = $^O;
488
489         my $rev = getrevision();
490         # Display Introduction Message..
491         print <<"STOP" ;
492 Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
493 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
494
495 *** If you are unsure of any of these values, leave it blank for    ***
496 *** standard settings that will work, and your server will run      ***
497 *** using them. Please consult your IRC network admin if in doubt.  ***
498
499 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
500 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
501 dir, otherwise you won't have a config file!
502
503 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
504 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
505 STOP
506         if ($rev eq "r0") {
507                 print " (Non-SVN build)";
508         }
509         print ".\n\n";
510
511         $config{CHANGE_COMPILER} = "n";
512         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";
513
514         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
515                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
516 should NOT be used. You should probably specify a newer compiler.\n\n";
517                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
518                 if ($config{CHANGE_COMPILER} =~ /y/i) {
519                         print "What command do you want to use to invoke your compiler?\n";
520                         print "[\e[1;32m$config{CC}\e[0m] -> ";
521                         chomp($config{CC} = <STDIN>);
522                         if ($config{CC} eq "") {
523                                 $config{CC} = "g++";
524                         }
525                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
526                         if ($foo ne "") {
527                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
528                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
529                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
530                                 if ($config{GCCVER} < 3) {
531                                         print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
532                                 }
533                         }
534                         else {
535                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
536                         }
537                 }
538         }
539
540         print "\n";
541
542         # Directory Settings..
543         my $tmpbase = $config{BASE_DIR};
544         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
545         if ($tmpbase ne $config{BASE_DIR}) {
546                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
547                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
548                 $config{DATA_DIR}        = resolve_directory($config{BASE_DIR}."/data");        # Data Directory
549                 $config{LOG_DIR}         = resolve_directory($config{BASE_DIR}."/logs");        # Log Directory
550                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
551         }
552
553         dir_check("are the configuration files", "CONFIG_DIR");
554         dir_check("are the modules to be compiled to", "MODULE_DIR");
555         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
556         dir_check("are variable data files to be located in", "DATA_DIR");
557         dir_check("are the logs to be stored in", "LOG_DIR");
558         dir_check("do you want the build to take place", "BUILD_DIR");
559                 
560         my $chose_hiperf = 0;
561         if ($has_kqueue) {
562                 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?");
563                 print "\n";
564                 if ($config{USE_KQUEUE} eq "y") {
565                         $chose_hiperf = 1;
566                 }
567         }
568         if ($has_epoll) {
569                 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?");
570                 print "\n";
571                 if ($config{USE_EPOLL} eq "y") {
572                         $chose_hiperf = 1;
573                 }
574         }
575         if ($has_ports) {
576                 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?");
577                 print "\n";
578                 if ($config{USE_PORTS} eq "y") {
579                         $chose_hiperf = 1;
580                 }
581         }
582
583         if (!$chose_hiperf) {
584                 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?");
585                 if ($config{USE_POLL} ne "y")
586                 {
587                         print "No high-performance socket engines are available, or you chose\n";
588                         print "not to enable one. Defaulting to select() engine.\n\n";
589                 }
590         }
591
592         $config{USE_FREEBSD_BASE_SSL} = "n";
593         $config{USE_FREEBSD_PORTS_SSL} = "n";
594         if ($config{HAS_OPENSSL_PORT} ne "")
595         {
596                 $config{USE_FREEBSD_PORTS_SSL} = "y";
597                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
598                 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";
599                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
600                 print "\n";
601                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
602
603                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
604                 {
605                         # update to port version
606                         $openssl_ver = $config{HAS_OPENSSL_PORT};
607                 }
608         }
609         else
610         {
611                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
612         }
613
614         $config{USE_SSL} = "n";
615         $config{MODUPDATE} = 'n';
616
617         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
618         {
619                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
620                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
621
622                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
623                 if ($config{USE_SSL} eq "y")
624                 {
625                         if ($config{HAS_GNUTLS} eq "y")
626                         {
627                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
628                                 if ($config{USE_GNUTLS} eq "y")
629                                 {
630                                         print "\nUsing GnuTLS SSL module.\n";
631                                 }
632                         }
633
634                         if ($config{HAS_OPENSSL} eq "y")
635                         {
636                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
637                                 if ($config{USE_OPENSSL} eq "y")
638                                 {
639                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
640                                 }
641                         }
642                 }
643         }
644         else
645         {
646                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
647                 print "is in your path.\n\n";
648         }
649
650         yesno('MODUPDATE',"Would you like to check for updates to third-party modules?");
651         print "\n";
652         if ($config{MODUPDATE} eq "y") {
653                 print "Checking for upgrades to extra and third party modules... ";
654                 system "./modulemanager upgrade";
655         }
656 }
657
658 # We are on a POSIX system, we can enable POSIX extras without asking
659 symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
660
661 dumphash();
662
663 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
664 {
665         print "Sorry, but i couldn't detect gnutls. Make sure gnutls-config is in your path.\n";
666         exit(0);
667 }
668 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
669 {
670         print "Sorry, but i couldn't detect openssl. Make sure openssl is in your path.\n";
671         exit(0);
672 }
673 our $failed = 0;
674
675 $config{CERTGEN} ||= 'y';
676 yesno('CERTGEN',"Would you like generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
677
678 if ($config{USE_GNUTLS} eq "y") {
679         unless (-r "src/modules/m_ssl_gnutls.cpp") {
680                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
681                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
682         }
683         if ($interactive && $config{CERTGEN} eq 'y')
684         {
685                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
686                         print "SSL Certificates Not found, Generating.. \n\n
687 *************************************************************
688 * Generating the Private Key may take some time, go grab a  *
689 * Coffee. Even better, to generate some more entropy if it  *
690 * is taking a while, open another console and type du / a   *
691 * few times and get that HD going :) Then answer the        *
692 * Questions which follow. If you are unsure, 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, 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 (
907                         (($config{GCCVER} == 4) && ($config{GCCMINOR} >= 3))
908                                 ||
909                         ($config{GCCVER} > 4)
910                 ) {
911                         print FILEHANDLE "#define HASHMAP_DEPRECATED\n";
912                 }
913                 if ($config{HAS_STRLCPY} eq "true") {
914                         print FILEHANDLE "#define HAS_STRLCPY\n";
915                 }
916                 if ($config{HAS_STDINT} eq "true") {
917                         print FILEHANDLE "#define HAS_STDINT\n";
918                 }
919                 if ($config{HAS_EVENTFD} eq 'true') {
920                         print FILEHANDLE "#define HAS_EVENTFD\n";
921                 }
922                 if ($config{OSNAME} !~ /DARWIN/i) {
923                         print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
924                 }
925                 my $use_hiperf = 0;
926                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
927                         print FILEHANDLE "#define USE_KQUEUE\n";
928                         $config{SOCKETENGINE} = "socketengine_kqueue";
929                         $use_hiperf = 1;
930                 }
931                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
932                         print FILEHANDLE "#define USE_EPOLL\n";
933                         $config{SOCKETENGINE} = "socketengine_epoll";
934                         $use_hiperf = 1;
935                 }
936                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
937                         print FILEHANDLE "#define USE_PORTS\n";
938                         $config{SOCKETENGINE} = "socketengine_ports";
939                         $use_hiperf = 1;
940                 }
941                 # user didn't choose either epoll or select for their OS.
942                 # default them to USE_SELECT (ewwy puke puke)
943                 if (!$use_hiperf) {
944                         print "no hi-perf, " . $config{USE_POLL};
945                         if ($config{USE_POLL} eq "y")
946                         {
947                                 print FILEHANDLE "#define USE_POLL\n";
948                                 $config{SOCKETENGINE} = "socketengine_poll";
949                         }
950                         else
951                         {
952                                 print FILEHANDLE "#define USE_SELECT\n";
953                                 $config{SOCKETENGINE} = "socketengine_select";
954                         }
955                 }
956                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
957                 close(FILEHANDLE);
958
959                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
960                 print FILEHANDLE <<EOF;
961 #define BRANCH "$branch"
962 #define VERSION "$version"
963 #define REVISION "$revision2"
964 #define SYSTEM "$incos"
965 EOF
966                 close FILEHANDLE;
967
968                 for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
969                         my $diff = 0;
970                         open my $fh1, $file or $diff = 1;
971                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
972                         while (!$diff) {
973                                 my $line1 = <$fh1>;
974                                 my $line2 = <$fh2>;
975                                 if (defined($line1) != defined($line2)) {
976                                         $diff = 1;
977                                 } elsif (!defined $line1) {
978                                         last;
979                                 } else {
980                                         $diff = ($line1 ne $line2);
981                                 }
982                         }
983                         if ($diff) {
984                                 unlink $file;
985                                 rename "$file.tmp", $file;
986                         } else {
987                                 unlink "$file.tmp";
988                         }
989                 }
990         }
991
992         # Write all .in files.
993         my $tmp = "";
994         my $file = "";
995         my $exe = "inspircd";
996
997         # Do this once here, and cache it in the .*.inc files,
998         # rather than attempting to read src/version.sh from
999         # compiled code -- we might not have the source to hand.
1000         # Fix for bug#177 by Brain.
1001
1002         chomp($version = `sh ./src/version.sh`);
1003         chomp(my $revision = getrevision());
1004         $version = "$version(r$revision)";
1005
1006         # We can actually parse any file starting with . and ending with .inc,
1007         # but right now we only parse .inspircd.inc to form './inspircd'
1008         prepare_dynamic_makefile();
1009
1010         my @dotfiles = qw(main.mk inspircd);
1011         push @dotfiles, 'org.inspircd.plist' if $config{OSNAME} eq 'darwin';
1012
1013         foreach my $file (@dotfiles) {
1014                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
1015                 $_ = join '', <FILEHANDLE>;
1016                 close(FILEHANDLE);
1017
1018                 $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
1019
1020                 for my $var (qw(
1021                         CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
1022                         STARTSCRIPT DESTINATION SOCKETENGINE
1023                 )) {
1024                         s/\@$var\@/$config{$var}/g;
1025                 }
1026                 s/\@EXECUTABLE\@/$exe/ if defined $exe;
1027                 s/\@VERSION\@/$version/ if defined $version;
1028
1029                 if ($file eq 'main.mk') {
1030                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
1031
1032                         my $mk_tmp = $_;
1033                         s/\@IFDEF (\S+)/ifdef $1/g;
1034                         s/\@IFNDEF (\S+)/ifndef $1/g;
1035                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
1036                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
1037                         s/\@ELSE/else/g;
1038                         s/\@ENDIF/endif/g;
1039                         s/ *\@BSD_ONLY .*\n//g;
1040                         s/\@GNU_ONLY //g;
1041                         s/\@DO_EXPORT (.*)/export $1/g;
1042                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1043                         print MKF $_;
1044                         close MKF;
1045
1046                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
1047                         $_ = $mk_tmp;
1048                         s/\@IFDEF (\S+)/.if defined($1)/g;
1049                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
1050                         s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1051                         s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1052                         s/\@ELSE/.else/g;
1053                         s/\@ENDIF/.endif/g;
1054                         s/\@BSD_ONLY //g;
1055                         s/ *\@GNU_ONLY .*\n//g;
1056                         $mk_tmp = $_;
1057                         $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1058                         open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1059                         print MKF $mk_tmp;
1060                         close MKF;
1061                 } else {
1062                         print "Writing \e[1;32m$file\e[0m ...\n";
1063                         open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1064                         print FILEHANDLE $_;
1065                         close(FILEHANDLE);
1066                 }
1067         }
1068
1069         chmod 0755, 'inspircd';
1070 }
1071
1072 sub depcheck
1073 {
1074         getmodules();
1075         for my $mod (@modlist) {
1076                 getcompilerflags("src/modules/m_$mod.cpp");
1077                 getlinkerflags("src/modules/m_$mod.cpp");
1078         }
1079 }
1080
1081 sub prepare_dynamic_makefile
1082 {
1083         my $i = 0;
1084
1085         if (!$has_epoll)
1086         {
1087                 $config{USE_EPOLL} = 0;
1088         }
1089         if (!$has_kqueue)
1090         {
1091                 $config{USE_KQUEUE} = 0;
1092         }
1093         if (!$has_ports)
1094         {
1095                 $config{USE_PORTS} = 0;
1096         }
1097 }
1098
1099 # Routine to list out the extra/ modules that have been enabled.
1100 # Note: when getting any filenames out and comparing, it's important to lc it if the
1101 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1102 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1103 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1104 sub list_extras () {
1105         use File::Spec;
1106         # @_ not used
1107         my $srcdir = File::Spec->catdir("src", "modules");
1108         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1109         local $_;
1110         my $dd;
1111         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1112         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1113         closedir $dd;
1114         undef $dd;
1115         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1116         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1117         closedir $dd;
1118         undef $dd;
1119         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1120         my %extras = ();
1121 EXTRA:  for my $extra (@extras) {
1122                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1123                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1124                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1125                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1126                 if (-l $abs_source) {
1127                         # Symlink, is it in the right place?
1128                         my $targ = readlink($abs_source);
1129                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1130                         if ($abs_targ eq $abs_extra) {
1131                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1132                         } else {
1133                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1134                         }
1135                 } elsif (-e $abs_source) {
1136                         my ($devext, $inoext) = stat($abs_extra);
1137                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1138                         if ($lnksrc > 1) {
1139                                 if ($devsrc == $devext && $inosrc == $inoext) {
1140                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1141                                 } else {
1142                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1143                                 }
1144                         } else {
1145                                 open my $extfd, "<", $abs_extra;
1146                                 open my $srcfd, "<", $abs_source;
1147                                 local $/ = undef;
1148                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1149                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1150                                 } else {
1151                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1152                                 }
1153                         }
1154                 } else {
1155                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1156                 }
1157         }
1158         # Now let's add dependency info
1159         for my $extra (keys(%extras)) {
1160                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1161                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1162                 my @deps = split / +/, getdependencies($abs_extra);
1163                 for my $dep (@deps) {
1164                         if (exists($extras{$dep})) {
1165                                 my $ref = \$extras{$dep}; # Take reference.
1166                                 if ($$ref !~ m/needed by/) {
1167                                         # First dependency found.
1168                                         if ($$ref =~ m/enabled/) {
1169                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1170                                         } else {
1171                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1172                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1173                                         }
1174                                 } else {
1175                                         if ($$ref =~ m/enabled/) {
1176                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1177                                         } else {
1178                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1179                                         }
1180                                 }
1181                         }
1182                 }
1183         }
1184         for my $extra (sort {$a cmp $b} keys(%extras)) {
1185                 my $text = $extras{$extra};
1186                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1187                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1188                 } else {
1189                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1190                 }
1191         }
1192         return keys(%extras) if wantarray; # Can be used by manage_extras.
1193 }
1194
1195 sub enable_extras (@) {
1196         my (@extras) = @_;
1197         for my $extra (@extras) {
1198                 my $extrapath = "src/modules/extra/$extra";
1199                 if (!-e $extrapath) {
1200                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1201                         next;
1202                 }
1203                 my $source = "src/modules/$extra";
1204                 if (-e $source) {
1205                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1206                         next;
1207                 }
1208                 # Get dependencies, and add them to be processed.
1209                 my @deps = split / +/, getdependencies($extrapath);
1210                 for my $dep (@deps) {
1211                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1212                         if (!-e "src/modules/$dep") {
1213                                 if (-e "src/modules/extra/$dep") {
1214                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1215                                         push @extras, $dep;
1216                                 } else {
1217                                         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";
1218                                 }
1219                         }
1220                 }
1221                 print "Enabling $extra ... \n";
1222                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1223         }
1224 }
1225
1226 sub disable_extras (@)
1227 {
1228         opendir my $dd, "src/modules/extra/";
1229         my @files = readdir($dd);
1230         closedir $dd;
1231         my (@extras) = @_;
1232 EXTRA:  for my $extra (@extras) {
1233                 my $extrapath = "src/modules/extra/$extra";
1234                 my $source = "src/modules/$extra";
1235                 if (!-e $extrapath) {
1236                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1237                         next;
1238                 }
1239                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1240                         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";
1241                         next;
1242                 }
1243                 # Check if anything needs this.
1244                 for my $file (@files) {
1245                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1246                         # File depends on this extra...
1247                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1248                                 # And is both enabled and not about to be disabled.
1249                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1250                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1251                                         next EXTRA;
1252                                 }
1253                         }
1254                 }
1255                 # Now remove.
1256                 print "Disabling $extra ... \n";
1257                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1258         }
1259 }