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