1/*  Part of SWI-Prolog
    2
    3    Author:        Carlo Capelli
    4    E-mail:        cc.carlo.cap@gmail.com
    5    Copyright (C): Carlo Capelli
    6
    7    This program is free software; you can redistribute it and/or
    8    modify it under the terms of the GNU General Public License
    9    as published by the Free Software Foundation; either version 2
   10    of the License, or (at your option) any later version.
   11
   12    This program is distributed in the hope that it will be useful,
   13    but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15    GNU General Public License for more details.
   16
   17    You should have received a copy of the GNU Lesser General Public
   18    License along with this library; if not, write to the Free Software
   19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   20
   21    As a special exception, if you link this library with other files,
   22    compiled with a Free Software compiler, to produce an executable, this
   23    library does not by itself cause the resulting executable to be covered
   24    by the GNU General Public License. This exception does not however
   25    invalidate any other reasons why the executable file might be covered by
   26    the GNU General Public License.
   27*/
   28
   29:- module(dirtree,
   30	  [dirtree/2,                % +Root, -Tree
   31	   dirtree/3,                % +Path, +Curr, -Tree
   32	   sortree/2,                % +Tree, -Sorted
   33	   sortree/3,                % +Compare, +Tree, -Sorted
   34	   compare_by_attr/4,	     % +Attribute, +Item1, +Item2, -Rel
   35	   extension_embedding_enable/0,
   36	   extension_to_embed/1,     % +Ext
   37	   source_target/2,          % +Path, +XML
   38	   extensions_from_saved/1,  % -Extensions
   39	   counted_extensions/1,     % -CountedExtensions
   40	   get_ftp_ls_output/2,	     % +Stdout, -Ftped:list
   41	   get_ftp_ls_output//1,     % -Ftped:list
   42	   dirzap/1,                 % +Root
   43	   capture_attrs/3,	     % +Attr, +Elem, ?Values
   44	   assign_path/2
   45	  ]).   46
   47:- use_module([library(lambda),
   48	       library(dcg/basics),
   49	       library(aggregate),
   50	       library(xpath)
   51	      ]).

Filesystem listing to XML.

This library provides utilities to load directories content from filesystem to XML element/3 and some utility. It's also possible load text of selected files types (by extension).

Of interest could be the parsing of FTP command 'ls', allowing remote browsing after process_create with redirection of standard output.

*/

   65:- dynamic extension_to_embed/1.   66:- dynamic source_target/2.
 dirtree(+Root, ?Xml) is det
 dirtree(+Path, +Item, ?Xml) is det
start actual scan (need current path)
   73dirtree(P, T) :-
   74	dirtree(P, P, T).
   75
   76dirtree(P, Dir, element(dir, [name = Dir, size = S, path = P], D)) :-
   77	exists_directory(P),
   78	!,
   79	scandir(P, Dir, D),
   80	aggregate_all(sum(X),
   81		      (member(element(_, As, _), D),
   82		       memberchk(size = X, As)
   83		      ), S).
   84dirtree(P, File, element(file, A, Q)) :-
   85	(   catch((exists_file(P), size_file(P, S)), E, (print_message(error, E), S = 0))
   86	->  A = [name = File, size = S, path = P],
   87	    embed(P, Q)
   88	;   A = [name = File, size = 0, path = P]
   89	).
   90
   91scandir(P, _Dir, D) :-
   92	directory_files(P, L),
   93	findall(X, ( member(E, L), E \== '..', E \== '.',
   94		     format(atom(Q), '~w/~w', [P, E]),
   95		     dirtree(Q, E, X)), D).
sort XML elements by name
  102sortree(T, S) :-
  103	sortree(compare_by_attr(name), T, S).
  104
  105:- meta_predicate sortree(3, +, -).  106
  107sortree(C, element(E, As, Cs), element(E, As, Ss)) :-
  108	predsort(C, Cs, Ts),
  109	maplist(sortree, Ts, Ss).
  110sortree(_C, E, E).
  111
  112compare_by_attr(A, R, X, Y) :-
  113	xpath(X, /self(@A), Vx),	xpath(Y, /self(@A), Vy),	( Vx @< Vy -> R = < ; R = > ).
 extension_embedding_enable is det
call before dirtree to load source text lines (not parsed)
  121extension_embedding_enable :-
  122	maplist(\E^(  extension_to_embed(E)
  123		   -> true
  124		   ;  assert(extension_to_embed(E))), [pl, php, tpl]).
  125
  126embed(P, XLines) :-
  127	file_name_extension(_, X, P),
  128	extension_to_embed(X),
  129	setup_call_cleanup(open(P, read, H), fetch_lines(H, Lines), close(H)),
  130	maplist(\Line^XLine^(XLine = element(line, [text = Line], [])), Lines, XLines),
  131	!.
  132embed(_, []).
  133
  134fetch_lines(H, L) :-
  135	read_line_to_codes(H, Codes),
  136	( Codes == end_of_file ->
  137	  L = []
  138	 ;
  139	  atom_codes(A, Codes),
  140	  xml_quote_attribute(A, Q),
  141	  fetch_lines(H, T),
  142	  L = [Q|T]
  143	).
 extensions_from_saved(-Result) is det
search each DOM branch and extract extension
  149extensions_from_saved(Exts) :-
  150	source_target(_, XML),
  151	load_xml_file(XML, DOM),
  152	extensions_from_DOM(DOM, Exts).
  153
  154extensions_from_DOM(DOM, Exts) :-
  155	aggregate_all(set(X), (xpath(DOM, //file(@name), File),
  156			       file_name_extension(_, X, File)
  157			      ), Exts).
  158
  159%%	counted_extensions(Counted) is det.
  160%
  161%       get all extensions and count each file
  162%
  163counted_extensions(Counted) :-
  164	source_target(_, XML),
  165	load_xml_file(XML, DOM),
  166	setof(X = Count, (findall(Ext, (
  167				xpath(DOM, //file(@name), File),
  168				file_name_extension(_, Ext, File)), Exts),
  169			        aggregate(count, member(X, Exts), Count)
  170			 ), Counted).
  171
  172counted_extensions_old(Counted) :-
  173	source_target(_, XML),
  174	load_xml_file(XML, DOM),
  175	extensions_from_DOM(DOM, ExtS),
  176	maplist(\Ext^Count^(aggregate_all(count,
  177					  (xpath(DOM, //file(@name), File),					   file_name_extension(_, Ext, File)					  ), C), Count = (Ext = C)			   ), ExtS, Counted).
 get_ftp_ls_output(Stdout, Ftped)
parse output of ftp command 'ls ...'
  186get_ftp_ls_output(Stdout, Ftped) :-
  187	phrase_from_file(get_ftp_ls_output(Ftped), Stdout).
  188
  189eos([],[]).
  190get_ftp_ls_output([]) --> eos.
  191get_ftp_ls_output([E|Es]) -->
  192	ftp_ls_entry(E),
  193	get_ftp_ls_output(Es).
  194ftp_ls_entry(E) -->
  195	"d",
  196	entry(dir, E).
  197ftp_ls_entry(E) -->
  198	"-",
  199	entry(file, E).
  200
  201s --> " ", whites.
  202
  203permission([R, W, X]) -->
  204	[R, W, X].
  205
  206entry(Kind, element(Kind, [name = Name, size = Size, lastmod = T, user = User, group = Group, perm = P, c2 = I1], [])) -->
  207	permission(UserP),
  208	permission(GroupP),
  209	permission(OthersP),
  210	s,
  211	integer(I1),
  212	s,
  213	string(GroupC),
  214	s,
  215	string(UserC),
  216	s,
  217	integer(Size),
  218	timestamp(T),
  219	string(NameC), "\n",
  220	{ maplist(atom_codes, P, [UserP, GroupP, OthersP]),
  221	  maplist(atom_codes, [User, Name, Group], [UserC, NameC, GroupC])
  222	}.
  223
  224month(M, I) :-
  225	atom_codes(A, M),
  226	downcase_atom(A, D),
  227	nth1(I, [jan,feb,mar,apr,maj,jun,jul,aug,sep,oct,nov,dec], D)
  228	-> true
  229	;  I = 0.
  230
  231timestamp(date(YYYY,MO,DD,HH,MM,SS,0,-,-)) -->
  232	(   " ", [M,O,N], " ", [D1,D2], " ", [H1,H2], ":", [M1,M2], " "
  233	->  {maplist(number_codes, [DD,HH,MM], [[D1,D2], [H1,H2], [M1,M2]]), month([M,O,N], MO), YYYY=2012, SS=0}
  234	;   " ", [M,O,N], " ", [D1,D2], "  ", [H1,H2,H3,H4], " " ->
  235	    {maplist(number_codes, [DD,YYYY], [[D1,D2], [H1,H2,H3,H4]]),  month([M,O,N], MO), HH=0, MM=0, SS=0}
  236	).
  237
  238
  239%%	dirzap(+X:atom) is det.
  240%
  241%	remove directory (content is lost of course)
  242%
  243dirzap(X) :-
  244	dirtree(X, T),
  245	forall(xpath(T, //file(@path), Path),	       delete_file(Path)),	findall(Path, xpath(T, //dir(@path), Path), Dirs),	reverse(Dirs, RDirs),	maplist(delete_directory, RDirs).
 capture_attrs(+Attr, +Elem, ?Values) is det
get all Attr=Value from attrs
  255capture_attrs(Attr, element(_, Attrs, Tree), [Value|List]) :-
  256	memberchk(Attr = Value, Attrs),
  257	maplist(capture_attrs(Attr), Tree, SList),
  258	flatten(SList, List).
 assign_path(+NoPath, -WithPath)
assign to dirtree path attribute from root
  264assign_path(element(dir, A, NoPath), element(dir, A, WithPath)) :-
  265	memberchk(path=Path, A),
  266	maplist(assign_path(Path), NoPath, WithPath).
  267
  268assign_path(Path, element(T, A, S), element(T, Wp, Sp)) :-
  269	memberchk(name=N, A),
  270	append(A, [path=Path], Wp),
  271	atomic_list_concat([Path, N], /, Next),
  272	(   T == dir
  273	->  maplist(assign_path(Next), S, Sp)
  274	;   Sp = S
  275	)