Wednesday, May 14, 2008

Following your directories

While designing some "integrity checker" tool, I've found myself in trouble whenever I need to
manage directories...

It seems that erlang, for now, is not able to detect "symbolic links" (Unix definition). Opening "kernel/include/file.hrl" holds the truth... The module "filelib" doesn't have any thing related to 'links' neither.


So if you have some links that point to "." you'll observe that "filelib:fold_files" follow the link many times (hopefully its stop somewhere) but you loose some precious time and increase disk accesses...

Then I've rewrote the fold_files to detect links by searching in the path some identical elements:

checktree([]) ->
true;
checktree([_Elem,_Elem|_Rest]) ->
false;
checktree([_|Rest]) ->
checktree(Rest).


This is not really high quality but seems to work as expected...

I've also added some "maxdeep" functionality that prevent the script to go too many deeper.

I've named this module "wfile" for no particular reason :) and here's the full code:

-module(wfile).
-export([list/1, list/2]).

list(Dir) ->
Fun = fun(X, _Acc) -> io:format("+ ~s~n", [X]) end,
list(Dir, Fun).

list(Dir, Fun) ->
fold_files(Dir, Fun, []).

fold_files(Dir, Fun, Acc) ->
fold_files(Dir, true, Fun, Acc).

fold_files(Dir, Recursive, Fun, Acc) ->
fold_files1(Dir, Recursive, Fun, Acc).

fold_files1(Dir, Recursive, Fun, Acc) ->
case file:list_dir(Dir) of
{ok, Files} -> fold_files2(Files, Dir, Recursive, Fun, Acc);
{error, _} -> Acc
end.

fold_files2([], _Dir, _Recursive, _Fun, Acc) ->
Acc;
fold_files2([File|T], Dir, Recursive, Fun, Acc0) ->
FullName = filename:join(Dir, File),
case filelib:is_regular(FullName) of
true ->
Acc = Fun(FullName, Acc0),
fold_files2(T, Dir, Recursive, Fun, Acc);

false ->
case Recursive and filelib:is_dir(FullName) and maxdeep(FullName, 6) of
true ->
Acc1 = fold_files1(FullName, Recursive, Fun, Acc0),
fold_files2(T, Dir, Recursive, Fun, Acc1);
false ->
fold_files2(T, Dir, Recursive, Fun, Acc0)
end
end.

maxdeep(Filename, Max) ->
Elems = filename:split(Filename),
( Max > length(Elems) ) and checktree(Elems).

checktree([]) ->
true;
checktree([_Elem,_Elem|_Rest]) ->
false;
checktree([_|Rest]) ->
checktree(Rest).



To end to story, here's how I used this module:

erl> IC = integrity_checker:start().
erl> wfile:list("/home/rolphin/tmp", fun(X, Acc) -> IC ! {add, X}, io:format("added: ~s~n", [X]) end).

No comments:

Sticky