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