36
37:- module(files_ex,
38 [ set_time_file/3, 39 link_file/3, 40 chmod/2, 41 relative_file_name/3, 42 directory_file_path/3, 43 directory_member/3, 44 copy_file/2, 45 make_directory_path/1, 46 copy_directory/2, 47 delete_directory_and_contents/1, 48 delete_directory_contents/1 49 ]). 50:- use_module(library(apply)). 51:- use_module(library(error)). 52
65
66:- predicate_options(directory_member/3, 3,
67 [ recursive(boolean),
68 follow_links(boolean),
69 file_type(atom),
70 extensions(list(atom)),
71 file_errors(oneof([fail,warning,error])),
72 access(oneof([read,write,execute])),
73 matches(text),
74 exclude(text),
75 exclude_directory(text),
76 hidden(boolean)
77 ]). 78
79
80:- use_foreign_library(foreign(files), install_files). 81
110
122
151
152relative_file_name(Path, RelTo, RelPath) :- 153 nonvar(Path),
154 !,
155 absolute_file_name(Path, AbsPath),
156 absolute_file_name(RelTo, AbsRelTo),
157 atomic_list_concat(PL, /, AbsPath),
158 atomic_list_concat(RL, /, AbsRelTo),
159 delete_common_prefix(PL, RL, PL1, PL2),
160 to_dot_dot(PL2, DotDot, PL1),
161 atomic_list_concat(DotDot, /, RelPath).
162relative_file_name(Path, RelTo, RelPath) :-
163 ( is_absolute_file_name(RelPath)
164 -> Path = RelPath
165 ; file_directory_name(RelTo, RelToDir),
166 directory_file_path(RelToDir, RelPath, Path0),
167 absolute_file_name(Path0, Path)
168 ).
169
170delete_common_prefix([H|T01], [H|T02], T1, T2) :-
171 !,
172 delete_common_prefix(T01, T02, T1, T2).
173delete_common_prefix(T1, T2, T1, T2).
174
175to_dot_dot([], Tail, Tail).
176to_dot_dot([_], Tail, Tail) :- !.
177to_dot_dot([_|T0], ['..'|T], Tail) :-
178 to_dot_dot(T0, T, Tail).
179
180
191
192directory_file_path(Dir, File, Path) :-
193 nonvar(Dir), nonvar(File),
194 !,
195 ( ( is_absolute_file_name(File)
196 ; Dir == '.'
197 )
198 -> Path = File
199 ; sub_atom(Dir, _, _, 0, /)
200 -> atom_concat(Dir, File, Path)
201 ; atomic_list_concat([Dir, /, File], Path)
202 ).
203directory_file_path(Dir, File, Path) :-
204 nonvar(Path),
205 !,
206 ( nonvar(Dir)
207 -> ( Dir == '.',
208 \+ is_absolute_file_name(Path)
209 -> File = Path
210 ; sub_atom(Dir, _, _, 0, /)
211 -> atom_concat(Dir, File, Path)
212 ; atom_concat(Dir, /, TheDir)
213 -> atom_concat(TheDir, File, Path)
214 )
215 ; nonvar(File)
216 -> atom_concat(Dir0, File, Path),
217 strip_trailing_slash(Dir0, Dir)
218 ; file_directory_name(Path, Dir),
219 file_base_name(Path, File)
220 ).
221directory_file_path(_, _, _) :-
222 throw(error(instantiation_error(_), _)).
223
224strip_trailing_slash(Dir0, Dir) :-
225 ( atom_concat(D, /, Dir0),
226 D \== ''
227 -> Dir = D
228 ; Dir = Dir0
229 ).
230
231
264
265directory_member(Directory, Member, Options) :-
266 dict_create(Dict, options, Options),
267 ( Dict.get(recursive) == true,
268 \+ Dict.get(follow_links) == false
269 -> empty_nb_set(Visited),
270 DictOptions = Dict.put(visited, Visited)
271 ; DictOptions = Dict
272 ),
273 directory_member_dict(Directory, Member, DictOptions).
274
275directory_member_dict(Directory, Member, Dict) :-
276 directory_files(Directory, Files, Dict),
277 member(Entry, Files),
278 \+ special(Entry),
279 directory_file_path(Directory, Entry, AbsEntry),
280 filter_link(AbsEntry, Dict),
281 ( exists_directory(AbsEntry)
282 -> ( filter_dir_member(AbsEntry, Entry, Dict),
283 Member = AbsEntry
284 ; filter_directory(Entry, Dict),
285 Dict.get(recursive) == true,
286 \+ hidden_file(Entry, Dict),
287 no_link_cycle(AbsEntry, Dict),
288 directory_member_dict(AbsEntry, Member, Dict)
289 )
290 ; filter_dir_member(AbsEntry, Entry, Dict),
291 Member = AbsEntry
292 ).
293
294directory_files(Directory, Files, Dict) :-
295 Errors = Dict.get(file_errors),
296 !,
297 errors_directory_files(Errors, Directory, Files).
298directory_files(Directory, Files, _Dict) :-
299 errors_directory_files(warning, Directory, Files).
300
301errors_directory_files(fail, Directory, Files) :-
302 catch(directory_files(Directory, Files), _, fail).
303errors_directory_files(warning, Directory, Files) :-
304 catch(directory_files(Directory, Files), E,
305 ( print_message(warning, E),
306 fail)).
307errors_directory_files(error, Directory, Files) :-
308 directory_files(Directory, Files).
309
310
311filter_link(File, Dict) :-
312 \+ ( Dict.get(follow_links) == false,
313 read_link(File, _, _)
314 ).
315
316no_link_cycle(Directory, Dict) :-
317 Visited = Dict.get(visited),
318 !,
319 absolute_file_name(Directory, Canonical,
320 [ file_type(directory)
321 ]),
322 add_nb_set(Canonical, Visited, true).
323no_link_cycle(_, _).
324
325hidden_file(Entry, Dict) :-
326 false == Dict.get(hidden),
327 sub_atom(Entry, 0, _, _, '.').
328
332
333filter_dir_member(_AbsEntry, Entry, Dict) :-
334 Exclude = Dict.get(exclude),
335 wildcard_match(Exclude, Entry),
336 !, fail.
337filter_dir_member(_AbsEntry, Entry, Dict) :-
338 Include = Dict.get(matches),
339 \+ wildcard_match(Include, Entry),
340 !, fail.
341filter_dir_member(AbsEntry, _Entry, Dict) :-
342 Type = Dict.get(file_type),
343 \+ matches_type(Type, AbsEntry),
344 !, fail.
345filter_dir_member(_AbsEntry, Entry, Dict) :-
346 ExtList = Dict.get(extensions),
347 file_name_extension(_, Ext, Entry),
348 \+ memberchk(Ext, ExtList),
349 !, fail.
350filter_dir_member(AbsEntry, _Entry, Dict) :-
351 Access = Dict.get(access),
352 \+ access_file(AbsEntry, Access),
353 !, fail.
354filter_dir_member(_AbsEntry, Entry, Dict) :-
355 hidden_file(Entry, Dict),
356 !, fail.
357filter_dir_member(_, _, _).
358
359matches_type(directory, Entry) :-
360 !,
361 exists_directory(Entry).
362matches_type(Type, Entry) :-
363 \+ exists_directory(Entry),
364 user:prolog_file_type(Ext, Type),
365 file_name_extension(_, Ext, Entry).
366
367
371
372filter_directory(Entry, Dict) :-
373 Exclude = Dict.get(exclude_directory),
374 wildcard_match(Exclude, Entry),
375 !, fail.
376filter_directory(_, _).
377
378
383
384copy_file(From, To) :-
385 destination_file(To, From, Dest),
386 setup_call_cleanup(
387 open(Dest, write, Out, [type(binary)]),
388 copy_from(From, Out),
389 close(Out)).
390
391copy_from(File, Stream) :-
392 setup_call_cleanup(
393 open(File, read, In, [type(binary)]),
394 copy_stream_data(In, Stream),
395 close(In)).
396
397destination_file(Dir, File, Dest) :-
398 exists_directory(Dir),
399 !,
400 file_base_name(File, Base),
401 directory_file_path(Dir, Base, Dest).
402destination_file(Dest, _, Dest).
403
404
409
410make_directory_path(Dir) :-
411 make_directory_path_2(Dir),
412 !.
413make_directory_path(Dir) :-
414 permission_error(create, directory, Dir).
415
416make_directory_path_2(Dir) :-
417 exists_directory(Dir),
418 !.
419make_directory_path_2(Dir) :-
420 atom_concat(RealDir, '/', Dir),
421 RealDir \== '',
422 !,
423 make_directory_path_2(RealDir).
424make_directory_path_2(Dir) :-
425 Dir \== (/),
426 !,
427 file_directory_name(Dir, Parent),
428 make_directory_path_2(Parent),
429 E = error(existence_error(directory, _), _),
430 catch(make_directory(Dir), E,
431 ( exists_directory(Dir)
432 -> true
433 ; throw(E)
434 )).
435
442
443copy_directory(From, To) :-
444 ( exists_directory(To)
445 -> true
446 ; make_directory(To)
447 ),
448 directory_files(From, Entries),
449 maplist(copy_directory_content(From, To), Entries).
450
451copy_directory_content(_From, _To, Special) :-
452 special(Special),
453 !.
454copy_directory_content(From, To, Entry) :-
455 directory_file_path(From, Entry, Source),
456 directory_file_path(To, Entry, Dest),
457 ( exists_directory(Source)
458 -> copy_directory(Source, Dest)
459 ; copy_file(Source, Dest)
460 ).
461
462special(.).
463special(..).
464
470
471delete_directory_and_contents(Dir) :-
472 read_link(Dir, _, _),
473 !,
474 delete_file(Dir).
475delete_directory_and_contents(Dir) :-
476 directory_files(Dir, Files),
477 maplist(delete_directory_contents(Dir), Files),
478 E = error(existence_error(directory, _), _),
479 catch(delete_directory(Dir), E,
480 ( \+ exists_directory(Dir)
481 -> true
482 ; throw(E)
483 )).
484
485delete_directory_contents(_, Entry) :-
486 special(Entry),
487 !.
488delete_directory_contents(Dir, Entry) :-
489 directory_file_path(Dir, Entry, Delete),
490 ( exists_directory(Delete)
491 -> delete_directory_and_contents(Delete)
492 ; E = error(existence_error(file, _), _),
493 catch(delete_file(Delete), E,
494 ( \+ exists_file(Delete)
495 -> true
496 ; throw(E)))
497 ).
498
505
506delete_directory_contents(Dir) :-
507 directory_files(Dir, Files),
508 maplist(delete_directory_contents(Dir), Files).
509
510
525
526chmod(File, +Spec) :-
527 must_be(ground, Spec),
528 !,
529 mode_bits(Spec, Bits),
530 file_mode_(File, Mode0),
531 Mode is Mode0 \/ Bits,
532 chmod_(File, Mode).
533chmod(File, -Spec) :-
534 must_be(ground, Spec),
535 !,
536 mode_bits(Spec, Bits),
537 file_mode_(File, Mode0),
538 Mode is Mode0 /\ \Bits,
539 chmod_(File, Mode).
540chmod(File, Spec) :-
541 must_be(ground, Spec),
542 !,
543 mode_bits(Spec, Bits),
544 chmod_(File, Bits).
545
546mode_bits(Spec, Spec) :-
547 integer(Spec),
548 !.
549mode_bits(Name, Bits) :-
550 atom(Name),
551 !,
552 ( file_mode(Name, Bits)
553 -> true
554 ; domain_error(posix_file_mode, Name)
555 ).
556mode_bits(Spec, Bits) :-
557 must_be(list(atom), Spec),
558 phrase(mode_bits(0, Bits), Spec).
559
560mode_bits(Bits0, Bits) -->
561 [Spec], !,
562 ( { file_mode(Spec, B), Bits1 is Bits0\/B }
563 -> mode_bits(Bits1, Bits)
564 ; { domain_error(posix_file_mode, Spec) }
565 ).
566mode_bits(Bits, Bits) -->
567 [].
568
569file_mode(suid, 0o4000).
570file_mode(sgid, 0o2000).
571file_mode(svtx, 0o1000).
572file_mode(Name, Bits) :-
573 atom_chars(Name, Chars),
574 phrase(who_mask(0, WMask0), Chars, Rest),
575 ( WMask0 =:= 0
576 -> WMask = 0o0777
577 ; WMask = WMask0
578 ),
579 maplist(mode_char, Rest, MBits),
580 foldl(or, MBits, 0, Mask),
581 Bits is Mask /\ WMask.
582
583who_mask(M0, M) -->
584 [C],
585 { who_mask(C,M1), !,
586 M2 is M0\/M1
587 },
588 who_mask(M2,M).
589who_mask(M, M) -->
590 [].
591
592who_mask(o, 0o0007).
593who_mask(g, 0o0070).
594who_mask(u, 0o0700).
595
596mode_char(r, 0o0444).
597mode_char(w, 0o0222).
598mode_char(x, 0o0111).
599
600or(B1, B2, B) :-
601 B is B1\/B2