36
   37:- module('$pack',
   38          [ attach_packs/0,
   39            attach_packs/1,                        40            attach_packs/2,                        41            pack_attach/2,                         42            '$pack_detach'/2                       43          ]).   44
   45:- multifile user:file_search_path/2.   46:- dynamic user:file_search_path/2.   47
   48:- dynamic
   49    pack_dir/3,                                50    pack/2.                                    51
   52user:file_search_path(pack, app_data(pack)).
   53
   54user:file_search_path(library, PackLib) :-
   55    pack_dir(_Name, prolog, PackLib).
   56user:file_search_path(foreign, PackLib) :-
   57    pack_dir(_Name, foreign, PackLib).
   58user:file_search_path(app, AppDir) :-
   59    pack_dir(_Name, app, AppDir).
   66'$pack_detach'(Name, Dir) :-
   67    (   atom(Name)
   68    ->  true
   69    ;   '$type_error'(atom, Name)
   70    ),
   71    (   retract(pack(Name, Dir))
   72    ->  retractall(pack_dir(Name, _, _)),
   73        reload_library_index
   74    ;   '$existence_error'(pack, Name)
   75    ).
   81pack_attach(Dir, Options) :-
   82    attach_package(Dir, Options),
   83    !.
   84pack_attach(Dir, _) :-
   85    (   exists_directory(Dir)
   86    ->  '$existence_error'(directory, Dir)
   87    ;   '$domain_error'(pack, Dir)
   88    ).
   95attach_packs :-
   96    set_prolog_flag(packs, true),
   97    set_pack_search_path,
   98    findall(PackDir, absolute_file_name(pack(.), PackDir,
   99                                        [ file_type(directory),
  100                                          access(read),
  101                                          solutions(all)
  102                                        ]),
  103            PackDirs),
  104    (   PackDirs \== []
  105    ->  remove_dups(PackDirs, UniquePackDirs, []),
  106        forall('$member'(PackDir, UniquePackDirs),
  107               attach_packs(PackDir, [duplicate(keep)]))
  108    ;   true
  109    ).
  110
  111set_pack_search_path :-
  112    getenv('SWIPL_PACK_PATH', Value),
  113    !,
  114    retractall(user:file_search_path(pack, _)),
  115    current_prolog_flag(path_sep, Sep),
  116    atomic_list_concat(Dirs, Sep, Value),
  117    register_pack_dirs(Dirs).
  118set_pack_search_path.
  119
  120register_pack_dirs([]).
  121register_pack_dirs([H|T]) :-
  122    prolog_to_os_filename(Dir, H),
  123    assertz(user:file_search_path(pack, Dir)),
  124    register_pack_dirs(T).
  131remove_dups([], [], _).
  132remove_dups([H|T0], T, Seen) :-
  133    memberchk(H, Seen),
  134    !,
  135    remove_dups(T0, T, Seen).
  136remove_dups([H|T0], [H|T], Seen) :-
  137    remove_dups(T0, T, [H|Seen]).
  161attach_packs(Dir) :-
  162    attach_packs(Dir, []).
  163
  164attach_packs(Dir, Options) :-
  165    (   '$option'(replace(true), Options)
  166    ->  forall(pack(Name, PackDir),
  167               '$pack_detach'(Name, PackDir)),
  168        retractall(user:file_search_path(pack, _))
  169    ;   true
  170    ),
  171    register_packs_from(Dir),
  172    absolute_file_name(Dir, Path,
  173                       [ file_type(directory),
  174                         file_errors(fail)
  175                       ]),
  176    catch(directory_files(Path, Entries), _, fail),
  177    !,
  178    ensure_slash(Path, SPath),
  179    attach_packages(Entries, SPath, Options),
  180    reload_library_index.
  181attach_packs(_, _).
  182
  183register_packs_from(Dir) :-
  184    (   user:file_search_path(pack, Dir)
  185    ->  true
  186    ;   asserta(user:file_search_path(pack, Dir))
  187    ).
  188
  189attach_packages([], _, _).
  190attach_packages([H|T], Dir, Options) :-
  191    attach_package(H, Dir, Options),
  192    attach_packages(T, Dir, Options).
  193
  194attach_package(Entry, Dir, Options) :-
  195    \+ special(Entry),
  196    atom_concat(Dir, Entry, PackDir),
  197    attach_package(PackDir, Options),
  198    !.
  199attach_package(_, _, _).
  200
  201special(.).
  202special(..).
  209attach_package(PackDir, Options) :-
  210    atomic_list_concat([PackDir, '/pack.pl'], InfoFile),
  211    access_file(InfoFile, read),
  212    file_base_name(PackDir, Pack),
  213    check_existing(Pack, PackDir, Options),
  214    prolog_dir(PackDir, PrologDir),
  215    !,
  216    assertz(pack(Pack, PackDir)),
  217    '$option'(search(Where), Options, last),
  218    (   Where == last
  219    ->  assertz(pack_dir(Pack, prolog, PrologDir))
  220    ;   Where == first
  221    ->  asserta(pack_dir(Pack, prolog, PrologDir))
  222    ;   '$domain_error'(option_search, Where)
  223    ),
  224    update_autoload(PrologDir),
  225    (   foreign_dir(Pack, PackDir, ForeignDir)
  226    ->  assertz(pack_dir(Pack, foreign, ForeignDir))
  227    ;   true
  228    ),
  229    (   app_dir(PackDir, AppDir)
  230    ->  assertz(pack_dir(Pack, app, AppDir))
  231    ;   true
  232    ),
  233    print_message(silent, pack(attached(Pack, PackDir))).
  240check_existing(Entry, Dir, _) :-
  241    retract(pack(Entry, Dir)),               242    !,
  243    retractall(pack_dir(Entry, _, _)).
  244check_existing(Entry, Dir, Options) :-
  245    pack(Entry, OldDir),
  246    !,
  247    '$option'(duplicate(Action), Options, warning),
  248    (   Action == warning
  249    ->  print_message(warning, pack(duplicate(Entry, OldDir, Dir))),
  250        fail
  251    ;   Action == keep
  252    ->  fail
  253    ;   Action == replace
  254    ->  print_message(silent, pack(replaced(Entry, OldDir, Dir))),
  255        '$pack_detach'(Entry, OldDir)
  256    ;   '$domain_error'(option_duplicate, Action)
  257    ).
  258check_existing(_, _, _).
  259
  260
  261prolog_dir(PackDir, PrologDir) :-
  262    atomic_list_concat([PackDir, '/prolog'], PrologDir),
  263    exists_directory(PrologDir).
  264
  265update_autoload(PrologDir) :-
  266    atom_concat(PrologDir, '/INDEX.pl', IndexFile),
  267    (   exists_file(IndexFile)
  268    ->  add_autoload_directory(PrologDir)
  269    ;   true
  270    ).
  271
  272add_autoload_directory(Dir) :-
  273    (   user:file_search_path(autoload, Dir)
  274    ->  true
  275    ;   assertz(user:file_search_path(autoload, Dir))
  276    ),
  277    reload_library_index.
  278
  279foreign_dir(Pack, PackDir, ForeignDir) :-
  280    atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
  281    exists_directory(ForeignBaseDir),
  282    !,
  283    (   arch(Arch),
  284	atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
  285        exists_directory(ForeignDir)
  286    ->  assertz(pack_dir(Pack, foreign, ForeignDir))
  287    ;   findall(Arch, arch(Arch), Archs),
  288	print_message(warning, pack(no_arch(Pack, Archs))),
  289        fail
  290    ).
  291
  292arch(Arch) :-
  293    current_prolog_flag(apple_universal_binary, true),
  294    Arch = 'fat-darwin'.
  295arch(Arch) :-
  296    current_prolog_flag(arch, Arch).
  297
  298ensure_slash(Dir, SDir) :-
  299    (   sub_atom(Dir, _, _, 0, /)
  300    ->  SDir = Dir
  301    ;   atom_concat(Dir, /, SDir)
  302    ).
  303
  304app_dir(PackDir, AppDir) :-
  305    atomic_list_concat([PackDir, '/app'], AppDir),
  306    exists_directory(AppDir)