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