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