Previous Section Table of Contents Next Section

Expand Indented File List to Full Paths

This function walks through a file that has paths indicated by indentation level, as shown in the following example:


lang

    perl

        src

            math.pl

            input.pl

            readme

        lib

            tools.pm

    c

        inc

            profile.h

            names.h

        src

            update.c

        readme


The program is passed a match string as an argument. It scans the result of the diamond operator (<>) input (either STDIN, or filenames passed as the arguments following the match string), and for every match, it prints out the full path of the matched line. For example, if it were asked to find "readme" with the previous example as input, it would print out the following:


lang/perl/src/readme

lang/c/readme


The program tracks the text printed at each unique indent level up to the indent of the current line; if the current line is less indented than previous lines, any previous indents of an equal or greater number of spaces are no longer tracked. It uses the Perl feature that you can truncate an array by assigning to $#array, which is the variable that holds the index of the last element of the array.

Source Code


 1.     # arguments: matchstring [filename1 [filename2 ...]]

 2.

 3.     $match = shift @ARGV;

 4.

 5.     while (<>) {

 6.

 7.        # remove \n from input

 8.        chomp;

 9.

10.        # process all lines except completely blank ones

11.        unless (/^\s*$/) {

12.

13.            # how many initial spaces in the line?

14.            /( *)/;

15.            $spaces = $1;

16.

17.            # chop the array at that point

18.            $#indentlabels = $spaces;

19.

20.            # save line text after spaces

21.            $indentlabels[$spaces] = $';

22.

23.            # does the line match the match string

24.            if (/$match/) {

25.

26.                # display path

27.                foreach $l (@indentlabels) {

28.                   print $l if defined($l);

29.                }

30.                print "\n";

31.             }

32.

33.            # add the separator for future printing

34.            $indentlabels[$spaces] .= "/";

35.         }

36.     }


Suggestions

  1. Check the regular expressions on lines 11 and 14 to ensure that they are correct.

  2. What number of spaces at the beginning of a line is most likely to cause problems? Probably zero. Walk through the code with a line that has no spaces at the beginning.

  3. How long will a particular entry remain in the @indentlabels array?

Hints

Walk through the code with the following match strings and input files:

  1. Match twice at different indents:

    match eq "abc", file contains:

    
    abc
    
        abc
    
    

  2. Match once at indent after outdent:

    match eq "abc", file contains:

    
    test
    
            directory
    
      xyz
    
        abc
    
    

  3. Match is substring of actual value:

    match eq "abc", file contains:

    
    ab
    
        cd
    
            abcd
    
    

Explanation of the Bug

Line 15 has a B.expression error:


$spaces = $1;


At line 15, the $1 string contains the piece of the string that matched the part of the regular expression on line 14 that was in parentheses. In this case, the regular expression is ( *), so $1 contains a certain number of space characters. However, it is used in the program as if it contained the number of spaces as an integer. The actual spaces always evaluates to 0 when converted to an integer. Therefore, the program will treat every line as if it were indented 0 spaces, which means it will keep chopping @indentlabels back to one element and never display full paths as it is supposed to.

Line 15 needs to be changed to read as follows:


$spaces = length($1);


    Previous Section Table of Contents Next Section