[Bio] / FigKernelPackages / Tracer.pm Repository:
ViewVC logotype

Diff of /FigKernelPackages/Tracer.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.34, Fri Jan 6 05:34:21 2006 UTC revision 1.52, Wed Jun 14 01:12:42 2006 UTC
# Line 19  Line 19 
19    
20      require Exporter;      require Exporter;
21      @ISA = ('Exporter');      @ISA = ('Exporter');
22      @EXPORT = qw(Trace T TSetup QTrace Confess Cluck Min Max Assert Open OpenDir TICK StandardSetup);      @EXPORT = qw(Trace T TSetup QTrace Confess Cluck Min Max Assert Open OpenDir TICK StandardSetup ScriptSetup ScriptFinish Insure ChDir);
23      @EXPORT_OK = qw(GetFile GetOptions Merge MergeOptions ParseCommand ParseRecord UnEscape Escape);      @EXPORT_OK = qw(GetFile GetOptions Merge MergeOptions ParseCommand ParseRecord UnEscape Escape);
24      use strict;      use strict;
25      use Carp qw(longmess croak);      use Carp qw(longmess croak);
26      use CGI;      use CGI;
27        use Cwd;
28      use FIG_Config;      use FIG_Config;
29      use PageBuilder;      use PageBuilder;
30      use Digest::MD5;      use Digest::MD5;
31        use File::Basename;
32        use File::Path;
33        use File::stat;
34    
35  =head1 Tracing and Debugging Helpers  =head1 Tracing and Debugging Helpers
36    
# Line 208  Line 212 
212    
213  =head3 StandardSetup  =head3 StandardSetup
214    
215  C<< my ($options, @parameters) = StandardSetup(\@categories, \%options, @ARGV); >>  C<< my ($options, @parameters) = StandardSetup(\@categories, \%options, $parmHelp, @ARGV); >>
216    
217  This method performs standard command-line parsing and tracing setup. The return  This method performs standard command-line parsing and tracing setup. The return
218  values are a hash of the command-line options and a list of the positional  values are a hash of the command-line options and a list of the positional
# Line 255  Line 259 
259      TransactFeatures -trace=3 -sql register ../xacts IDs.tbl      TransactFeatures -trace=3 -sql register ../xacts IDs.tbl
260    
261  Standard tracing is output to the standard output and echoed to the file  Standard tracing is output to the standard output and echoed to the file
262  C<trace.log> in the FIG temporary directory.  C<trace>I<$$>C<.log> in the FIG temporary directory, where I<$$> is the
263    process ID. You can also specify the C<user> parameter to put a user ID
264    instead of a process ID in the trace file name. So, for example
265    
266  The default trace level is 3. This dumps out all SQL commands if SQL tracing  The default trace level is 2. To get all messages, specify a trace level of 4.
267  is turned on and tends to produce one flurry of messages per genome. To get all  For a genome-by-genome update, use 3.
268  messages, specify a trace level of 4. For generally quiet output, use 2.  
269        TransactFeatures -trace=3 -sql -user=Bruce register ../xacts IDs.tbl
270    
271    would send the trace output to C<traceBruce.log> in the temporary directory.
272    
273  The I<options> parameter is a reference to a hash containing the command-line  The I<options> parameter is a reference to a hash containing the command-line
274  options and their default values. Command-line options may be in the form of switches  options, their default values, and an explanation of what they mean. Command-line
275  or keywords. In the case of a switch, the option value is 1 if it is specified and  options may be in the form of switches or keywords. In the case of a switch, the
276  0 if it is not specified. In the case of a keyword, the value is separated from the  option value is 1 if it is specified and 0 if it is not specified. In the case
277  option name by an equal sign. You can see this last in the command-line example above.  of a keyword, the value is separated from the option name by an equal sign. You
278    can see this last in the command-line example above.
279    
280    You can specify a different default trace level by setting C<$options->{trace}>
281    prior to calling this method.
282    
283  An example at this point would help. Consider, for example, the command-line utility  An example at this point would help. Consider, for example, the command-line utility
284  C<TransactFeatures>. It accepts a list of positional parameters plus the options  C<TransactFeatures>. It accepts a list of positional parameters plus the options
# Line 273  Line 286 
286  the following code.  the following code.
287    
288      my ($options, @parameters) = Tracer::StandardSetup(["DocUtils"],      my ($options, @parameters) = Tracer::StandardSetup(["DocUtils"],
289                                                        { trace => 3, sql => 0,                          { safe => [0, "use database transactions"],
290                                                          safe => 0, noAlias => 0,                            noAlias => [0, "do not expect aliases in CHANGE transactions"],
291                                                          start => ' ', tblFiles => 0},                            start => [' ', "start with this genome"],
292                              tblFiles => [0, "output TBL files containing the corrected IDs"] },
293                            "command transactionDirectory IDfile",
294                                                      @ARGV);                                                      @ARGV);
295    
296    
# Line 304  Line 319 
319  need to be added in the future, they can be processed by this method without  need to be added in the future, they can be processed by this method without
320  upsetting the command-line utilities.  upsetting the command-line utilities.
321    
322    If the C<background> option is specified on the command line, then the
323    standard and error outputs will be directed to files in the temporary
324    directory, using the same suffix as the trace file. So, if the command
325    line specified
326    
327        -user=Bruce -background
328    
329    then the trace output would go to C<traceBruce.log>, the standard output to
330    C<outBruce.log>, and the error output to C<errBruce.log>. This is designed to
331    simplify starting a command in the background.
332    
333    Finally, if the special option C<-h> is specified, the option names will
334    be traced at level 0 and the program will exit without processing.
335    This provides a limited help capability. For example, if the user enters
336    
337        TransactFeatures -h
338    
339    he would see the following output.
340    
341        TransactFeatures [options] command transactionDirectory IDfile
342            -trace    tracing level (default 2)
343            -sql      trace SQL commands
344            -safe     use database transactions
345            -noAlias  do not expect aliases in CHANGE transactions
346            -start    start with this genome
347            -tblFiles output TBL files containing the corrected IDs
348    
349    The caller has the option of modifying the tracing scheme by placing a value
350    for C<trace> in the incoming options hash. The default value can be overridden,
351    or the tracing to the standard output can be turned off by suffixing a minus
352    sign to the trace level. So, for example,
353    
354        { trace => [0, "tracing level (default 0)"],
355           ...
356    
357    would set the default trace level to 0 instead of 2, while
358    
359        { trace => ["2-", "tracing level (default 2)"],
360           ...
361    
362    would leave the default at 2, but trace only to the log file, not to the
363    standard output.
364    
365  The parameters to this method are as follows.  The parameters to this method are as follows.
366    
367  =over 4  =over 4
# Line 317  Line 375 
375  =item options  =item options
376    
377  Reference to a hash containing the legal options for the current command mapped  Reference to a hash containing the legal options for the current command mapped
378  to their default values. The use can override the defaults by specifying the  to their default values and descriptions. The user can override the defaults
379  options as command-line switches prefixed by a hyphen. Tracing-related options  by specifying the options as command-line switches prefixed by a hyphen.
380  may be added to this hash.  Tracing-related options may be added to this hash. If the C<-h> option is
381    specified on the command line, the option descriptions will be used to
382    explain the options. To turn off tracing to the standard output, add a
383    minus sign to the value for C<trace> (see above).
384    
385    =item parmHelp
386    
387  =item ARGV  A string that vaguely describes the positional parameters. This is used
388    if the user specifies the C<-h> option.
389    
390    =item argv
391    
392  List of command line parameters, including the option switches, which must  List of command line parameters, including the option switches, which must
393  precede the positional parameters and be prefixed by a hyphen.  precede the positional parameters and be prefixed by a hyphen.
# Line 339  Line 405 
405    
406  sub StandardSetup {  sub StandardSetup {
407      # Get the parameters.      # Get the parameters.
408      my ($categories, $options, @argv) = @_;      my ($categories, $options, $parmHelp, @argv) = @_;
409      # Add the tracing options.      # Add the tracing options.
410      $options->{trace} = 3;      if (! exists $options->{trace}) {
411      $options->{sql} = 0;          $options->{trace} = [2, "tracing level"];
412        }
413        $options->{sql} = [0, "turn on SQL tracing"];
414        $options->{h} = [0, "display command-line options"];
415        $options->{user} = [$$, "trace log file name suffix"];
416        $options->{background} = [0, "spool standard and error output"];
417        # Create a parsing hash from the options hash. The parsing hash
418        # contains the default values rather than the default value
419        # and the description. While we're at it, we'll memorize the
420        # length of the longest option name.
421        my $longestName = 0;
422        my %parseOptions = ();
423        for my $key (keys %{$options}) {
424            if (length $key > $longestName) {
425                $longestName = length $key;
426            }
427            $parseOptions{$key} = $options->{$key}->[0];
428        }
429      # Parse the command line.      # Parse the command line.
430      my ($retOptions, @retParameters) = ParseCommand($options, @argv);      my ($retOptions, @retParameters) = ParseCommand(\%parseOptions, @argv);
431        # Get the logfile suffix.
432        my $suffix = $retOptions->{user};
433        # Check for background mode.
434        if ($retOptions->{background}) {
435            my $outFileName = "$FIG_Config::temp/out$suffix.log";
436            my $errFileName = "$FIG_Config::temp/err$suffix.log";
437            open STDOUT, ">$outFileName";
438            open STDERR, ">$errFileName";
439        }
440      # Now we want to set up tracing. First, we need to know if SQL is to      # Now we want to set up tracing. First, we need to know if SQL is to
441      # be traced.      # be traced.
442      my @cats = @{$categories};      my @cats = @{$categories};
# Line 353  Line 445 
445      }      }
446      # Add the default categories.      # Add the default categories.
447      push @cats, "Tracer", "FIG";      push @cats, "Tracer", "FIG";
448      # Next, we create the category string by prefixing the trace level      # Next, we create the category string by joining the categories.
449      # and joining the categories.      my $cats = join(" ", @cats);
450      my $cats = join(" ", $options->{trace}, @cats);      # Check to determine whether or not the caller wants to turn off tracing
451        # to the standard output.
452        my $traceLevel = $retOptions->{trace};
453        my $textOKFlag = 1;
454        if ($traceLevel =~ /^(.)-/) {
455            $traceLevel = $1;
456            $textOKFlag = 0;
457        }
458        # Now we set up the trace mode.
459        my $traceMode;
460        # Verify that we can open a file in the FIG temporary directory.
461        my $traceFileName = "$FIG_Config::temp/trace$suffix.log";
462        if (open TESTTRACE, ">$traceFileName") {
463            # Here we can trace to a file.
464            $traceMode = ">$traceFileName";
465            if ($textOKFlag) {
466                # Echo to standard output if the text-OK flag is set.
467                $traceMode = "+$traceMode";
468            }
469            # Close the test file.
470            close TESTTRACE;
471        } else {
472            # Here we can't trace to a file. We trace to the standard output if it's
473            # okay, and the error log otherwise.
474            if ($textOKFlag) {
475                $traceMode = "TEXT";
476            } else {
477                $traceMode = "WARN";
478            }
479        }
480      # Now set up the tracing.      # Now set up the tracing.
481      TSetup($cats, "+>$FIG_Config::temp/trace.log");      TSetup("$traceLevel $cats", $traceMode);
482        # Check for the "h" option. If it is specified, dump the command-line
483        # options and exit the program.
484        if ($retOptions->{h}) {
485            $0 =~ m#[/\\](\w+)(\.pl)?$#i;
486            Trace("$1 [options] $parmHelp") if T(0);
487            for my $key (sort keys %{$options}) {
488                my $name = Pad($key, $longestName, 0, ' ');
489                my $desc = $options->{$key}->[1];
490                if ($options->{$key}->[0]) {
491                    $desc .= " (default " . $options->{$key}->[0] . ")";
492                }
493                Trace("  $name $desc") if T(0);
494            }
495            exit(0);
496        }
497      # Return the parsed parameters.      # Return the parsed parameters.
498      return ($retOptions, @retParameters);      return ($retOptions, @retParameters);
499  }  }
# Line 917  Line 1053 
1053          # Convert it to lower case before we hash it.          # Convert it to lower case before we hash it.
1054          $category = lc $category;          $category = lc $category;
1055          # Use the category and tracelevel to compute the result.          # Use the category and tracelevel to compute the result.
1056            if (ref $traceLevel) {
1057                Confess("Bad trace level.");
1058            } elsif (ref $TraceLevel) {
1059                Confess("Bad trace config.");
1060            }
1061          $retVal = ($traceLevel <= $TraceLevel && ($AllTrace || exists $Categories{$category}));          $retVal = ($traceLevel <= $TraceLevel && ($AllTrace || exists $Categories{$category}));
1062      }      }
1063      # Return the computed result.      # Return the computed result.
# Line 1211  Line 1352 
1352    
1353  C<< my @fileContents = Tracer::GetFile($fileName); >>  C<< my @fileContents = Tracer::GetFile($fileName); >>
1354    
1355  Return the entire contents of a file.      or
1356    
1357    C<< my $fileContents = Tracer::GetFile($fileName); >>
1358    
1359    Return the entire contents of a file. In list context, line-ends are removed and
1360    each line is a list element. In scalar context, line-ends are replaced by C<\n>.
1361    
1362  =over 4  =over 4
1363    
# Line 1222  Line 1368 
1368  =item RETURN  =item RETURN
1369    
1370  In a list context, returns the entire file as a list with the line terminators removed.  In a list context, returns the entire file as a list with the line terminators removed.
1371  In a scalar context, returns the entire file as a string.  In a scalar context, returns the entire file as a string. If an error occurs opening
1372    the file, an empty list will be returned.
1373    
1374  =back  =back
1375    
# Line 1670  Line 1817 
1817      return `$commandString`;      return `$commandString`;
1818  }  }
1819    
1820    =head3 ScriptSetup
1821    
1822    C<< my ($query, $varHash) = ScriptSetup(); >>
1823    
1824    Perform standard tracing and debugging setup for scripts. The value returned is
1825    the CGI object followed by a pre-built variable hash.
1826    
1827    The C<Trace> query parameter is used to determine whether or not tracing is active and
1828    which trace modules (other than C<Tracer> and C<FIG>) should be turned on. Specifying
1829    the C<CGI> trace module will trace parameter and environment information. Parameters are
1830    traced at level 3 and environment variables at level 4. At the end of the script, the
1831    client should call L</ScriptFinish> to output the web page.
1832    
1833    =cut
1834    
1835    sub ScriptSetup {
1836        # Get the CGI query object.
1837        my $query = CGI->new();
1838        # Check for tracing. Set it up if the user asked for it.
1839        if ($query->param('Trace')) {
1840            # Set up tracing to be queued for display at the bottom of the web page.
1841            TSetup($query->param('Trace') . " FIG Tracer", "QUEUE");
1842            # Trace the parameter and environment data.
1843            if (T(CGI => 3)) {
1844                # Here we want to trace the parameter data.
1845                my @names = $query->param;
1846                for my $parmName (sort @names) {
1847                    # Note we skip "Trace", which is for our use only.
1848                    if ($parmName ne 'Trace') {
1849                        my @values = $query->param($parmName);
1850                        Trace("CGI: $parmName = " . join(", ", @values));
1851                    }
1852                }
1853            }
1854            if (T(CGI => 4)) {
1855                # Here we want the environment data too.
1856                for my $envName (sort keys %ENV) {
1857                    Trace("ENV: $envName = $ENV{$envName}");
1858                }
1859            }
1860        } else {
1861            # Here tracing is to be turned off. All we allow is errors traced into the
1862            # error log.
1863            TSetup("0", "WARN");
1864        }
1865        # Create the variable hash.
1866        my $varHash = { DebugData => '' };
1867        # If we're in DEBUG mode, set up the debug mode data for forms.
1868        if (Tracer::DebugMode) {
1869            $varHash->{DebugData} = GetFile("Html/DebugFragment.html");
1870        }
1871        # Return the query object and variable hash.
1872        return ($query, $varHash);
1873    }
1874    
1875    =head3 ScriptFinish
1876    
1877    C<< ScriptFinish($webData, $varHash); >>
1878    
1879    Output a web page at the end of a script. Either the string to be output or the
1880    name of a template file can be specified. If the second parameter is omitted,
1881    it is assumed we have a string to be output; otherwise, it is assumed we have the
1882    name of a template file. The template should have the variable C<DebugData>
1883    specified in any form that invokes a standard script. If debugging mode is turned
1884    on, a form field will be put in that allows the user to enter tracing data.
1885    Trace messages will be placed immediately before the terminal C<BODY> tag in
1886    the output, formatted as a list.
1887    
1888    A typical standard script would loook like the following.
1889    
1890        BEGIN {
1891            # Print the HTML header.
1892            print "CONTENT-TYPE: text/html\n\n";
1893        }
1894        use Tracer;
1895        use CGI;
1896        use FIG;
1897        # ... more uses ...
1898    
1899        my ($query, $varHash) = ScriptSetup();
1900        eval {
1901            # ... get data from $query, put it in $varHash ...
1902        };
1903        if ($@) {
1904            Trace("Script Error: $@") if T(0);
1905        }
1906        ScriptFinish("Html/MyTemplate.html", $varHash);
1907    
1908    The idea here is that even if the script fails, you'll see trace messages and
1909    useful output.
1910    
1911    =over 4
1912    
1913    =item webData
1914    
1915    A string containing either the full web page to be written to the output or the
1916    name of a template file from which the page is to be constructed. If the name
1917    of a template file is specified, then the second parameter must be present;
1918    otherwise, it must be absent.
1919    
1920    =item varHash (optional)
1921    
1922    If specified, then a reference to a hash mapping variable names for a template
1923    to their values. The template file will be read into memory, and variable markers
1924    will be replaced by data in this hash reference.
1925    
1926    =back
1927    
1928    =cut
1929    
1930    sub ScriptFinish {
1931        # Get the parameters.
1932        my ($webData, $varHash) = @_;
1933        # Check for a template file situation.
1934        my $outputString;
1935        if (defined $varHash) {
1936            # Here we have a template file. We need to apply the variables to the template.
1937            $outputString = PageBuilder::Build("<$webData", $varHash, "Html");
1938        } else {
1939            # Here the user gave us a raw string.
1940            $outputString = $webData;
1941        }
1942        # Check for trace messages.
1943        if ($Destination eq "QUEUE") {
1944            # We have trace messages, so we want to put them at the end of the body. This
1945            # is either at the end of the whole string or at the beginning of the BODY
1946            # end-tag.
1947            my $pos = length $outputString;
1948            if ($outputString =~ m#</body>#gi) {
1949                $pos = (pos $outputString) - 7;
1950            }
1951            substr $outputString, $pos, 0, QTrace('Html');
1952        }
1953        # Write the output string.
1954        print $outputString;
1955    }
1956    
1957    =head3 Insure
1958    
1959    C<< Insure($dirName); >>
1960    
1961    Insure a directory is present.
1962    
1963    =over 4
1964    
1965    =item dirName
1966    
1967    Name of the directory to check. If it does not exist, it will be created.
1968    
1969    =back
1970    
1971    =cut
1972    
1973    sub Insure {
1974        my ($dirName) = @_;
1975        if (! -d $dirName) {
1976            Trace("Creating $dirName directory.") if T(2);
1977            eval { mkpath $dirName; };
1978            if ($@) {
1979                Confess("Error creating $dirName: $@");
1980            }
1981        }
1982    }
1983    
1984    =head3 ChDir
1985    
1986    C<< ChDir($dirName); >>
1987    
1988    Change to the specified directory.
1989    
1990    =over 4
1991    
1992    =item dirName
1993    
1994    Name of the directory to which we want to change.
1995    
1996    =back
1997    
1998    =cut
1999    
2000    sub ChDir {
2001        my ($dirName) = @_;
2002        if (! -d $dirName) {
2003            Confess("Cannot change to directory $dirName: no such directory.");
2004        } else {
2005            Trace("Changing to directory $dirName.") if T(4);
2006            my $okFlag = chdir $dirName;
2007            if (! $okFlag) {
2008                Confess("Error switching to directory $dirName.");
2009            }
2010        }
2011    }
2012    
2013    =head3 SetPermissions
2014    
2015    C<< Tracer::SetPermissions($dirName, $group, $mask, %otherMasks); >>
2016    
2017    Set the permissions for a directory and all the files and folders inside it.
2018    In addition, the group ownership will be changed to the specified value.
2019    
2020    This method is more vulnerable than most to permission and compatability
2021    problems, so it does internal error recovery.
2022    
2023    =over 4
2024    
2025    =item dirName
2026    
2027    Name of the directory to process.
2028    
2029    =item group
2030    
2031    Name of the group to be assigned.
2032    
2033    =item mask
2034    
2035    Permission mask. Bits that are C<1> in this mask will be ORed into the
2036    permission bits of any file or directory that does not already have them
2037    set to 1.
2038    
2039    =item otherMasks
2040    
2041    Map of search patterns to permission masks. If a directory name matches
2042    one of the patterns, that directory and all its members and subdirectories
2043    will be assigned the new pattern. For example, the following would
2044    assign 01664 to most files, but would use 01777 for directories named C<tmp>.
2045    
2046        Tracer::SetPermissions($dirName, 'fig', 01664, '^tmp$' => 01777);
2047    
2048    The list is ordered, so the following would use 0777 for C<tmp1> and
2049    0666 for C<tmp>, C<tmp2>, or C<tmp3>.
2050    
2051        Tracer::SetPermissions($dirName, 'fig', 01664, '^tmp1' => 0777,
2052                                                       '^tmp' => 0666);
2053    
2054    Note that the pattern matches are all case-insensitive, and only directory
2055    names are matched, not file names.
2056    
2057    =back
2058    
2059    =cut
2060    
2061    sub SetPermissions {
2062        # Get the parameters.
2063        my ($dirName, $group, $mask, @otherMasks) = @_;
2064        # Set up for error recovery.
2065        eval {
2066            # Switch to the specified directory.
2067            ChDir($dirName);
2068            # Get the group ID.
2069            my $gid = getgrnam($group);
2070            # Get the mask for tracing.
2071            my $traceMask = sprintf("%04o", $mask) . "($mask)";
2072            Trace("Fixing permissions for directory $dirName using group $group($gid) and mask $traceMask.") if T(2);
2073            my $fixCount = 0;
2074            my $lookCount = 0;
2075            # @dirs will be a stack of directories to be processed.
2076            my @dirs = (getcwd());
2077            while (scalar(@dirs) > 0) {
2078                # Get the current directory.
2079                my $dir = pop @dirs;
2080                # Check for a match to one of the specified directory names. To do
2081                # that, we need to pull the individual part of the name off of the
2082                # whole path.
2083                my $simpleName = $dir;
2084                if ($dir =~ m!/(.+)$!) {
2085                    $simpleName = $1;
2086                }
2087                # Search for a match.
2088                my $match = 0;
2089                my $i;
2090                for ($i = 0; $i < $#otherMasks && ! $match; $i += 2) {
2091                    my $pattern = $otherMasks[$i];
2092                    if ($simpleName =~ /$pattern/i) {
2093                        $match = 1;
2094                    }
2095                }
2096                # Check for a match. Note we use $i-1 because the loop added 2
2097                # before terminating due to the match.
2098                if ($match && $otherMasks[$i-1] != $mask) {
2099                    # This directory matches one of the incoming patterns, and it's
2100                    # a different mask, so we process it recursively with that mask.
2101                    SetPermissions($dir, $group, $otherMasks[$i-1], @otherMasks);
2102                } else {
2103                    # Here we can process normally. Get all of the non-hidden members.
2104                    my @submems = OpenDir($dir, 1);
2105                    for my $submem (@submems) {
2106                        # Get the full name.
2107                        my $thisMem = "$dir/$submem";
2108                        Trace("Checking member $thisMem.") if T(4);
2109                        $lookCount++;
2110                        if ($lookCount % 1000 == 0) {
2111                            Trace("$lookCount members examined. Current is $thisMem. Mask is $traceMask") if T(3);
2112                        }
2113                        # Fix the group.
2114                        chown -1, $gid, $thisMem;
2115                        # Insure this member is not a symlink.
2116                        if (! -l $thisMem) {
2117                            # Get its info.
2118                            my $fileInfo = stat $thisMem;
2119                            # Only proceed if we got the info. Otherwise, it's a hard link
2120                            # and we want to skip it anyway.
2121                            if ($fileInfo) {
2122                                my $fileMode = $fileInfo->mode;
2123                                if (($fileMode & $mask) == 0) {
2124                                    # Fix this member.
2125                                    $fileMode |= $mask;
2126                                    chmod $fileMode, $thisMem;
2127                                    $fixCount++;
2128                                }
2129                                # If it's a subdirectory, stack it.
2130                                if (-d $thisMem) {
2131                                    push @dirs, $thisMem;
2132                                }
2133                            }
2134                        }
2135                    }
2136                }
2137            }
2138            Trace("$lookCount files and directories processed, $fixCount fixed.") if T(2);
2139        };
2140        # Check for an error.
2141        if ($@) {
2142            Confess("SetPermissions error: $@");
2143        }
2144    }
2145    
2146  1;  1;

Legend:
Removed from v.1.34  
changed lines
  Added in v.1.52

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3