ccc-analyzer revision 651f13cea278ec967336033dd032faef0e9fc2ec
1#!/usr/bin/env perl
2#
3#                     The LLVM Compiler Infrastructure
4#
5# This file is distributed under the University of Illinois Open Source
6# License. See LICENSE.TXT for details.
7#
8##===----------------------------------------------------------------------===##
9#
10#  A script designed to interpose between the build system and gcc.  It invokes
11#  both gcc and the static analyzer.
12#
13##===----------------------------------------------------------------------===##
14
15use strict;
16use warnings;
17use FindBin;
18use Cwd qw/ getcwd abs_path /;
19use File::Temp qw/ tempfile /;
20use File::Path qw / mkpath /;
21use File::Basename;
22use Text::ParseWords;
23
24##===----------------------------------------------------------------------===##
25# Compiler command setup.
26##===----------------------------------------------------------------------===##
27
28my $Compiler;
29my $Clang;
30my $DefaultCCompiler;
31my $DefaultCXXCompiler;
32my $IsCXX;
33
34if (`uname -a` =~ m/Darwin/) {
35  $DefaultCCompiler = 'clang';
36  $DefaultCXXCompiler = 'clang++';
37} else {
38  $DefaultCCompiler = 'gcc';
39  $DefaultCXXCompiler = 'g++';
40}
41
42if ($FindBin::Script =~ /c\+\+-analyzer/) {
43  $Compiler = $ENV{'CCC_CXX'};
44  if (!defined $Compiler || ! -x $Compiler) { $Compiler = $DefaultCXXCompiler; }
45
46  $Clang = $ENV{'CLANG_CXX'};
47  if (!defined $Clang || ! -x $Clang) { $Clang = 'clang++'; }
48
49  $IsCXX = 1
50}
51else {
52  $Compiler = $ENV{'CCC_CC'};
53  if (!defined $Compiler || ! -x $Compiler) { $Compiler = $DefaultCCompiler; }
54
55  $Clang = $ENV{'CLANG'};
56  if (!defined $Clang || ! -x $Clang) { $Clang = 'clang'; }
57
58  $IsCXX = 0
59}
60
61##===----------------------------------------------------------------------===##
62# Cleanup.
63##===----------------------------------------------------------------------===##
64
65my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'};
66if (!defined $ReportFailures) { $ReportFailures = 1; }
67
68my $CleanupFile;
69my $ResultFile;
70
71# Remove any stale files at exit.
72END {
73  if (defined $ResultFile && -z $ResultFile) {
74    `rm -f $ResultFile`;
75  }
76  if (defined $CleanupFile) {
77    `rm -f $CleanupFile`;
78  }
79}
80
81##----------------------------------------------------------------------------##
82#  Process Clang Crashes.
83##----------------------------------------------------------------------------##
84
85sub GetPPExt {
86  my $Lang = shift;
87  if ($Lang =~ /objective-c\+\+/) { return ".mii" };
88  if ($Lang =~ /objective-c/) { return ".mi"; }
89  if ($Lang =~ /c\+\+/) { return ".ii"; }
90  return ".i";
91}
92
93# Set this to 1 if we want to include 'parser rejects' files.
94my $IncludeParserRejects = 0;
95my $ParserRejects = "Parser Rejects";
96my $AttributeIgnored = "Attribute Ignored";
97my $OtherError = "Other Error";
98
99sub ProcessClangFailure {
100  my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_;
101  my $Dir = "$HtmlDir/failures";
102  mkpath $Dir;
103
104  my $prefix = "clang_crash";
105  if ($ErrorType eq $ParserRejects) {
106    $prefix = "clang_parser_rejects";
107  }
108  elsif ($ErrorType eq $AttributeIgnored) {
109    $prefix = "clang_attribute_ignored";
110  }
111  elsif ($ErrorType eq $OtherError) {
112    $prefix = "clang_other_error";
113  }
114
115  # Generate the preprocessed file with Clang.
116  my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX",
117                                 SUFFIX => GetPPExt($Lang),
118                                 DIR => $Dir);
119  system $Clang, @$Args, "-E", "-o", $PPFile;
120  close ($PPH);
121
122  # Create the info file.
123  open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n";
124  print OUT abs_path($file), "\n";
125  print OUT "$ErrorType\n";
126  print OUT "@$Args\n";
127  close OUT;
128  `uname -a >> $PPFile.info.txt 2>&1`;
129  `$Compiler -v >> $PPFile.info.txt 2>&1`;
130  system 'mv',$ofile,"$PPFile.stderr.txt";
131  return (basename $PPFile);
132}
133
134##----------------------------------------------------------------------------##
135#  Running the analyzer.
136##----------------------------------------------------------------------------##
137
138sub GetCCArgs {
139  my $mode = shift;
140  my $Args = shift;
141
142  pipe (FROM_CHILD, TO_PARENT);
143  my $pid = fork();
144  if ($pid == 0) {
145    close FROM_CHILD;
146    open(STDOUT,">&", \*TO_PARENT);
147    open(STDERR,">&", \*TO_PARENT);
148    exec $Clang, "-###", $mode, @$Args;
149  }
150  close(TO_PARENT);
151  my $line;
152  while (<FROM_CHILD>) {
153    next if (!/\s"?-cc1"?\s/);
154    $line = $_;
155  }
156
157  waitpid($pid,0);
158  close(FROM_CHILD);
159
160  die "could not find clang line\n" if (!defined $line);
161  # Strip leading and trailing whitespace characters.
162  $line =~ s/^\s+|\s+$//g;
163  my @items = quotewords('\s+', 0, $line);
164  my $cmd = shift @items;
165  die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/));
166  return \@items;
167}
168
169sub Analyze {
170  my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir,
171      $file) = @_;
172
173  my @Args = @$OriginalArgs;
174  my $Cmd;
175  my @CmdArgs;
176  my @CmdArgsSansAnalyses;
177
178  if ($Lang =~ /header/) {
179    exit 0 if (!defined ($Output));
180    $Cmd = 'cp';
181    push @CmdArgs, $file;
182    # Remove the PCH extension.
183    $Output =~ s/[.]gch$//;
184    push @CmdArgs, $Output;
185    @CmdArgsSansAnalyses = @CmdArgs;
186  }
187  else {
188    $Cmd = $Clang;
189
190    # Create arguments for doing regular parsing.
191    my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
192    @CmdArgsSansAnalyses = @$SyntaxArgs;
193
194    # Create arguments for doing static analysis.
195    if (defined $ResultFile) {
196      push @Args, '-o', $ResultFile;
197    }
198    elsif (defined $HtmlDir) {
199      push @Args, '-o', $HtmlDir;
200    }
201    if ($Verbose) {
202      push @Args, "-Xclang", "-analyzer-display-progress";
203    }
204
205    foreach my $arg (@$AnalyzeArgs) {
206      push @Args, "-Xclang", $arg;
207    }
208
209    # Display Ubiviz graph?
210    if (defined $ENV{'CCC_UBI'}) {
211      push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph";
212    }
213
214    my $AnalysisArgs = GetCCArgs("--analyze", \@Args);
215    @CmdArgs = @$AnalysisArgs;
216  }
217
218  my @PrintArgs;
219  my $dir;
220
221  if ($Verbose) {
222    $dir = getcwd();
223    print STDERR "\n[LOCATION]: $dir\n";
224    push @PrintArgs,"'$Cmd'";
225    foreach my $arg (@CmdArgs) {
226        push @PrintArgs,"\'$arg\'";
227    }
228  }
229  if ($Verbose == 1) {
230    # We MUST print to stderr.  Some clients use the stdout output of
231    # gcc for various purposes.
232    print STDERR join(' ', @PrintArgs);
233    print STDERR "\n";
234  }
235  elsif ($Verbose == 2) {
236    print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
237  }
238
239  # Capture the STDERR of clang and send it to a temporary file.
240  # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR.
241  # We save the output file in the 'crashes' directory if clang encounters
242  # any problems with the file.
243  pipe (FROM_CHILD, TO_PARENT);
244  my $pid = fork();
245  if ($pid == 0) {
246    close FROM_CHILD;
247    open(STDOUT,">&", \*TO_PARENT);
248    open(STDERR,">&", \*TO_PARENT);
249    exec $Cmd, @CmdArgs;
250  }
251
252  close TO_PARENT;
253  my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
254
255  while (<FROM_CHILD>) {
256    print $ofh $_;
257    print STDERR $_;
258  }
259  close $ofh;
260
261  waitpid($pid,0);
262  close(FROM_CHILD);
263  my $Result = $?;
264
265  # Did the command die because of a signal?
266  if ($ReportFailures) {
267    if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) {
268      ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
269                          $HtmlDir, "Crash", $ofile);
270    }
271    elsif ($Result) {
272      if ($IncludeParserRejects && !($file =~/conftest/)) {
273        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
274                            $HtmlDir, $ParserRejects, $ofile);
275      } else {
276        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
277                            $HtmlDir, $OtherError, $ofile);
278      }
279    }
280    else {
281      # Check if there were any unhandled attributes.
282      if (open(CHILD, $ofile)) {
283        my %attributes_not_handled;
284
285        # Don't flag warnings about the following attributes that we
286        # know are currently not supported by Clang.
287        $attributes_not_handled{"cdecl"} = 1;
288
289        my $ppfile;
290        while (<CHILD>) {
291          next if (! /warning: '([^\']+)' attribute ignored/);
292
293          # Have we already spotted this unhandled attribute?
294          next if (defined $attributes_not_handled{$1});
295          $attributes_not_handled{$1} = 1;
296
297          # Get the name of the attribute file.
298          my $dir = "$HtmlDir/failures";
299          my $afile = "$dir/attribute_ignored_$1.txt";
300
301          # Only create another preprocessed file if the attribute file
302          # doesn't exist yet.
303          next if (-e $afile);
304
305          # Add this file to the list of files that contained this attribute.
306          # Generate a preprocessed file if we haven't already.
307          if (!(defined $ppfile)) {
308            $ppfile = ProcessClangFailure($Clang, $Lang, $file,
309                                          \@CmdArgsSansAnalyses,
310                                          $HtmlDir, $AttributeIgnored, $ofile);
311          }
312
313          mkpath $dir;
314          open(AFILE, ">$afile");
315          print AFILE "$ppfile\n";
316          close(AFILE);
317        }
318        close CHILD;
319      }
320    }
321  }
322
323  unlink($ofile);
324}
325
326##----------------------------------------------------------------------------##
327#  Lookup tables.
328##----------------------------------------------------------------------------##
329
330my %CompileOptionMap = (
331  '-nostdinc' => 0,
332  '-include' => 1,
333  '-idirafter' => 1,
334  '-imacros' => 1,
335  '-iprefix' => 1,
336  '-iquote' => 1,
337  '-isystem' => 1,
338  '-iwithprefix' => 1,
339  '-iwithprefixbefore' => 1
340);
341
342my %LinkerOptionMap = (
343  '-framework' => 1,
344  '-fobjc-link-runtime' => 0
345);
346
347my %CompilerLinkerOptionMap = (
348  '-Wwrite-strings' => 0,
349  '-ftrapv-handler' => 1, # specifically call out separated -f flag
350  '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '='
351  '-isysroot' => 1,
352  '-arch' => 1,
353  '-m32' => 0,
354  '-m64' => 0,
355  '-stdlib' => 0, # This is really a 1 argument, but always has '='
356  '-target' => 1,
357  '-v' => 0,
358  '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '='
359  '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '='
360);
361
362my %IgnoredOptionMap = (
363  '-MT' => 1,  # Ignore these preprocessor options.
364  '-MF' => 1,
365
366  '-fsyntax-only' => 0,
367  '-save-temps' => 0,
368  '-install_name' => 1,
369  '-exported_symbols_list' => 1,
370  '-current_version' => 1,
371  '-compatibility_version' => 1,
372  '-init' => 1,
373  '-e' => 1,
374  '-seg1addr' => 1,
375  '-bundle_loader' => 1,
376  '-multiply_defined' => 1,
377  '-sectorder' => 3,
378  '--param' => 1,
379  '-u' => 1,
380  '--serialize-diagnostics' => 1
381);
382
383my %LangMap = (
384  'c'   => $IsCXX ? 'c++' : 'c',
385  'cp'  => 'c++',
386  'cpp' => 'c++',
387  'cxx' => 'c++',
388  'txx' => 'c++',
389  'cc'  => 'c++',
390  'C'   => 'c++',
391  'ii'  => 'c++-cpp-output',
392  'i'   => $IsCXX ? 'c++-cpp-output' : 'c-cpp-output',
393  'm'   => 'objective-c',
394  'mi'  => 'objective-c-cpp-output',
395  'mm'  => 'objective-c++',
396  'mii' => 'objective-c++-cpp-output',
397);
398
399my %UniqueOptions = (
400  '-isysroot' => 0
401);
402
403##----------------------------------------------------------------------------##
404# Languages accepted.
405##----------------------------------------------------------------------------##
406
407my %LangsAccepted = (
408  "objective-c" => 1,
409  "c" => 1,
410  "c++" => 1,
411  "objective-c++" => 1,
412  "c-cpp-output" => 1,
413  "objective-c-cpp-output" => 1,
414  "c++-cpp-output" => 1
415);
416
417##----------------------------------------------------------------------------##
418#  Main Logic.
419##----------------------------------------------------------------------------##
420
421my $Action = 'link';
422my @CompileOpts;
423my @LinkOpts;
424my @Files;
425my $Lang;
426my $Output;
427my %Uniqued;
428
429# Forward arguments to gcc.
430my $Status = system($Compiler,@ARGV);
431if (defined $ENV{'CCC_ANALYZER_LOG'}) {
432  print STDERR "$Compiler @ARGV\n";
433}
434if ($Status) { exit($Status >> 8); }
435
436# Get the analysis options.
437my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
438
439# Get the plugins to load.
440my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
441
442# Get the store model.
443my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
444
445# Get the constraints engine.
446my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
447
448#Get the internal stats setting.
449my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
450
451# Get the output format.
452my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
453if (!defined $OutputFormat) { $OutputFormat = "html"; }
454
455# Get the config options.
456my $ConfigOptions = $ENV{'CCC_ANALYZER_CONFIG'};
457
458# Determine the level of verbosity.
459my $Verbose = 0;
460if (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; }
461if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; }
462
463# Get the HTML output directory.
464my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
465
466my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
467my %ArchsSeen;
468my $HadArch = 0;
469
470# Process the arguments.
471foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
472  my $Arg = $ARGV[$i];
473  my ($ArgKey) = split /=/,$Arg,2;
474
475  # Modes ccc-analyzer supports
476  if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
477  elsif ($Arg eq '-c') { $Action = 'compile'; }
478  elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
479
480  # Specially handle duplicate cases of -arch
481  if ($Arg eq "-arch") {
482    my $arch = $ARGV[$i+1];
483    # We don't want to process 'ppc' because of Clang's lack of support
484    # for Altivec (also some #defines won't likely be defined correctly, etc.)
485    if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
486    $HadArch = 1;
487    ++$i;
488    next;
489  }
490
491  # Options with possible arguments that should pass through to compiler.
492  if (defined $CompileOptionMap{$ArgKey}) {
493    my $Cnt = $CompileOptionMap{$ArgKey};
494    push @CompileOpts,$Arg;
495    while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
496    next;
497  }
498  # Handle the case where there isn't a space after -iquote
499  if ($Arg =~ /^-iquote.*/) {
500    push @CompileOpts,$Arg;
501    next;
502  }
503
504  # Options with possible arguments that should pass through to linker.
505  if (defined $LinkerOptionMap{$ArgKey}) {
506    my $Cnt = $LinkerOptionMap{$ArgKey};
507    push @LinkOpts,$Arg;
508    while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
509    next;
510  }
511
512  # Options with possible arguments that should pass through to both compiler
513  # and the linker.
514  if (defined $CompilerLinkerOptionMap{$ArgKey}) {
515    my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
516
517    # Check if this is an option that should have a unique value, and if so
518    # determine if the value was checked before.
519    if ($UniqueOptions{$Arg}) {
520      if (defined $Uniqued{$Arg}) {
521        $i += $Cnt;
522        next;
523      }
524      $Uniqued{$Arg} = 1;
525    }
526
527    push @CompileOpts,$Arg;
528    push @LinkOpts,$Arg;
529
530    while ($Cnt > 0) {
531      ++$i; --$Cnt;
532      push @CompileOpts, $ARGV[$i];
533      push @LinkOpts, $ARGV[$i];
534    }
535    next;
536  }
537
538  # Ignored options.
539  if (defined $IgnoredOptionMap{$ArgKey}) {
540    my $Cnt = $IgnoredOptionMap{$ArgKey};
541    while ($Cnt > 0) {
542      ++$i; --$Cnt;
543    }
544    next;
545  }
546
547  # Compile mode flags.
548  if ($Arg =~ /^-[D,I,U](.*)$/) {
549    my $Tmp = $Arg;
550    if ($1 eq '') {
551      # FIXME: Check if we are going off the end.
552      ++$i;
553      $Tmp = $Arg . $ARGV[$i];
554    }
555    push @CompileOpts,$Tmp;
556    next;
557  }
558
559  if ($Arg =~ /^-m.*/) {
560    push @CompileOpts,$Arg;
561    next;
562  }
563
564  # Language.
565  if ($Arg eq '-x') {
566    $Lang = $ARGV[$i+1];
567    ++$i; next;
568  }
569
570  # Output file.
571  if ($Arg eq '-o') {
572    ++$i;
573    $Output = $ARGV[$i];
574    next;
575  }
576
577  # Get the link mode.
578  if ($Arg =~ /^-[l,L,O]/) {
579    if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
580    elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
581    else { push @LinkOpts,$Arg; }
582
583    # Must pass this along for the __OPTIMIZE__ macro
584    if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; }
585    next;
586  }
587
588  if ($Arg =~ /^-std=/) {
589    push @CompileOpts,$Arg;
590    next;
591  }
592
593  # Get the compiler/link mode.
594  if ($Arg =~ /^-F(.+)$/) {
595    my $Tmp = $Arg;
596    if ($1 eq '') {
597      # FIXME: Check if we are going off the end.
598      ++$i;
599      $Tmp = $Arg . $ARGV[$i];
600    }
601    push @CompileOpts,$Tmp;
602    push @LinkOpts,$Tmp;
603    next;
604  }
605
606  # Input files.
607  if ($Arg eq '-filelist') {
608    # FIXME: Make sure we aren't walking off the end.
609    open(IN, $ARGV[$i+1]);
610    while (<IN>) { s/\015?\012//; push @Files,$_; }
611    close(IN);
612    ++$i;
613    next;
614  }
615
616  if ($Arg =~ /^-f/) {
617    push @CompileOpts,$Arg;
618    push @LinkOpts,$Arg;
619    next;
620  }
621
622  # Handle -Wno-.  We don't care about extra warnings, but
623  # we should suppress ones that we don't want to see.
624  if ($Arg =~ /^-Wno-/) {
625    push @CompileOpts, $Arg;
626    next;
627  }
628
629  if (!($Arg =~ /^-/)) {
630    push @Files, $Arg;
631    next;
632  }
633}
634
635if ($Action eq 'compile' or $Action eq 'link') {
636  my @Archs = keys %ArchsSeen;
637  # Skip the file if we don't support the architectures specified.
638  exit 0 if ($HadArch && scalar(@Archs) == 0);
639
640  foreach my $file (@Files) {
641    # Determine the language for the file.
642    my $FileLang = $Lang;
643
644    if (!defined($FileLang)) {
645      # Infer the language from the extension.
646      if ($file =~ /[.]([^.]+)$/) {
647        $FileLang = $LangMap{$1};
648      }
649    }
650
651    # FileLang still not defined?  Skip the file.
652    next if (!defined $FileLang);
653
654    # Language not accepted?
655    next if (!defined $LangsAccepted{$FileLang});
656
657    my @CmdArgs;
658    my @AnalyzeArgs;
659
660    if ($FileLang ne 'unknown') {
661      push @CmdArgs, '-x', $FileLang;
662    }
663
664    if (defined $StoreModel) {
665      push @AnalyzeArgs, "-analyzer-store=$StoreModel";
666    }
667
668    if (defined $ConstraintsModel) {
669      push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
670    }
671
672    if (defined $InternalStats) {
673      push @AnalyzeArgs, "-analyzer-stats";
674    }
675
676    if (defined $Analyses) {
677      push @AnalyzeArgs, split '\s+', $Analyses;
678    }
679
680    if (defined $Plugins) {
681      push @AnalyzeArgs, split '\s+', $Plugins;
682    }
683
684    if (defined $OutputFormat) {
685      push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
686      if ($OutputFormat =~ /plist/) {
687        # Change "Output" to be a file.
688        my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
689                               DIR => $HtmlDir);
690        $ResultFile = $f;
691        # If the HtmlDir is not set, we should clean up the plist files.
692        if (!defined $HtmlDir || -z $HtmlDir) {
693          $CleanupFile = $f;
694        }
695      }
696    }
697    if (defined $ConfigOptions) {
698      push @AnalyzeArgs, split '\s+', $ConfigOptions;
699    }
700
701    push @CmdArgs, @CompileOpts;
702    push @CmdArgs, $file;
703
704    if (scalar @Archs) {
705      foreach my $arch (@Archs) {
706        my @NewArgs;
707        push @NewArgs, '-arch', $arch;
708        push @NewArgs, @CmdArgs;
709        Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
710                $Verbose, $HtmlDir, $file);
711      }
712    }
713    else {
714      Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
715              $Verbose, $HtmlDir, $file);
716    }
717  }
718}
719
720exit($Status >> 8);
721