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