Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'S4U'
[simgrid.git] / tools / tesh / tesh.pl
index f25be34..54f053a 100755 (executable)
@@ -220,12 +220,15 @@ the content it gets as input.
 It is not possible to use the cat command, as one would expect,
 because stream redirections are currently not implemented in Tesh.
 
 It is not possible to use the cat command, as one would expect,
 because stream redirections are currently not implemented in Tesh.
 
-=head1 BUGS AND LIMITATIONS
+=head1 BUGS, LIMITATIONS AND POSSIBLE IMPROVEMENTS
 
 The main limitation is the lack of stream redirections in the commands
 (">", "<" and "|" shell constructs and friends). The B<mkfile> builtin
 command makes this situation bearable.
 
 
 The main limitation is the lack of stream redirections in the commands
 (">", "<" and "|" shell constructs and friends). The B<mkfile> builtin
 command makes this situation bearable.
 
+It would be nice if we could replace the tesh file completely with
+command line flags when the output is not to be verified.
+
 =cut
 
 BEGIN {
 =cut
 
 BEGIN {
@@ -276,6 +279,9 @@ BEGIN {
         *WEXITSTATUS = sub { $_[0] >> 8 };
         *WIFSIGNALED = sub { ( $_[0] & 127 ) && ( $_[0] & 127 != 127 ) };
         *WTERMSIG    = sub { $_[0] & 127 };
         *WEXITSTATUS = sub { $_[0] >> 8 };
         *WIFSIGNALED = sub { ( $_[0] & 127 ) && ( $_[0] & 127 != 127 ) };
         *WTERMSIG    = sub { $_[0] & 127 };
+
+       # used on the command lines
+       $environ{'EXEEXT'} = ".exe";
     }
 }
 
     }
 }
 
@@ -302,6 +308,8 @@ GetOptions(
 );
 
 $tesh_file = pop @ARGV;
 );
 
 $tesh_file = pop @ARGV;
+$tesh_name = $tesh_file;
+$tesh_name =~ s|^.*?/([^/]*)$|$1|;
 
 print "Enable coverage\n" if ($enable_coverage);
 
 
 print "Enable coverage\n" if ($enable_coverage);
 
@@ -312,34 +320,14 @@ if ($diff_tool) {
 }
 
 if ( $tesh_file =~ m/(.*)\.tesh/ ) {
 }
 
 if ( $tesh_file =~ m/(.*)\.tesh/ ) {
-    $tesh_name = $1;
-    print "Test suite `$tesh_name'\n";
+    print "Test suite `$tesh_file'\n";
 } else {
 } else {
-    $tesh_file = "(stdin)";
     $tesh_name = "(stdin)";
     print "Test suite from stdin\n";
 }
 
 ###########################################################################
 
     $tesh_name = "(stdin)";
     print "Test suite from stdin\n";
 }
 
 ###########################################################################
 
-sub exit_status {
-    my $status = shift;
-    if ( WIFEXITED($status) ) {
-        $exitcode = WEXITSTATUS($status) + 40;
-        return "returned code " . WEXITSTATUS($status);
-    } elsif ( WIFSIGNALED($status) ) {
-        my $code;
-        if    ( WTERMSIG($status) == SIGINT )  { $code = "SIGINT"; }
-        elsif ( WTERMSIG($status) == SIGTERM ) { $code = "SIGTERM"; }
-        elsif ( WTERMSIG($status) == SIGKILL ) { $code = "SIGKILL"; }
-        elsif ( WTERMSIG($status) == SIGABRT ) { $code = "SIGABRT"; }
-        elsif ( WTERMSIG($status) == SIGSEGV ) { $code = "SIGSEGV"; }
-        $exitcode = WTERMSIG($status) + 4;
-        return "got signal $code";
-    }
-    return "Unparsable status. Is the process stopped?";
-}
-
 sub exec_cmd {
     my %cmd = %{ $_[0] };
     if ( $opts{'debug'} ) {
 sub exec_cmd {
     my %cmd = %{ $_[0] };
     if ( $opts{'debug'} ) {
@@ -348,14 +336,7 @@ sub exec_cmd {
         print "CMD: $cmd{'cmd'}\n";
     }
 
         print "CMD: $cmd{'cmd'}\n";
     }
 
-    # cleanup the command line
-    if (RUNNING_ON_WINDOWS) {
-        var_subst( $cmd{'cmd'}, "EXEEXT", ".exe" );
-    } else {
-        var_subst( $cmd{'cmd'}, "EXEEXT", "" );
-    }
-
-    # substitute environ variables
+    # substitute environment variables
     foreach my $key ( keys %environ ) {
         $cmd{'cmd'} = var_subst( $cmd{'cmd'}, $key, $environ{$key} );
     }
     foreach my $key ( keys %environ ) {
         $cmd{'cmd'} = var_subst( $cmd{'cmd'}, $key, $environ{$key} );
     }
@@ -403,32 +384,27 @@ sub exec_cmd {
 
 sub analyze_result {
     my %cmd    = %{ $_[0] };
 
 sub analyze_result {
     my %cmd    = %{ $_[0] };
-    
+    $cmd{'timeouted'} = 0; # initialization
+
+    # Wait for the end of the child process
+    #####
     eval {
        finish( $cmd{'job'} );
     };
     eval {
        finish( $cmd{'job'} );
     };
-    if ($@) {
+    if ($@) { # deal with the errors that occured in the child process
        if ($@ =~ /timeout/) {
            $cmd{'job'}->kill_kill;
            $cmd{'timeouted'} = 1;
        if ($@ =~ /timeout/) {
            $cmd{'job'}->kill_kill;
            $cmd{'timeouted'} = 1;
-       } elsif ($@ =~ /^ack / and $@ =~ /pipe/) {
+       } elsif ($@ =~ /^ack / and $@ =~ /pipe/) { # IPC::Run is not very expressive about the pipes that it gets :(
            print STDERR "Tesh: Broken pipe (ignored).\n";
        } else {
            die $@; # Don't know what it is, so let it go.
        }
     } 
            print STDERR "Tesh: Broken pipe (ignored).\n";
        } else {
            die $@; # Don't know what it is, so let it go.
        }
     } 
-    $cmd{'timeouted'} ||= 0;
-    
-    my $gotret = $cmd{'gotret'} = exit_status($?); 
-
-    my $wantret;
-
-    if ( defined( $cmd{'expect'} ) and ( $cmd{'expect'} ne "" ) ) {
-        $wantret = "got signal $cmd{'expect'}";
-    } else {
-        $wantret = "returned code " . ( defined( $cmd{'return'} ) ? $cmd{'return'} : 0 );
-    }
 
 
+    # Gather information
+    ####
+    
     # pop all output from executing child
     my @got;
     map { print "GOT: $_\n" } ${$cmd{'got'}} if $opts{'debug'};
     # pop all output from executing child
     my @got;
     map { print "GOT: $_\n" } ${$cmd{'got'}} if $opts{'debug'};
@@ -442,11 +418,53 @@ sub analyze_result {
         }
     }
 
         }
     }
 
-    if ( $cmd{'sort'} ) {
+    # How did the child process terminate?
+    my $status = $?;
+    $cmd{'gotret'} = "Unparsable status. Please report this tesh bug.";
+    if ( $cmd{'timeouted'} ) {
+        $cmd{'gotret'} = "timeout after $cmd{'timeout'} sec";
+        $error    = 1;
+        $exitcode = 3;
+    } elsif ( WIFEXITED($status) ) {
+        $exitcode = WEXITSTATUS($status) + 40;
+       $cmd{'gotret'} = "returned code " . WEXITSTATUS($status);
+    } elsif ( WIFSIGNALED($status) ) {
+        my $code;
+        if    ( WTERMSIG($status) == SIGINT )  { $code = "SIGINT"; }
+        elsif ( WTERMSIG($status) == SIGTERM ) { $code = "SIGTERM"; }
+        elsif ( WTERMSIG($status) == SIGKILL ) { $code = "SIGKILL"; }
+        elsif ( WTERMSIG($status) == SIGABRT ) { $code = "SIGABRT"; }
+        elsif ( WTERMSIG($status) == SIGSEGV ) { $code = "SIGSEGV"; }
+        $exitcode = WTERMSIG($status) + 4;
+        $cmd{'gotret'} = "got signal $code";
+    }
 
 
-        # Save the unsorted observed output to report it on error.
-        map { push @{ $cmd{'unsorted got'} }, $_ } @got;
+    # How was it supposed to terminate?
+    my $wantret;
+    if ( defined( $cmd{'expect'} ) and ( $cmd{'expect'} ne "" ) ) {
+        $wantret = "got signal $cmd{'expect'}";
+    } else {
+        $wantret = "returned code " . ( defined( $cmd{'return'} ) ? $cmd{'return'} : 0 );
+    }
 
 
+    # Enforce the outcome
+    ####
+    
+    # Did it end as expected?
+    if ( $cmd{'gotret'} ne $wantret ) {
+        $error = 1;
+        my $msg = "Test suite `$tesh_name': NOK (<$tesh_name:$cmd{'line'}> $cmd{'gotret'})\n";
+        if ( scalar @got ) {
+            $msg = $msg . "Output of <$tesh_name:$cmd{'line'}> so far:\n";
+           map { $msg .= "|| $_\n" } @got;
+        } else {
+           $msg .= "<$tesh_name:$cmd{'line'}> No output so far.\n";
+       }
+        print STDERR "$msg";
+    }
+
+    # Does the output match?
+    if ( $cmd{'sort'} ) {
         sub mysort {
             substr( $a, 0, $sort_prefix ) cmp substr( $b, 0, $sort_prefix );
         }
         sub mysort {
             substr( $a, 0, $sort_prefix ) cmp substr( $b, 0, $sort_prefix );
         }
@@ -460,7 +478,7 @@ sub analyze_result {
             shift @got;
         }
 
             shift @got;
         }
 
-        # Sort the expected output to make it easier to write for humans
+        # Sort the expected output too, to make tesh files easier to write for humans
         if ( defined( $cmd{'out'} ) ) {
             if ( $sort_prefix > 0 ) {
                 @{ $cmd{'out'} } = sort mysort @{ $cmd{'out'} };
         if ( defined( $cmd{'out'} ) ) {
             if ( $sort_prefix > 0 ) {
                 @{ $cmd{'out'} } = sort mysort @{ $cmd{'out'} };
@@ -473,58 +491,38 @@ sub analyze_result {
         }
     }
 
         }
     }
 
-    # Did we timeout?
-
-    if ( $cmd{'timeouted'} ) {
-        $gotret   = "timeout after $cmd{'timeout'} sec";
-        $error    = 1;
-        $exitcode = 3;
-        print STDERR "<$cmd{'file'}:$cmd{'line'}> timeouted. Kill the process.\n";
-    }
-    if ( $gotret ne $wantret ) {
-        $error = 1;
-        my $msg = "Test suite `$cmd{'file'}': NOK (<$cmd{'file'}:$cmd{'line'}> $gotret)\n";
-        if ( scalar @got ) {
-            $msg = $msg . "Output of <$cmd{'file'}:$cmd{'line'}> so far:\n";
-           map { $msg .= "|| $_\n" } @got;
-        } else {
-           $msg .= "<$cmd{'file'}:$cmd{'line'}> No output so far.\n";
-       }
-        print STDERR "$msg";
-    }
-
-    # Does the output match?
-    my $diff;
+    # Report the output if asked so or if it differs
     if ( defined( $cmd{'output display'} ) ) {
         print "[Tesh/INFO] Here is the (ignored) command output:\n";
         map { print "||$_\n" } @got;
     } elsif ( defined( $cmd{'output ignore'} ) ) {
     if ( defined( $cmd{'output display'} ) ) {
         print "[Tesh/INFO] Here is the (ignored) command output:\n";
         map { print "||$_\n" } @got;
     } elsif ( defined( $cmd{'output ignore'} ) ) {
-        print "(ignoring the output of <$cmd{'file'}:$cmd{'line'}> as requested)\n";
+        print "(ignoring the output of <$tesh_name:$cmd{'line'}> as requested)\n";
     } else {
     } else {
-        $diff = build_diff( \@{ $cmd{'out'} }, \@got );
-    }
-    if ( length $diff ) {
-        print "Output of <$cmd{'file'}:$cmd{'line'}> mismatch" . ( $cmd{'sort'} ? " (even after sorting)" : "" ) . ":\n";
-        map { print "$_\n" } split( /\n/, $diff );
-        if ( $cmd{'sort'} ) {
-            print "WARNING: Both the observed output and expected output were sorted as requested.\n";
-            print "WARNING: Output were only sorted using the $sort_prefix first chars.\n"
-              if ( $sort_prefix > 0 );
-            print "WARNING: Use <! output sort 19> to sort by simulated date and process ID only.\n";
-
-            # print "----8<---------------  Begin of unprocessed observed output (as it should appear in file):\n";
-            # map {print "> $_\n"} @{$cmd{'unsorted got'}};
-            # print "--------------->8----  End of the unprocessed observed output.\n";
-        }
-
-        print "Test suite `$cmd{'file'}': NOK (<$cmd{'file'}:$cmd{'line'}> output mismatch)\n";
-        exit 2;
+        my $diff = build_diff( \@{ $cmd{'out'} }, \@got );
+    
+       if ( length $diff ) {
+           print "Output of <$tesh_name:$cmd{'line'}> mismatch" . ( $cmd{'sort'} ? " (even after sorting)" : "" ) . ":\n";
+           map { print "$_\n" } split( /\n/, $diff );
+           if ( $cmd{'sort'} ) {
+               print "WARNING: Both the observed output and expected output were sorted as requested.\n";
+               print "WARNING: Output were only sorted using the $sort_prefix first chars.\n"
+                   if ( $sort_prefix > 0 );
+               print "WARNING: Use <! output sort 19> to sort by simulated date and process ID only.\n";
+
+               # print "----8<---------------  Begin of unprocessed observed output (as it should appear in file):\n";
+               # map {print "> $_\n"} @{$cmd{'unsorted got'}};
+               # print "--------------->8----  End of the unprocessed observed output.\n";
+           }
+           
+           print "Test suite `$tesh_name': NOK (<$tesh_name:$cmd{'line'}> output mismatch)\n";
+           exit 2;
+       }
     }
 }
 
 # parse tesh file
 my $infh;    # The file descriptor from which we should read the teshfile
     }
 }
 
 # parse tesh file
 my $infh;    # The file descriptor from which we should read the teshfile
-if ( $tesh_file eq "(stdin)" ) {
+if ( $tesh_name eq "(stdin)" ) {
     $infh = *STDIN;
 } else {
     open $infh, $tesh_file
     $infh = *STDIN;
 } else {
     open $infh, $tesh_file
@@ -533,7 +531,7 @@ if ( $tesh_file eq "(stdin)" ) {
 
 my %cmd;     # everything about the next command to run
 my $line_num = 0;
 
 my %cmd;     # everything about the next command to run
 my $line_num = 0;
-LINE: while ( defined( my $line = <$infh> ) and not $error ) {
+LINE: while ( not $error and defined( my $line = <$infh> )) {
     chomp $line;
     $line =~ s/\r//g;
 
     chomp $line;
     $line =~ s/\r//g;
 
@@ -551,7 +549,7 @@ LINE: while ( defined( my $line = <$infh> ) and not $error ) {
         $line = $1 . $next;
     }
 
         $line = $1 . $next;
     }
 
-    # Push delayed commands on empty lines
+    # If the line is empty, run any previously defined block and proceed to next line
     unless ( $line =~ m/^(.)(.*)$/ ) {
         if ( defined( $cmd{'cmd'} ) ) {
             exec_cmd( \%cmd );
     unless ( $line =~ m/^(.)(.*)$/ ) {
         if ( defined( $cmd{'cmd'} ) ) {
             exec_cmd( \%cmd );
@@ -567,31 +565,40 @@ LINE: while ( defined( my $line = <$infh> ) and not $error ) {
     $arg =~ s/\r//g;
     $arg =~ s/\\\\/\\/g;
 
     $arg =~ s/\r//g;
     $arg =~ s/\\\\/\\/g;
 
-    # handle the commands
+    # Deal with the lines that can contribute to the current command block
     if ( $cmd =~ /^#/ ) {    # comment
     if ( $cmd =~ /^#/ ) {    # comment
+       next LINE;
     } elsif ( $cmd eq '>' ) {    # expected result line
         print "[TESH/debug] push expected result\n" if $opts{'debug'};
         push @{ $cmd{'out'} }, $arg;
     } elsif ( $cmd eq '>' ) {    # expected result line
         print "[TESH/debug] push expected result\n" if $opts{'debug'};
         push @{ $cmd{'out'} }, $arg;
+       next LINE;
 
     } elsif ( $cmd eq '<' ) {    # provided input
         print "[TESH/debug] push provided input\n" if $opts{'debug'};
         push @{ $cmd{'in'} }, $arg;
 
     } elsif ( $cmd eq '<' ) {    # provided input
         print "[TESH/debug] push provided input\n" if $opts{'debug'};
         push @{ $cmd{'in'} }, $arg;
+       next LINE;
 
     } elsif ( $cmd eq 'p' ) {    # comment
         print "[$tesh_name:$line_num] $arg\n";
 
     } elsif ( $cmd eq 'p' ) {    # comment
         print "[$tesh_name:$line_num] $arg\n";
+       next LINE;
 
 
-    } elsif ( $cmd eq '$' ) {    # Command
-                                 # if we have something buffered, run it now
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        if ( $arg =~ /^\s*mkfile / ) {    # "mkfile" command line
+    } 
+
+    # We dealt with all sort of lines that can contribute to a command block, so we have something else here.
+    # If we have something buffered, run it now and start a new block
+    if ( defined( $cmd{'cmd'} ) ) {
+       exec_cmd( \%cmd );
+       %cmd = ();
+    }
+
+    # Deal with the lines that must be placed before a command block
+    if ( $cmd eq '$' ) {    # Command
+        if ( $arg =~ /^mkfile / ) {    # "mkfile" command line
             die "[TESH/CRITICAL] Output expected from mkfile command!\n"
               if scalar @{ cmd { 'out' } };
 
             $cmd{'arg'} = $arg;
             die "[TESH/CRITICAL] Output expected from mkfile command!\n"
               if scalar @{ cmd { 'out' } };
 
             $cmd{'arg'} = $arg;
-            $cmd{'arg'} =~ s/\s*mkfile //;
+            $cmd{'arg'} =~ s/mkfile //;
             mkfile_cmd( \%cmd );
             %cmd = ();
 
             mkfile_cmd( \%cmd );
             %cmd = ();
 
@@ -607,80 +614,56 @@ LINE: while ( defined( my $line = <$infh> ) and not $error ) {
 
         } else {    # regular command
             $cmd{'cmd'}  = $arg;
 
         } else {    # regular command
             $cmd{'cmd'}  = $arg;
-            $cmd{'file'} = $tesh_file;
             $cmd{'line'} = $line_num;
         }
             $cmd{'line'} = $line_num;
         }
-    } elsif ( $cmd eq '&' ) {    # background command line
 
 
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
+    } elsif ( $cmd eq '&' ) {    # background command line
+       die "[TESH/CRITICAL] mkfile cannot be run in background\n"
+           if ($arg =~ /^mkfile/);
+       die "[TESH/CRITICAL] cd cannot be run in background\n"
+           if ($arg =~ /^cd/);
+       
         $cmd{'background'} = 1;
         $cmd{'cmd'}        = $arg;
         $cmd{'background'} = 1;
         $cmd{'cmd'}        = $arg;
-        $cmd{'file'}       = $tesh_file;
         $cmd{'line'}       = $line_num;
 
         $cmd{'line'}       = $line_num;
 
-    } elsif ( $line =~ /^!\s*output sort/ ) {    #output sort
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $cmd{'sort'} = 1;
-        if ( $line =~ /^!\s*output sort\s+(\d+)/ ) {
-            $sort_prefix = $1;
-        }
-    } elsif ( $line =~ /^!\s*output ignore/ ) {    #output ignore
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $cmd{'output ignore'} = 1;
-    } elsif ( $line =~ /^!\s*output display/ ) {    #output display
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $cmd{'output display'} = 1;
-    } elsif ( $line =~ /^!\s*expect signal (\w*)/ ) {    #expect signal SIGABRT
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $cmd{'expect'} = "$1";
-    } elsif ( $line =~ /^!\s*expect return/ ) {          #expect return
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $line =~ s/^! expect return //g;
-        $line =~ s/\r//g;
-        $cmd{'return'} = $line;
-    } elsif ( $line =~ /^!\s*setenv/ ) {                 #setenv
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $line =~ s/^! setenv //g;
-        $line =~ s/\r//g;
-        setenv_cmd($line);
-    } elsif ( $line =~ /^!\s*timeout/ ) {                #timeout
-        if ( defined( $cmd{'cmd'} ) ) {
-            exec_cmd( \%cmd );
-            %cmd = ();
-        }
-        $line =~ s/^! timeout //;
-        $line =~ s/\r//g;
-        $cmd{'timeout'} = $line;
+    # Deal with the meta-commands
+    } elsif ( $line =~ /^! (.*)/) {
+       $line = $1;
+
+       if ( $line =~ /^output sort/ ) {
+           $cmd{'sort'} = 1;
+           if ( $line =~ /^output sort\s+(\d+)/ ) {
+               $sort_prefix = $1;
+           }
+       } elsif ($line =~ /^output ignore/ ) {
+           $cmd{'output ignore'} = 1;
+       } elsif ( $line =~ /^output display/ ) {
+           $cmd{'output display'} = 1;
+       } elsif ( $line =~ /^expect signal (\w*)/ ) {
+           $cmd{'expect'} = $1;
+       } elsif ( $line =~ /^expect return/ ) {
+           $line =~ s/^expect return //g;
+           $line =~ s/\r//g;
+           $cmd{'return'} = $line;
+       } elsif ( $line =~ /^setenv/ ) {
+           $line =~ s/^setenv //g;
+           $line =~ s/\r//g;
+           setenv_cmd($line);
+       } elsif ( $line =~ /^timeout/ ) {
+           $line =~ s/^timeout //;
+           $line =~ s/\r//g;
+           $cmd{'timeout'} = $line;
+       }
     } else {
         die "[TESH/CRITICAL] parse error: $line\n";
     }
 }
 
     } else {
         die "[TESH/CRITICAL] parse error: $line\n";
     }
 }
 
-# We're done reading the input file
-close $infh unless ( $tesh_file eq "(stdin)" );
+# We are done reading the input file
+close $infh unless ( $tesh_name eq "(stdin)" );
 
 
-# Deal with last command
+# Deal with last command, if any
 if ( defined( $cmd{'cmd'} ) ) {
     exec_cmd( \%cmd );
     %cmd = ();
 if ( defined( $cmd{'cmd'} ) ) {
     exec_cmd( \%cmd );
     %cmd = ();
@@ -699,7 +682,7 @@ if ($diff_tool) {
 
 if ( $error != 0 ) {
     exit $exitcode;
 
 if ( $error != 0 ) {
     exit $exitcode;
-} elsif ( $tesh_file eq "(stdin)" ) {
+} elsif ( $tesh_name eq "(stdin)" ) {
     print "Test suite from stdin OK\n";
 } else {
     print "Test suite `$tesh_name' OK\n";
     print "Test suite from stdin OK\n";
 } else {
     print "Test suite `$tesh_name' OK\n";
@@ -787,7 +770,7 @@ sub cd_cmd($) {
         print "Chdir to $directory failed: No such file or directory\n";
     }
     if ( $failure == 1 ) {
         print "Chdir to $directory failed: No such file or directory\n";
     }
     if ( $failure == 1 ) {
-        print "Test suite `$tesh_file': NOK (system error)\n";
+        print "Test suite `$tesh_name': NOK (system error)\n";
         exit 4;
     }
 }
         exit 4;
     }
 }