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