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