29
30:- module(conf_d,
31 [ load_conf_d/2, 32 conf_d_enabled/1, 33 conf_d_reload/0,
34 conf_d_members/3, 35 conf_d_member_data/3, 36 conf_d_configuration/3 37 ]).
38:- use_module(library(option)).
39:- use_module(library(ordsets)).
40:- use_module(library(lists)).
41:- use_module(library(apply)).
42:- use_module(library(version)).
43:- use_module(library(prolog_xref)).
44:- if(exists_source(library(pldoc/doc_process))).
45:- use_module(library(pldoc)).
46:- use_module(library(pldoc/doc_process)).
47:- endif.
48
64
85
86load_conf_d(Spec, Options) :-
87 set_top_dir,
88 select_option(solutions(Sols), Options, LoadOptions0, all),
89 merge_options(LoadOptions0,
90 [ if(changed),
91 extension(pl)
92 ], LoadOptions),
93 phrase(collect_dirs(Spec, Sols), Dirs),
94 list_to_set(Dirs, Set),
95 maplist(load_conf_dir(LoadOptions), Set),
96 git_update_versions(_). 97
98collect_dirs([], _) --> !.
99collect_dirs([H|T], Sols) --> !,
100 collect_dirs(H, Sols),
101 collect_dirs(T, Sols).
102collect_dirs(Spec, Sols) -->
103 findall(Dir, absolute_file_name(Spec, Dir,
104 [ file_type(directory),
105 file_errors(fail),
106 access(read),
107 solutions(Sols)
108 ])).
109
110
111:- dynamic
112 conf_d/3. 113
114load_conf_dir(Options, Dir) :-
115 conf_d_files(Dir, Files, Options),
116 delete(Options, extension(_), LoadOptions),
117 update_conf_d(Dir, Files, Options),
118 load_files(user:Files, LoadOptions).
119
120conf_d_files(Dir, Files, Options) :-
121 option(extension(Ext), Options, pl),
122 atomic_list_concat([Dir, '/*.', Ext], Pattern),
123 expand_file_name(Pattern, Matches),
124 include(accessible, Matches, AccessibleFiles),
125 maplist(absolute_file_name, AccessibleFiles, CanonicalFiles),
126 sort(CanonicalFiles, Files).
127
128accessible(File) :-
129 access_file(File, read).
130
131update_conf_d(Dir, Files, Options) :-
132 \+ conf_d(Dir, _, _), !,
133 assert(conf_d(Dir, Options, Files)).
134update_conf_d(Dir, Files, Options) :-
135 retract(conf_d(Dir, _, OldFiles)), !,
136 ord_subtract(OldFiles, Files, Removed),
137 ( Removed \== []
138 -> print_message(informational, conf_d(unload(Removed))),
139 catch(maplist(unload_file, Removed), E,
140 print_message(error, E))
141 ; true
142 ),
143 ord_subtract(Files, OldFiles, New),
144 ( New \== []
145 -> print_message(informational, conf_d(new(New)))
146 ; true
147 ),
148 assert(conf_d(Dir, Options, Files)).
149
153
154conf_d_enabled(Dir) :-
155 conf_d(Dir, _, _).
156
165
166conf_d_reload :-
167 findall(Dir-Options, conf_d(Dir, Options, _Files), Pairs),
168 forall(member(Dir-Options, Pairs),
169 load_conf_dir(Options, Dir)).
170
178
179conf_d_members(DirSpec, InfoRecords, Options) :-
180 findall(Files,
181 ( absolute_file_name(DirSpec, Dir,
182 [ file_type(directory),
183 solutions(all)
184 ]),
185 conf_d_files(Dir, Files, Options)
186 ), FileLists),
187 append(FileLists, Files0),
188 sort(Files0, Files), 189 maplist(conf_file, Files, InfoRecords).
190
191conf_file(File, config_file(Path, Module, Title)) :-
192 xref_public_list(File, Path, Module, _Public, _Meta, []), !,
193 ( current_predicate(doc_comment/4),
194 doc_comment(_:module(Title), Path:_, _Summary, _Comment)
195 -> true
196 ; true
197 ).
198conf_file(File, config_file(File, _Module, _Title)).
199
214
215conf_d_member_data(file, config_file(F, _, _), F).
216conf_d_member_data(module, config_file(_, M, _), M) :- nonvar(M).
217conf_d_member_data(title, config_file(_, _, T), T) :- nonvar(T).
218conf_d_member_data(loaded, config_file(F, _, _), B) :-
219 ( source_file(F)
220 -> B = true
221 ; B = false
222 ).
223
224
230
231set_top_dir :-
232 ( source_file(add_relative_search_path(_,_), File)
233 -> file_directory_name(File, Dir)
234 ; prolog_load_context(directory, Dir)
235 -> true
236 ; working_directory(Dir,Dir)
237 ),
238 ( user:file_search_path(cp_application, Dir)
239 -> true
240 ; assert(user:file_search_path(cp_application, Dir))
241 ).
242
252
253conf_d_configuration(Available, Enabled, Configs) :-
254 keyed_config(Available, Templ),
255 keyed_config(Enabled, Installed),
256 merge_pairlists([Templ, Installed], Configs).
257
258
259keyed_config(Dir, List) :-
260 conf_d_members(Dir, TemplMembers, []),
261 map_list_to_pairs(key_by_file, TemplMembers, List0),
262 keysort(List0, List).
263
264key_by_file(Data, Key) :-
265 conf_d_member_data(file, Data, Path),
266 file_name_extension(Plain, _, Path),
267 file_base_name(Plain, Key).
268
269
270 273
289
290merge_pairlists(Lists, Merged) :-
291 heads(Lists, Heads),
292 sort(Heads, Sorted),
293 merge_pairlists(Sorted, Lists, Merged).
294
295heads([], []).
296heads([[K-_|_]|T0], [K|T]) :- !,
297 heads(T0, T).
298heads([[]|T0], T) :-
299 heads(T0, T).
300
301merge_pairlists([], _, []).
302merge_pairlists([K|T0], Lists, [K-Vs|T]) :-
303 take_key(Lists, K, NewLists, NewKsUnsorted, Vs),
304 sort(NewKsUnsorted, NewKs),
305 ord_union(T0, NewKs, Ks),
306 merge_pairlists(Ks, NewLists, T).
307
308take_key([], _, [], [], []).
309take_key([List|T0], K, NewLists, NewKs, Vs) :-
310 ( List = [KH-V|ListT],
311 KH == K
312 -> NewLists = [ListT|T],
313 Vs = [V|Vs1],
314 ( ListT = [NewK-_|_]
315 -> NewKs = [NewK|NewKs1]
316 ; NewKs1 = NewKs
317 ),
318 take_key(T0, K, T, NewKs1, Vs1)
319 ; NewLists = [List|T],
320 Vs = [(-)|Vs1],
321 take_key(T0, K, T, NewKs, Vs1)
322 ).
323
324
325 328
329:- multifile
330 prolog:message//1.
331
332prolog:message(conf_d(unload(Files))) -->
333 [ 'Unloaded the following config files:'-[] ],
334 files(Files).
335prolog:message(conf_d(new(Files))) -->
336 [ 'Added the following config files:'-[] ],
337 files(Files).
338
339files([]) --> [].
340files([H|T]) --> [ nl, ' ~w'-[H] ], files(T).