]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
7b1452c5d4be23c97b5b5c81703e9783b2a41a71
[user/henk/code/inspircd.git] / configure
1 #!/usr/bin/env perl
2
3 #
4 # InspIRCd -- Internet Relay Chat Daemon
5 #
6 #   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
7 #   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
8 #   Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
9 #   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
10 #   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
11 #   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
12 #   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
13 #   Copyright (C) 2003-2006 Craig McLure <craig@chatspike.net>
14 #
15 # This file is part of InspIRCd.  InspIRCd is free software: you can
16 # redistribute it and/or modify it under the terms of the GNU General Public
17 # License as published by the Free Software Foundation, version 2.
18 #
19 # This program is distributed in the hope that it will be useful, but WITHOUT
20 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22 # details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 #
27
28
29 BEGIN {
30         require 5.8.0;
31 }
32
33 use strict;
34 use warnings FATAL => qw(all);
35
36 use File::Basename        qw(basename);
37 use File::Copy            ();
38 use File::Spec::Functions qw(rel2abs);
39 use Getopt::Long          qw(GetOptions);
40 use POSIX                 qw(getgid getuid);
41
42 use make::common;
43 use make::configure;
44 use make::console;
45
46 my ($opt_binary_dir,
47     $opt_config_dir,
48     $opt_data_dir,
49     $opt_development,
50     $opt_disable_interactive,
51     $opt_distribution_label,
52     $opt_gid,
53     $opt_log_dir,
54     $opt_manual_dir,
55     $opt_module_dir,
56     $opt_prefix,
57     $opt_socketengine,
58     $opt_system,
59     $opt_uid);
60
61 sub list_extras ();
62
63 sub enable_extras (@);
64
65 sub disable_extras (@);
66
67 my @opt_enableextras;
68 my @opt_disableextras;
69
70 GetOptions(
71         'clean'  => \&cmd_clean,
72         'help'   => \&cmd_help,
73         'update' => \&cmd_update,
74
75         'development'          => \$opt_development,
76         'disable-interactive'  => \$opt_disable_interactive,
77         'distribution-label=s' => \$opt_distribution_label,
78         'binary-dir=s'         => \$opt_binary_dir,
79         'config-dir=s'         => \$opt_config_dir,
80         'data-dir=s'           => \$opt_data_dir,
81         'gid=s'                => \$opt_gid,
82         'log-dir=s'            => \$opt_log_dir,
83         'manual-dir=s'         => \$opt_manual_dir,
84         'module-dir=s'         => \$opt_module_dir,
85         'prefix=s'             => \$opt_prefix,
86         'socketengine=s'       => \$opt_socketengine,
87         'system'               => \$opt_system,
88         'uid=s'                => \$opt_uid,
89
90         # TODO: when the modulemanager rewrite is done these should be removed.
91         'disable-extras=s@' => \@opt_disableextras,
92         'enable-extras=s@'  => \@opt_enableextras,
93         'list-extras'       => sub { list_extras; exit 0; },
94 );
95
96 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
97         @opt_enableextras = split /,/, join(',', @opt_enableextras);
98         @opt_disableextras = split /,/, join(',', @opt_disableextras);
99         enable_extras(@opt_enableextras);
100         disable_extras(@opt_disableextras);
101         list_extras;
102         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
103         exit 0;
104 }
105
106 our $interactive = !(
107         !-t STDIN ||
108         !-t STDOUT ||
109         defined $opt_binary_dir ||
110         defined $opt_config_dir ||
111         defined $opt_data_dir ||
112         defined $opt_development ||
113         defined $opt_disable_interactive ||
114         defined $opt_distribution_label ||
115         defined $opt_gid ||
116         defined $opt_log_dir ||
117         defined $opt_manual_dir ||
118         defined $opt_module_dir ||
119         defined $opt_prefix ||
120         defined $opt_socketengine ||
121         defined $opt_system ||
122         defined $opt_uid
123 );
124
125 my %version = get_version();
126 print_format "<|BOLD Configuring InspIRCd $version{MAJOR}.$version{MINOR}.$version{PATCH}+$version{LABEL} on $^O.|>\n";
127
128 our %config;
129 if ($interactive) {
130         %config = read_configure_cache();
131         run_test CONFIGURE_CACHE_FILE, %config;
132         if (!defined $config{VERSION}) {
133                 $config{VERSION} = CONFIGURE_CACHE_VERSION;
134         } elsif ($config{VERSION} != CONFIGURE_CACHE_VERSION) {
135                 print_warning "ignoring contents of ${\CONFIGURE_CACHE_FILE} as it was generated by an incompatible version of $0!";
136                 %config = ('VERSION', CONFIGURE_CACHE_VERSION);
137         }
138 }
139
140 $config{CXX} = find_compiler($config{CXX} || $ENV{CXX});
141 unless ($config{CXX}) {
142         print "A suitable C++ compiler could not be detected on your system!\n";
143         print "Set the CXX environment variable to the compiler binary path if this is incorrect.\n";
144         exit 1; 
145 }
146 my %compiler = get_compiler_info($config{CXX});
147
148 $config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
149 $config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
150
151 if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
152         $config{SOCKETENGINE} ||= 'epoll';
153 }
154
155 if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
156         $config{SOCKETENGINE} ||= 'kqueue';
157 }
158
159 if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
160         $config{SOCKETENGINE} ||= 'ports';
161 }
162
163 if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
164         $config{SOCKETENGINE} ||= 'poll';
165 }
166
167 # Select is available on all platforms
168 $config{HAS_SELECT} = 1;
169 $config{SOCKETENGINE} ||= 'select';
170
171 if (defined $opt_socketengine) {
172         my $cfgkey = 'HAS_' . uc $opt_socketengine;
173         if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
174                 $config{SOCKETENGINE} = $opt_socketengine;
175         } else {
176                 print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
177                 print "Available socket engines are:";
178                 foreach (<src/socketengines/socketengine_*.cpp>) {
179                         s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
180                         print " $1" if $config{'HAS_' . uc $1};
181                 }
182                 print "\n";     
183                 exit 1;
184         }
185 }
186
187 # If the user has specified a distribution label then we use it in
188 # place of the label from src/version.sh or Git.
189 if (defined $opt_distribution_label) {
190         $version{LABEL} = $opt_distribution_label;
191 }
192
193 if (defined $opt_system) {
194         $config{BASE_DIR}   = $opt_prefix     || '/var/lib/inspircd';
195         $config{BINARY_DIR} = $opt_binary_dir || '/usr/sbin';
196         $config{CONFIG_DIR} = $opt_config_dir || '/etc/inspircd';
197         $config{DATA_DIR}   = $opt_data_dir   || '/var/inspircd';
198         $config{LOG_DIR}    = $opt_module_dir || '/var/log/inspircd';
199         $config{MANUAL_DIR} = $opt_manual_dir || '/usr/share/man/man1';
200         $config{MODULE_DIR} = $opt_module_dir || '/usr/lib/inspircd';
201 } else {
202         $config{BASE_DIR}   = $opt_prefix     || $config{BASE_DIR}   || rel2abs 'run';
203         $config{BINARY_DIR} = $opt_binary_dir || $config{BINARY_DIR} || rel2abs $config{BASE_DIR} . '/bin';
204         $config{CONFIG_DIR} = $opt_config_dir || $config{CONFIG_DIR} || rel2abs $config{BASE_DIR} . '/conf';
205         $config{DATA_DIR}   = $opt_data_dir   || $config{DATA_DIR}   || rel2abs $config{BASE_DIR} . '/data';
206         $config{LOG_DIR}    = $opt_log_dir    || $config{LOG_DIR}    || rel2abs $config{BASE_DIR} . '/logs';
207         $config{MANUAL_DIR} = $opt_manual_dir || $config{MANUAL_DIR} || rel2abs $config{BASE_DIR} . '/manuals';
208         $config{MODULE_DIR} = $opt_module_dir || $config{MODULE_DIR} || rel2abs $config{BASE_DIR} . '/modules';
209 }
210
211 # Parse --gid=123 or --gid=foo and extract the group id.
212 my @group;
213 if (defined $opt_gid) {
214         @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
215         print_error "there is no '$opt_gid' group on this system!" unless @group;
216 } else {
217         @group = $opt_system ? getgrnam('irc') : getgrgid($config{GID} || getgid());
218         print_error "you need to specify a group to run as using '--gid [id|name]'!" unless @group;
219 }
220 $config{GROUP} = $group[0];
221 $config{GID}   = $group[2];
222
223 # Parse --uid=123 or --uid=foo and extract the user id.
224 my @user;
225 if (defined $opt_uid) {
226         @user = $opt_uid =~ /^\d+$/ ? getpwuid($opt_uid) : getpwnam($opt_uid);
227         print_error "there is no '$opt_uid' user on this system!" unless @user;
228 } else {
229         @user = $opt_system ? getpwnam('irc') : getpwuid($config{UID} || getuid());
230         print_error "you need to specify a user to run as using '--uid [id|name]'!" unless @user;
231 }
232 $config{USER} = $user[0];
233 $config{UID}  = $user[2];
234
235 # Clear the screen.
236 system 'tput', 'clear' if $interactive;
237
238 # Check that the user actually wants this version.
239 if ($version{LABEL} ne 'release') {
240         print_warning <<'EOW';
241 You are building a development version. This contains code which has
242 not been tested as heavily and may contain various faults which could seriously
243 affect the running of your server. It is recommended that you use a stable
244 version instead.
245
246 You can obtain the latest stable version from http://www.inspircd.org/ or by
247 running `git checkout insp20` if you are installing from Git.
248 EOW
249         if (!prompt_bool $interactive, 'I understand this warning and want to continue anyway.', $opt_development || 0) {
250                 print STDERR "If you understand this warning and still want to continue pass the --development flag.\n" unless $interactive;
251                 exit 1;
252         }
253 }
254
255 # Configure directory settings.
256 my $question = <<"EOQ";
257 Currently, InspIRCd is configured with the following paths:
258
259 <|BOLD Base:|>   $config{BASE_DIR}
260 <|BOLD Binary:|> $config{BINARY_DIR}
261 <|BOLD Config:|> $config{CONFIG_DIR}
262 <|BOLD Data:|>   $config{DATA_DIR}
263 <|BOLD Log:|>    $config{LOG_DIR}
264 <|BOLD Manual:|> $config{MANUAL_DIR}
265 <|BOLD Module:|> $config{MODULE_DIR}
266
267 Do you want to change these settings?
268 EOQ
269 if (prompt_bool $interactive, $question, 0) {
270         my $original_base_dir = $config{BASE_DIR};
271         $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
272         foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR)) {
273                 $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
274         }
275         $config{BINARY_DIR} = prompt_dir $interactive, 'In what directory should the InspIRCd binary be placed?',     $config{BINARY_DIR};
276         $config{CONFIG_DIR} = prompt_dir $interactive, 'In what directory are configuration files to be stored?', $config{CONFIG_DIR};
277         $config{DATA_DIR}   = prompt_dir $interactive, 'In what directory are variable data files to be stored?',     $config{DATA_DIR};
278         $config{LOG_DIR}    = prompt_dir $interactive, 'In what directory are log files to be stored?',               $config{LOG_DIR};
279         $config{MANUAL_DIR} = prompt_dir $interactive, 'In what directory are manual pages to be placed?',        $config{MANUAL_DIR};
280         $config{MODULE_DIR} = prompt_dir $interactive, 'In what directory are modules to be placed?',             $config{MODULE_DIR};
281 }
282
283 # Configure module settings.
284 $question = <<'EOQ';
285 Currently, InspIRCd is configured to automatically enable all available extra modules.
286
287 Would you like to enable extra modules manually?
288 EOQ
289 if (prompt_bool $interactive, $question, 0) {
290         foreach my $extra (<src/modules/extra/m_*.cpp>) {
291                 my $module_name = basename $extra, '.cpp';
292                 if (prompt_bool $interactive, "Would you like to enable $module_name?", 0) {
293                         enable_extras "$module_name.cpp";
294                 }
295         }
296 } else {
297         # TODO: finish modulemanager rewrite and replace this code with:
298         # system './modulemanager', 'enable', '--auto';
299         enable_extras 'm_ssl_gnutls.cpp' unless system 'gnutls-cli --version >/dev/null 2>&1';
300         enable_extras 'm_ssl_openssl.cpp' unless system 'openssl --version >/dev/null 2>&1';
301 }
302
303 # Generate SSL certificates.
304 if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, 'Would you like to generate SSL certificates now?', $interactive) {
305         system './tools/genssl', 'auto';
306 }
307
308 write_configure_cache %config;
309 parse_templates \%config, \%compiler, \%version;
310
311 print_format <<"EOM";
312
313 Configuration is complete! You have chosen to build with the following settings:
314
315 <|GREEN Compiler:|>
316   <|GREEN Binary:|>  $config{CXX}
317   <|GREEN Name:|>    $compiler{NAME}
318   <|GREEN Version:|> $compiler{VERSION}
319
320 <|GREEN Extra Modules:|>
321 EOM
322
323 for my $file (<src/modules/m_*>) {
324         my $module = basename $file, '.cpp';
325         print "  * $module\n" if -l $file;
326 }
327
328 print_format <<"EOM";
329
330 <|GREEN Paths:|>
331   <|GREEN Base:|>   $config{BASE_DIR}
332   <|GREEN Binary:|> $config{BINARY_DIR}
333   <|GREEN Config:|> $config{CONFIG_DIR}
334   <|GREEN Data:|>   $config{DATA_DIR}
335   <|GREEN Log:|>    $config{LOG_DIR}
336   <|GREEN Manual:|> $config{MANUAL_DIR}
337   <|GREEN Module:|> $config{MODULE_DIR}
338
339 <|GREEN Execution Group:|> $config{GROUP} ($config{GID})
340 <|GREEN Execution User:|>  $config{USER} ($config{UID})
341 <|GREEN Socket Engine:|>   $config{SOCKETENGINE}
342
343 To build with these settings run '<|GREEN make -j${\get_cpu_count}|>' now.
344
345 EOM
346
347 # Routine to list out the extra/ modules that have been enabled.
348 # Note: when getting any filenames out and comparing, it's important to lc it if the
349 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
350 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
351 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
352 sub list_extras () {
353         use File::Spec;
354         # @_ not used
355         my $srcdir = File::Spec->catdir("src", "modules");
356         my $abs_srcdir = File::Spec->rel2abs($srcdir);
357         local $_;
358         my $dd;
359         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
360         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
361         closedir $dd;
362         undef $dd;
363         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
364         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
365         closedir $dd;
366         undef $dd;
367         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
368         my %extras = ();
369 EXTRA:  for my $extra (@extras) {
370                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
371                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
372                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
373                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
374                 if (-l $abs_source) {
375                         # Symlink, is it in the right place?
376                         my $targ = readlink($abs_source);
377                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
378                         if ($abs_targ eq $abs_extra) {
379                                 $extras{$extra} = "\e[32;1menabled\e[0m";
380                         } else {
381                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
382                         }
383                 } elsif (-e $abs_source) {
384                         my ($devext, $inoext) = stat($abs_extra);
385                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
386                         if ($lnksrc > 1) {
387                                 if ($devsrc == $devext && $inosrc == $inoext) {
388                                         $extras{$extra} = "\e[32;1menabled\e[0m";
389                                 } else {
390                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
391                                 }
392                         } else {
393                                 open my $extfd, "<", $abs_extra;
394                                 open my $srcfd, "<", $abs_source;
395                                 local $/ = undef;
396                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
397                                         $extras{$extra} = "\e[32;1menabled\e[0m";
398                                 } else {
399                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
400                                 }
401                         }
402                 } else {
403                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
404                 }
405         }
406         # Now let's add dependency info
407         for my $extra (keys(%extras)) {
408                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
409                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
410                 my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
411                 for my $dep (@deps) {
412                         if (exists($extras{$dep})) {
413                                 my $ref = \$extras{$dep}; # Take reference.
414                                 if ($$ref !~ m/needed by/) {
415                                         # First dependency found.
416                                         if ($$ref =~ m/enabled/) {
417                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
418                                         } else {
419                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
420                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
421                                         }
422                                 } else {
423                                         if ($$ref =~ m/enabled/) {
424                                                 $$ref .= ", \e[32;1m$extra\e[0m";
425                                         } else {
426                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
427                                         }
428                                 }
429                         }
430                 }
431         }
432         for my $extra (sort {$a cmp $b} keys(%extras)) {
433                 my $text = $extras{$extra};
434                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
435                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
436                 } else {
437                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
438                 }
439         }
440         return keys(%extras) if wantarray; # Can be used by manage_extras.
441 }
442
443 sub enable_extras (@) {
444         my (@extras) = @_;
445         for my $extra (@extras) {
446                 my $extrapath = "src/modules/extra/$extra";
447                 if (!-e $extrapath) {
448                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
449                         next;
450                 }
451                 my $source = "src/modules/$extra";
452                 if (-e $source) {
453                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
454                         next;
455                 }
456                 # Get dependencies, and add them to be processed.
457                 my @deps = split /\s+/, get_property($extrapath, 'ModDep');
458                 for my $dep (@deps) {
459                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
460                         if (!-e "src/modules/$dep" && !-e "include/$dep") {
461                                 if (-e "src/modules/extra/$dep") {
462                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
463                                         push @extras, $dep;
464                                 } else {
465                                         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";
466                                 }
467                         }
468                 }
469                 print "Enabling $extra ... \n";
470                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
471         }
472 }
473
474 sub disable_extras (@)
475 {
476         opendir my $dd, "src/modules/extra/";
477         my @files = readdir($dd);
478         closedir $dd;
479         my (@extras) = @_;
480 EXTRA:  for my $extra (@extras) {
481                 my $extrapath = "src/modules/extra/$extra";
482                 my $source = "src/modules/$extra";
483                 if (!-e $extrapath) {
484                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
485                         next;
486                 }
487                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
488                         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";
489                         next;
490                 }
491                 # Check if anything needs this.
492                 for my $file (@files) {
493                         my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
494                         # File depends on this extra...
495                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
496                                 # And is both enabled and not about to be disabled.
497                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
498                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
499                                         next EXTRA;
500                                 }
501                         }
502                 }
503                 # Now remove.
504                 print "Disabling $extra ... \n";
505                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
506         }
507 }