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