http://qs321.pair.com?node_id=501932

rupesh has asked for the wisdom of the Perl Monks concerning the following question:


Greetings Monks,
I've ran into a small situation and here I am to seek the help of you esteemed.

This is the requirement - to emulate a windows-explorer like directory tree structure (using perl, ofcourse)
Oh, and the directories that we're talking of is from a linux box.
So, considering I have a recursive directory listing in the form like this:
examples/html: examples/html/bars: examples/html/headers: examples/html/links: examples/html/lists: examples/html/menus: examples/html/rgb: examples/html/tables: examples/ps: examples/ps/marks: examples/splash: examples/splash/dropbox: examples/splash/frame: examples/splash/hair: examples/splash/icon: examples/splash/menu: examples/splash/menubar:
..the output that is required is a kind like this:
<ul> <li><a href="examples/">examples/</a> <ul> <li><a href="examples/html">html/</a> <ul> <li><a href="examples/html/bars/">bars</a></li> <li><a href="examples/html/headers/">headers</a></li> <li><a href="examples/html/links/">links</a></li> <li><a href="examples/html/lists/">lists</a></li> <li><a href="examples/html/menus/">menus</a></li> <li><a href="examples/html/rgb/">rgb</a></li> <li><a href="examples/html/tables/">tables</a></li> + </ul> </li> <li><a href="examples/ps/"">ps/</a> <ul> <li><a href="examples/ps/marks">marks</a></li> </ul> </li> <li><a href="examples/splash/">splash/</a> <ul> <li><a href="examples/splash/dropbox/">dropbox</a></li +> <li><a href="examples/splash/frame/">frame</a></li> <li><a href="examples/splash/hair/">hair</a></li> <li><a href="examples/splash/icon/">icon</a></li> <li><a href="examples/splash/menu/">menu</a></li> <li><a href="examples/splash/menubar/">menubar</a></li +> </ul> </li> </ul> </li> </ul>

The Unordered-List will then be used along with a JavaScript code and a Style Sheet making it a tree-like expandable struture.
I'm finding the making of the unordered list a bit complicated - from the raw data. (Array of arrays, multiple arrays etc.)
And this is only the directories and we aren't listing any files at all.

Any help is appreciated.

Thanks,
Rupesh.

Replies are listed 'Best First'.
Re: Directory Tree Structure
by marto (Cardinal) on Oct 21, 2005 at 09:47 UTC
    Hi Rupesh,

    Have a look at the File::Find module.
    You can use it to traverse the directory, I think once you look at the documentation that you will be able to determine how to use it in conjunction with your JavaScript and Style sheet.

    Hope this helps.

    Martin

      Thanks marto,
      I've had a look at the module and also File::Find::Recurse.
      The issue is, how do I structure the directories in the form of a tree?

      Here's what I need to identify:
      The main directory (done)
      All the subdirectories for the main directory (done)
      Each subdirectory's subdirectories (confusing, but doable)
      ..recuresively (uh..uh)
      and then, put it all in one data structure, wherein I can identify specific points where I can start and end the  <li> and the <ul>tags.

      Thanks,
      Rupesh.
        Hi rupesh,

        "Each subdirectory's subdirectories (confusing, but doable)
        ..recuresively (uh..uh)"

        Are you having difficulty doing this recursively?
        As a little example look at this code below:
        #!/usr/bin/perl use strict; use warnings; use File::Find; my $TargetPath = $ARGV[0]; find (\&ProcessTree,$TargetPath); sub ProcessTree { print "Directory: $File::Find::name\n" if -d; print "File: $File::Find::name\n" unless -d; }

        Call this script with the path you want to traverse as an argument.
        Are you planning on using the HTML::Template module?
        I hope the above example has given you an idea of how to tackle this.
        Let me know how you get on.

        Martin
        I've always stuck with File::Find and never really got involved into its fancy variants. I think you can print some opening tags as a preprocess action and some closing one at postprocess time. Check the documentation: there may be alternative ways, but this should be easy enough to be left as an exercise to the reader...
Re: Directory Tree Structure
by graff (Chancellor) on Oct 21, 2005 at 12:51 UTC
    Given that you already have the listing of directories (presumably as created by a command line like  find examples -type d), I think it's pointless to talk about using File::Find (which is certainly not an improvement over the unix "find" utility).

    As I understand it, the question is how to parse that listing into an appropriate html structure to render it as a nested set of unordered lists. This should be fairly simple, given that the input list is already sorted as shown in your example:

    use strict; # let's assume you read the list from stdin: my @paths = <>; chomp @paths; s/:$// for @paths; # don't want the punctuation my $prev_path = shift @paths; my $prev_depth = ( $prev_path =~ tr{/}{/} ); # count /'s my $indent = 1; print "<UL>\n <LI> $prev_path: </LI>\n"; for ( @paths ) { my $depth_now = ( tr{/}{/} ); if ( index( $_, "$prev_path/" ) == 0 ) { # $prev_path is contained within this one, so indent print ' ' x $indent, "<UL>\n"; $indent++; } elsif ( $depth_now < $prev_depth ) { # need to back off the indentation while ( $depth_now < $prev_depth ) { $indent--; print ' ' x $indent, "</UL>\n"; $prev_depth--; } } print ' ' x $indent, "<LI> $_: </LI>\n"; # put ":" back in $prev_path = $_; $prev_depth = $depth_now; } while ( $indent ) { $indent--; print ' ' x $indent, "</UL>\n"; } __OUTPUT__ <UL> <LI> examples/html: </LI> <UL> <LI> examples/html/bars: </LI> <LI> examples/html/headers: </LI> <LI> examples/html/links: </LI> <LI> examples/html/lists: </LI> <LI> examples/html/menus: </LI> <LI> examples/html/rgb: </LI> <LI> examples/html/tables: </LI> </UL> <LI> examples/ps: </LI> <UL> <LI> examples/ps/marks: </LI> </UL> <LI> examples/splash: </LI> <UL> <LI> examples/splash/dropbox: </LI> <LI> examples/splash/frame: </LI> <LI> examples/splash/hair: </LI> <LI> examples/splash/icon: </LI> <LI> examples/splash/menu: </LI> <LI> examples/splash/menubar: </LI> </UL> </UL>
    (updated to fix one of the comments in the code; also, looking more closely at the OP, I realize that I left out some of the desired formatting in the LI elements: having an href attribute containing the full path, and using just the last component of the path as the displayed text. I'll leave that as an exercise... it should be easy enough to work out.)

      graff, Thanks.

      It's just what I wanted.

      Sad, though, that you can do a "++" only once. ;).

      Cheers,
      Rupesh.
      graff,

      I know that this is almost 4 years later, but I see a problem with the output of the code. It would cause several HTML validation errors.

      If you please look at your output at <LI> examples/html: </LI>. The </li> needs to be down after </ul> after <LI> examples/html/tables: </LI> since the indented unordered list is within the above list item.

      I have tried fixing it and will keep trying in the meantime, but so far I have had only a little success.

      <ul> <li>Foo level 1 <ul> <li>Foo level 2 <ul> <li>Bar</li> <li>Baz</li> </ul> </li> #This closes Foo level 2 <li>Qux</li> <li>Quux</li> </ul> </li> #This closes Foo level 1 <li>Widget</li> </ul>
      Have a nice day!
      Lady Aleena
        Lady Aleena,

        Nice to hear from you again. What particular method are you using for html validation? (That is, who says that <ul>...</ul> positioned between </li>...<li> is invalid?) UPDATE: almut's reply clears this up -- I'm wrong, and the markup I posted above is Not Good™.

        I'm somewhat skeptical that the output I posted above constitutes an error in html syntax. Despite knowing that this markup is invalid, it may be worth pointing out that I stuck it into a file surrounded by <html>...</html> and loaded it into Safari and Firefox, and they both render it as I would expect (i.e. as intended). So in that sense at least, it works.

        Having said that, I'll acknowledge that the following layout for the markup also works, and I can well imagine that one or another validator might it has been shown that html validators should insist on this version (even though I don't see it as a necessary condition it seems to work either way):

        <UL> <LI> examples/html: <UL> <LI> examples/html/bars: </LI> <LI> examples/html/headers: </LI> <LI> examples/html/links: </LI> <LI> examples/html/lists: </LI> <LI> examples/html/menus: </LI> <LI> examples/html/rgb: </LI> <LI> examples/html/tables: </LI> </UL> </LI> <LI> examples/ps: <UL> <LI> examples/ps/marks: </LI> </UL> </LI> <LI> examples/splash: <UL> <LI> examples/splash/dropbox: </LI> <LI> examples/splash/frame: </LI> <LI> examples/splash/hair: </LI> <LI> examples/splash/icon: </LI> <LI> examples/splash/menu: </LI> <LI> examples/splash/menubar: </LI> </UL> </LI> </UL>
        If you are having trouble getting the code to output this pattern, show the code you have so far, and we'll see what's up with that.
Re: Directory Tree Structure
by martinvi (Monk) on Oct 21, 2005 at 10:00 UTC

    File::Find is the obvious thing for getting the data. When you have the data in some structure, you'll print them with HTML::Template.

    JavaScript and CSS go to the template-file along with some HTML and HTML::Templates "meta tags".

    The documentation is really easy and full of useful examples.

Re: Directory Tree Structure
by mickep76 (Beadle) on Oct 05, 2009 at 09:19 UTC

    Hi

    This is one example of using recursion to solve it.

    use strict; my $path = '/var/www/html'; print "<ul>\n"; recurse_path($path, " "); print "</ul>\n"; sub recurse_path { my $path = shift; my $padding = shift; my $dir = $path; $dir =~ s/.*\///g; print "$padding<li><a href=\"$path\">$dir</a>\n"; my $has_subdir = 0; foreach(glob("$path/*")) { if(-d $_ && ! $has_subdir) { print "$padding<ul>\n"; $has_subdir = 1; recurse_path($_, $padding . " ") } elsif(-d $_) { recurse_path($_, $padding . " ") } } if($has_subdir) { print "$padding</ul>\n" } }

      Hello mickep76,

      I know it's been a few months since you posted this, but I just saw it. FYI, you aren't closing the list items (</li>). I would give it a shot, but your recursion is confusing me a bit.

      Have a nice day!
      Lady Aleena