:- module( spudlike, [spudlike/0,spudlike/1] ). :- use_module(library(lists)). % append/3. :- use_module(library(apply)). % exclude/3, include/3. :- use_module(library(debug)). % /1,3. :- use_module(library(process)). % process_create/3. :- use_module(library(readutil)). % read_line_to_codes/2. :- use_module(library(doc_http)). % doc_server/2. :- use_module(library(www_browser)). % www_open_url/1. :- debug(spudlike). /** spudlike. See doc for spudlike/0. */ /** spudlike. spudlike(Opts). (Re-)Start a server at Port serving all currently installed packs.
Only tested on linux. == % upsh spudlike == Opts * allow(Allow=[]) Ips allowed to connect to server * browser(Browser=true) whether to start a browser window authomatically. if not a boolean is taken to be the page to start * ignore(AbsIgnore=_) ignore file in this absolute location * kill(Kill=true) whether to kill existing server * port(Port=3003) port for server * scripts(Scripts=false) experimental: untested, whether to load to server, upsh locatable scripts * server(Server=localhost) domain name of the server to access You can add something like the following to a launcher in your linux windows manager: == /usr/local/users/nicos/local/git/bin/swipl -f none -g "doc_collect(true),[pack(spuds/scripts/spudlike)]" -t spudlike /usr/local/users/nicos/local/git/bin/swipl -f none -g "[pack(spuds/scripts/spudlike)]" -g spudlike /usr/local/users/nicos/local/git/bin/swipl -f none -g "doc_collect(true),[pack(spuds/scripts/spudlike)]" -g "spudlike(browser('search?for=stoics_lib&in=all&match=summary'))" == which will also give you doc for any start-up loading packs (and spudlike/0,1). The "-f" is critical in that normal user profile is not loaded before the server has been started (in which case it will not be in the doc server). @author nicos angelopoulos @version 0.2 2018/01/26 @version 0.3 2018/02/07, removed dependency to by_unix @version 0.4 2019/03/21, generalise for home network use. added options and CLI example @version 0.5 2020/03/19, steadfast kill code; doc enhancements; work in tmp dir courtesy tmp_file/2; browser to page @version 0.6 2020/08/20, first take on running on macs (no killing yet) */ spudlike :- spudlike( [] ). spudlike( ArgS ) :- ( is_list(ArgS) -> Args=ArgS; Args=[ArgS] ), tmp_file( spudlike, Tmp ), make_directory( Tmp ), working_directory( _, Tmp ), debug( spudlike, 'Working dir: ~w', [Tmp] ), ( getenv('HOST',Host) -> atomic_list_concat( ['spudlike_', Host, '.pl'], HostPrefsB ) ; % Host = localhost, HostPrefsB = 'spudlike.pl' ), getenv('HOME',Home), directory_file_path(Home,'.pl', HostPrefsD), directory_file_path(HostPrefsD,HostPrefsB,AbsHostPrefsF), ( exists_file(AbsHostPrefsF) -> debug( spudlike, 'Preferences from: ~w', [AbsHostPrefsF] ), spudlike_read_file( AbsHostPrefsF, UserOpts ) ; % atom_concat( '~/', HostPrefsB, RepPrefsF ), debug( spudlike, 'No preferences file found (~p).', [AbsHostPrefsF] ), % fixme, also for .pl/spudlike.pl here UserOpts = [] ), append( Args, UserOpts, Opts ), debug( spudlike, 'Opts: ~w', [Opts] ), options_val( port(Port), Opts, 3003 ), options_val( server(Server), Opts, localhost ), options_val( kill(Kill), Opts, true ), % ( memberchk(port(Port),Opts) -> true; Port = 3003 ), % ( memberchk(server(Server),Opts) -> true; Server = localhost ), % ( memberchk(kill(Kill),Opts) -> true; Kill = true ), findall( allow(Allow), member(allow(Allow),Opts), AllowsPrv ), ( AllowsPrv = [] -> ( Server == localhost -> Allows = [] % doc_server/2 defaults are fine ; % process_output( hostname, ['-I'], Atom ), process_output( hostname, [], Atom ), atom_concat( IP, ' \n', Atom ), Allows = [allow(IP)] ) ; AllowsPrv = Allows ), debug( spudslike, 'Server: ~w, Port: ~w', [Server,Port] ), options_val( scripts(Scripts), Opts, true ), findall( Ign, member(ignore(Ign),Opts), Ignore ), spudlike( Server, Port, Allows, Kill, Scripts, Ignore ), ( memberchk(browser(Browser),Opts) -> true; Browser = true ), debug( spudslike, 'Browser: ~w', [Browser] ), ( Browser == false -> true ; ( Browser == true -> atomic_list_concat( ['http://',Server,':',Port,'/pldoc'], '', Url ) ; atomic_list_concat( ['http://',Server,':',Port,'/pldoc/',Browser], '', Url ) ), debug( spudslike, 'URL: ~w', [Url] ), www_open_url(Url) ), spudlike_busy. spudlike_busy :- sleep( 10000 ), !, spudlike_busy. spudlike( localhost, Port, Allows, Kill, Scripts, Ignore ) :- spudlike_kill( Kill, localhost ), doc_server( Port, Allows ), debug( spudlike, 'doc_server(~w,~w)', [Port,Allows] ), use_module( library(lib) ), user:file_search_path( pack, Pack ), AbsOpts = [file_type(directory),file_errors(fail)], absolute_file_name( Pack, Packed, AbsOpts ), !, debug( spudlike, 'Packs directory: ~p', Packed ), directory_files( Packed, WithDotsSubs ), once( select('.',WithDotsSubs,NodSubs) ), once( select('..',NodSubs,Subs) ), % os_dir_dirs( Packed, Packs ), maplist( spudlike_load(Packed), Subs ), spudlike_scripts( Scripts, Packed, Subs, Ignore ), atomic_list_concat( ['http://localhost:',Port,'/pldoc'], '', Url ), debug( spudlike, '~w', [Url] ). spudlike( Remote, _Port, _Allows, _Kill, _Scripts, _Ignore ) :- % fixme: pass Opts ? getenv( 'USER', User ), atomic_list_concat( [User,Remote], '@', From ), process_create( path(ssh), ['-Y',From,pupsh,spudlike], [] ). spudlike_scripts( true, _Packed, _PackSubs, Ignore ) :- !, % fixme(here). % do the bin first % use tmp module ? % user:file_search_path( home, Home ), % ( expand_file_name( '$HOME', [Home|_] ) -> true; Home = '/home/nicos' ), expand_file_name( '$HOME', [Home|_] ), directory_file_path( Home, bin, Bin ), directory_file_path( Bin, cline_upsh, Cline ), ( exists_directory(Cline) -> directory_files( Cline, ClineSubs ), /* ignores([/home/nicos/bin/cline_upsh/publish_pack.pl]) plp(/home/nicos/bin/cline_upsh/publish_pack.pl) */ findall( _, ( member(PlF,ClineSubs), atom_concat(_,pl,PlF), write(doing_bin_script(PlF)),nl, directory_file_path(Cline, PlF, PlP), \+ memberchk(PlP, Ignore), consult(tmp:PlP) % consider abolishing all tmp:_ ? ), _ ) % then do the Subs (again tmp:module) % fixme(Subs). ; true ), absolute_file_name( user_app_data(pack), PackD ), ( exists_directory(PackD) -> directory_files( PackD, Packs ), findall( _, ( member(Pack,Packs), directory_file_path(Pack,scripts,ScriptsD), write(scripts_dir(ScriptsD)), nl, absolute_file_name(pack(ScriptsD), AbsScriptsD), exists_directory(AbsScriptsD), directory_files(AbsScriptsD,Scripts), member(Script,Scripts), file_name_extension(_Base,pl,Script), write(doing_pack_script(Script)), nl, directory_file_path(AbsScriptsD,Script,AbsScript), consult(tmp:AbsScript) ), _ ) ; true ), true. spudlike_scripts(_,_,_,_). spudlike_load( _, 'Downloads' ) :- !. spudlike_load( Root, Pack ) :- directory_file_path( Root, Pack, Path ), exists_directory( Path ), !, debug( spudlike, '...loading: ~w', Pack ), spudlike_loads( Pack, LoadThis ), ( catch(lib(LoadThis),_,fail) -> true ; debug( spudlike, '...FAILED to load it', [] ) ). spudlike_load( _Root, _Pack ). % skipping litter files spudlike_loads( Pack, LoadThis ) :- spudlike_loads_specific( Pack, LoadThis ), !. spudlike_loads( Pack, Pack ). spudlike_kill( true, localhost ) :- % only attempt when running locally current_prolog_flag( unix, true ), current_prolog_flag( pid, ThisPid ), debug( spudlike, 'This process id: ~d', ThisPid ), nl, psa_lines( spudlike, LnsPrv ), exclude( atom_sub('grep cline'), LnsPrv, LnsCline ), include( atom_sub('-g spudlike'), LnsCline, LnsG ), ( LnsG = [] -> include( atom_sub('-t spudlike'), LnsCline, Lns ) ; Lns = LnsG ), % ( (include( atom_sub('swipl'),LnsCline,LnsSwi),write(here(LnsSwi)), nl,LnsSwi\==[]) -> true; LnsSwi = LnsCline ), % debug( spudlike, 'Swi lines: ~w', [LnsSwi] ), % upsh: (but currently SWI throws error anyway % ( (include( atom_sub('swipl -f'),LnsCline,LnsSwi),LnsSwi\==[]) -> true; LnsSwi = LnsCline ), % ( (include(atom_sub('upsh'),LnsSwi,LnsUpsh),LnsUpsh \==[]) -> true; LnsUpsh = LnsSwi ), % include( atom_sub('swipl'), LnsUpsh, Lns ), % % findall( Aln, (member(Aln,LnsSwi),atom_concat(_,spudlike,Aln)), Lns ), % include( atom_sub('spudlike'),LnsCline,LnsSwi),write(here(LnsSwi)), nl,LnsSwi\==[]) -> true; LnsSwi = LnsCline ), debug( spudlike, 'Identified lines: ~w', [Lns] ), member( Ln, Lns ), once( (atomic_list_concat([_|T],' ',Ln),member(Pid,T),Pid \== '',atom_number(Pid,PidNum) ) ), PidNum =\= ThisPid, debug( spudlike, 'Killing process id: ~d', PidNum ), nl, !, debug( spudlike, 'Killing old spudlike process: ~w', Pid ), nl, catch( process_create( path(kill), ['-9',Pid], [] ), _, true ), sleep(3). spudlike_kill( true, _Server ) :- current_prolog_flag( unix, true ), !, debug( spudlike, 'No running spudlike found.', [] ). spudlike_kill( _, _Server ). % SWI will throw an error if an old instance is running... so let it succeed here psa_lines( spudlike, Lines) :- current_prolog_flag( apple, true ), !, setup_call_cleanup( process_create(path(ps), ['-Af' ], [ stdout(pipe(Out)) ]), read_lines(Out, Lines), close(Out)). psa_lines( spudlike, Lines) :- setup_call_cleanup( process_create(path(ps), [ '--columns',300, '-Af' ], [ stdout(pipe(Out)) ]), read_lines(Out, Lines), close(Out)). read_lines(Out, Lines) :- read_line_to_codes(Out, Line1), read_lines(Line1, Out, Lines). read_lines(end_of_file, _, []) :- !. read_lines(Codes, Out, [Line|Lines]) :- atom_codes(Line, Codes), read_line_to_codes(Out, Line2), read_lines(Line2, Out, Lines). atom_sub( Sub, Atom ) :- sub_atom( Atom, _, _, _, Sub ), !. spudlike_read_file( File, Terms ) :- open( File, read, In ), read( In, Term ), spudlike_read_stream( Term, In, Terms ), close( In ). spudlike_read_stream( end_of_file, _In, Terms ) :- !, Terms = []. spudlike_read_stream( InTerm, In, Terms ) :- Terms = [InTerm|Tail], read( In, Next ), spudlike_read_stream( Next, In, Tail ). process_output( Exe, Args, Atom ) :- Opts = [stdout(pipe(Out))], Create = process_create(path(Exe),Args,Opts), setup_call_cleanup( Create, read_string(Out,_,Output), close(Out) ), atom_string( Atom, Output ). options_val( Compound, Opts, Def ) :- ( memberchk(Compound,Opts) -> true; arg(1,Compound,Def) ), !. spudlike_loads_specific( terminus_store_prolog, terminus_store ).