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