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