]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
Move 2 static extensibles from user class to inspircd class for global access in...
[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 MOD_PATH "$config{MODULE_DIR}"
932 #define SOMAXCONN_S "$config{_SOMAXCONN}"
933 #define ENTRYPOINT int main(int argc, char** argv)
934
935 EOF
936 print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
937
938                 if ($config{OSNAME} =~ /SunOS/i) {
939                         print FILEHANDLE "#define IS_SOLARIS\n";
940                 }
941                 if ($config{OSNAME} =~ /MINGW32/i) {
942                         print FILEHANDLE "#define IS_MINGW\n";
943                 }
944                 if ($config{GCCVER} >= 3) {
945                         print FILEHANDLE "#define GCC3\n";
946                 }
947                 if (
948                         (($config{GCCVER} == 4) && ($config{GCCMINOR} >= 3))
949                                 ||
950                         ($config{GCCVER} > 4)
951                 ) {
952                         print FILEHANDLE "#define HASHMAP_DEPRECATED\n";
953                 }
954                 if ($config{HAS_STRLCPY} eq "true") {
955                         print FILEHANDLE "#define HAS_STRLCPY\n";
956                 }
957                 if ($config{HAS_STDINT} eq "true") {
958                         print FILEHANDLE "#define HAS_STDINT\n";
959                 }
960                 if ($config{IPV6} =~ /y/i) {
961                         print FILEHANDLE "#define IPV6\n";
962                 }
963                 if ($config{HAS_EVENTFD} eq 'true') {
964                         print FILEHANDLE "#define HAS_EVENTFD\n";
965                 }
966                 my $use_hiperf = 0;
967                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
968                         print FILEHANDLE "#define USE_KQUEUE\n";
969                         $config{SOCKETENGINE} = "socketengine_kqueue";
970                         $use_hiperf = 1;
971                 }
972                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
973                         print FILEHANDLE "#define USE_EPOLL\n";
974                         $config{SOCKETENGINE} = "socketengine_epoll";
975                         $use_hiperf = 1;
976                 }
977                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
978                         print FILEHANDLE "#define USE_PORTS\n";
979                         $config{SOCKETENGINE} = "socketengine_ports";
980                         $use_hiperf = 1;
981                 }
982                 # user didn't choose either epoll or select for their OS.
983                 # default them to USE_SELECT (ewwy puke puke)
984                 if (!$use_hiperf) {
985                         print "no hi-perf, " . $config{USE_POLL};
986                         if ($config{USE_POLL} eq "y")
987                         {
988                                 print FILEHANDLE "#define USE_POLL\n";
989                                 $config{SOCKETENGINE} = "socketengine_poll";
990                         }
991                         else
992                         {
993                                 print FILEHANDLE "#define USE_SELECT\n";
994                                 $config{SOCKETENGINE} = "socketengine_select";
995                         }
996                 }
997                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
998                 close(FILEHANDLE);
999
1000                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
1001                 print FILEHANDLE <<EOF;
1002 #define VERSION "$version"
1003 #define REVISION "$revision2"
1004 #define SYSTEM "$incos"
1005 EOF
1006                 close FILEHANDLE;
1007
1008                 for my $file (qw(include/inspircd_config.h include/inspircd_version.h)) {
1009                         my $diff = 0;
1010                         open my $fh1, $file or $diff = 1;
1011                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
1012                         while (!$diff) {
1013                                 my $line1 = <$fh1>;
1014                                 my $line2 = <$fh2>;
1015                                 if (defined($line1) != defined($line2)) {
1016                                         $diff = 1;
1017                                 } elsif (!defined $line1) {
1018                                         last;
1019                                 } else {
1020                                         $diff = ($line1 ne $line2);
1021                                 }
1022                         }
1023                         if ($diff) {
1024                                 unlink $file;
1025                                 rename "$file.tmp", $file;
1026                         } else {
1027                                 unlink "$file.tmp";
1028                         }
1029                 }
1030         }
1031
1032         # Write all .in files.
1033         my $tmp = "";
1034         my $file = "";
1035         my $exe = "inspircd";
1036
1037         # Do this once here, and cache it in the .*.inc files,
1038         # rather than attempting to read src/version.sh from
1039         # compiled code -- we might not have the source to hand.
1040         # Fix for bug#177 by Brain.
1041
1042         chomp($version = `sh ./src/version.sh`);
1043         chomp(my $revision = getrevision());
1044         $version = "$version(r$revision)";
1045
1046         # We can actually parse any file starting with . and ending with .inc,
1047         # but right now we only parse .inspircd.inc to form './inspircd'
1048         prepare_dynamic_makefile();
1049
1050     print "Writing \e[1;32mMakefiles\e[0m\n";
1051
1052         opendir(DIRHANDLE, $this);
1053
1054         foreach my $name (sort readdir(DIRHANDLE)) {
1055                 if ($name =~ /^\.(.+)\.inc$/) {
1056                         $file = $1;
1057
1058                         # Bug #353, omit this on non-darwin
1059                         next if (($config{OSNAME} !~ /darwin/) && ($file eq "org.inspircd.plist"));
1060
1061                         # All .name.inc files need parsing!
1062                         open(FILEHANDLE, ".$file.inc") or die ("Can't open .$file.inc");
1063                         $_ = join '', <FILEHANDLE>;
1064                         close(FILEHANDLE);
1065
1066                         print "Writing \e[1;32m$file\e[0m ...\n";
1067
1068                         $config{BUILD_DIR} ||= resolve_directory($config{ME}."/build");
1069
1070                         for my $var (qw(
1071                                 CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR
1072                                 STARTSCRIPT DESTINATION EXTRA_DIR SOCKETENGINE
1073                         )) {
1074                                 s/\@$var\@/$config{$var}/g;
1075                         }
1076                         s/\@EXECUTABLE\@/$exe/ if defined $exe;
1077                         s/\@VERSION\@/$version/ if defined $version;
1078
1079                         if ($file eq 'Makefile') {
1080                                 my $mk_tmp = $_;
1081                                 s/\@IFDEF (\S+)/ifdef $1/g;
1082                                 s/\@IFNDEF (\S+)/ifndef $1/g;
1083                                 s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
1084                                 s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
1085                                 s/\@ELSE/else/g;
1086                                 s/\@ENDIF/endif/g;
1087                                 s/ *\@BSD_ONLY .*\n//g;
1088                                 s/\@GNU_ONLY //g;
1089                                 s/\@DO_EXPORT (.*)/export $1/g;
1090                                 open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1091                                 print MKF $_;
1092                                 close MKF;
1093                                 $_ = $mk_tmp;
1094                                 s/\@IFDEF (\S+)/.if defined($1)/g;
1095                                 s/\@IFNDEF (\S+)/.if !defined($1)/g;
1096                                 s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1097                                 s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1098                                 s/\@ELSE/.else/g;
1099                                 s/\@ENDIF/.endif/g;
1100                                 s/\@BSD_ONLY //g;
1101                                 s/ *\@GNU_ONLY .*\n//g;
1102                                 $mk_tmp = $_;
1103                                 $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1104                                 open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1105                                 print MKF $mk_tmp;
1106                                 close MKF;
1107                         } else {
1108                                 open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1109                                 print FILEHANDLE $_;
1110                                 close(FILEHANDLE);
1111                         }
1112                 }
1113         }
1114         closedir(DIRHANDLE);
1115
1116         # Make inspircd executable!
1117         chmod 0744, 'inspircd';
1118 }
1119
1120 sub depcheck
1121 {
1122         getmodules();
1123         for my $mod (@modlist) {
1124                 getcompilerflags("src/modules/m_$mod.cpp");
1125                 getlinkerflags("src/modules/m_$mod.cpp");
1126         }
1127 }
1128
1129 sub prepare_dynamic_makefile
1130 {
1131         my $i = 0;
1132
1133         if (!$has_epoll)
1134         {
1135                 $config{USE_EPOLL} = 0;
1136         }
1137         if (!$has_kqueue)
1138         {
1139                 $config{USE_KQUEUE} = 0;
1140         }
1141         if (!$has_ports)
1142         {
1143                 $config{USE_PORTS} = 0;
1144         }
1145 }
1146
1147 # Routine to list out the extra/ modules that have been enabled.
1148 # Note: when getting any filenames out and comparing, it's important to lc it if the
1149 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1150 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1151 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1152 sub list_extras () {
1153         use File::Spec;
1154         # @_ not used
1155         my $srcdir = File::Spec->catdir("src", "modules");
1156         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1157         local $_;
1158         my $dd;
1159         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1160         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1161         closedir $dd;
1162         undef $dd;
1163         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1164         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1165         closedir $dd;
1166         undef $dd;
1167         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1168         my %extras = ();
1169 EXTRA:  for my $extra (@extras) {
1170                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1171                 next if ($extra eq '.svn');
1172                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1173                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1174                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1175                 if (-l $abs_source) {
1176                         # Symlink, is it in the right place?
1177                         my $targ = readlink($abs_source);
1178                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1179                         if ($abs_targ eq $abs_extra) {
1180                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1181                         } else {
1182                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1183                         }
1184                 } elsif (-e $abs_source) {
1185                         my ($devext, $inoext) = stat($abs_extra);
1186                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1187                         if ($lnksrc > 1) {
1188                                 if ($devsrc == $devext && $inosrc == $inoext) {
1189                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1190                                 } else {
1191                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1192                                 }
1193                         } else {
1194                                 open my $extfd, "<", $abs_extra;
1195                                 open my $srcfd, "<", $abs_source;
1196                                 local $/ = undef;
1197                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1198                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1199                                 } else {
1200                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1201                                 }
1202                         }
1203                 } else {
1204                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1205                 }
1206         }
1207         # Now let's add dependency info
1208         for my $extra (keys(%extras)) {
1209                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1210                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1211                 my @deps = split / +/, getdependencies($abs_extra);
1212                 for my $dep (@deps) {
1213                         if (exists($extras{$dep})) {
1214                                 my $ref = \$extras{$dep}; # Take reference.
1215                                 if ($$ref !~ m/needed by/) {
1216                                         # First dependency found.
1217                                         if ($$ref =~ m/enabled/) {
1218                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1219                                         } else {
1220                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1221                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1222                                         }
1223                                 } else {
1224                                         if ($$ref =~ m/enabled/) {
1225                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1226                                         } else {
1227                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1228                                         }
1229                                 }
1230                         }
1231                 }
1232         }
1233         for my $extra (sort {$a cmp $b} keys(%extras)) {
1234                 my $text = $extras{$extra};
1235                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1236                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1237                 } else {
1238                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1239                 }
1240         }
1241         return keys(%extras) if wantarray; # Can be used by manage_extras.
1242 }
1243
1244 sub enable_extras (@) {
1245         my (@extras) = @_;
1246         for my $extra (@extras) {
1247                 my $extrapath = "src/modules/extra/$extra";
1248                 if (!-e $extrapath) {
1249                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1250                         next;
1251                 }
1252                 my $source = "src/modules/$extra";
1253                 if (-e $source) {
1254                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1255                         next;
1256                 }
1257                 # Get dependencies, and add them to be processed.
1258                 my @deps = split / +/, getdependencies($extrapath);
1259                 for my $dep (@deps) {
1260                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1261                         if (!-e "src/modules/$dep") {
1262                                 if (-e "src/modules/extra/$dep") {
1263                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1264                                         push @extras, $dep;
1265                                 } else {
1266                                         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";
1267                                 }
1268                         }
1269                 }
1270                 print "Enabling $extra ... \n";
1271                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1272         }
1273 }
1274
1275 sub disable_extras (@)
1276 {
1277         opendir my $dd, "src/modules/extra/";
1278         my @files = readdir($dd);
1279         closedir $dd;
1280         my (@extras) = @_;
1281 EXTRA:  for my $extra (@extras) {
1282                 my $extrapath = "src/modules/extra/$extra";
1283                 my $source = "src/modules/$extra";
1284                 if (!-e $extrapath) {
1285                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1286                         next;
1287                 }
1288                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1289                         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";
1290                         next;
1291                 }
1292                 # Check if anything needs this.
1293                 for my $file (@files) {
1294                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1295                         # File depends on this extra...
1296                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1297                                 # And is both enabled and not about to be disabled.
1298                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1299                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1300                                         next EXTRA;
1301                                 }
1302                         }
1303                 }
1304                 # Now remove.
1305                 print "Disabling $extra ... \n";
1306                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1307         }
1308 }