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