%%% BEGIN openflax/handler/autocomplete.erl %%%
%%%
%%% openflax - Open Source web server for Erlang/OTP
%%% Copyright (c)2003 Cat's Eye Technologies. All rights reserved.
%%%
%%% Redistribution and use in source and binary forms, with or without
%%% modification, are permitted provided that the following conditions
%%% are met:
%%%
%%% Redistributions of source code must retain the above copyright
%%% notice, this list of conditions and the following disclaimer.
%%%
%%% Redistributions in binary form must reproduce the above copyright
%%% notice, this list of conditions and the following disclaimer in
%%% the documentation and/or other materials provided with the
%%% distribution.
%%%
%%% Neither the name of Cat's Eye Technologies nor the names of its
%%% contributors may be used to endorse or promote products derived
%%% from this software without specific prior written permission.
%%%
%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
%%% CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
%%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
%%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
%%% DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
%%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
%%% OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
%%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
%%% OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
%%% ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
%%% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
%%% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%% @doc Filename auto-completion handler for OpenFlax.
%%
%%
This handler should be invoked from cfg_openflax_dispatch
%% when the URI matches a pattern of "* " or similar.
%% The trailing space (shown as a plus sign in the URL) indicates the
%% filename is not complete. This handler will complete that filename,
%% if possible, and redirect the user agent to that resource.
%%
%% This handler requires openflax.handler.file.
%%
%% @end
-module(openflax.handler.autocomplete).
-vsn('$Id: autocomplete.erl 31 2004-04-23 07:00:11Z catseye $').
-author('cpressey@catseye.mine.nu').
-copyright('Copyright (c)2003 Cat`s Eye Technologies. All rights reserved.').
-behaviour(openflax.handler).
-export([start/1, stop/1, serve/1]). % behaviour
-import(lists).
-import(string).
-import(filename).
-import(file).
%% @spec start(conf()) -> conf()
%% @doc Initializes the filename auto-completion handler.
start(Conf) ->
openflax.conf:new().
%% @spec stop(conf()) -> conf()
%% @doc Shuts down the auto-completion handler.
stop(Conf) ->
Conf.
%% @spec serve(conf()) -> ok | {error, Reason}
%% @doc Redirects the user agent from a partial filename
%% to a full filename matching the partial filename, if any.
serve(Conf) ->
FileName = openflax.handler.file:document(Conf),
% Perform filename completion
case complete(openflax.string:truncate(FileName), Conf) of
[NewFileName | _] ->
% Found a prefix, so redirect them to it
redirect_completed(NewFileName, Conf);
[] ->
% No match at all - still not found
{not_found, Conf}
end.
%% @spec redirect_completed(filename(), conf()) -> ok
%% @doc Redirects the user agent to the completed filename.
%% Called only when filename completion is successful.
redirect_completed(NewFileName, Conf) ->
StartChar = length(openflax.conf:get_string(cfg_document_root, Conf)) + 1,
NewFileName0 = "/" ++ string:substr(NewFileName, StartChar),
{{moved_permanently, NewFileName0}, Conf}.
%% @spec complete(string(), conf()) -> [filename()]
%% @doc Given a partial name of a file with a full path, returns a list of
%% files in that directory that the prefix matches. The list may be empty
%% (if no files match,) it may have one entry (if there was a unique match,)
%% or it may have many entries (if the prefix was ambiguous.)
complete(PartialFileName, Conf) ->
DirName = filename:dirname(PartialFileName),
BaseName = filename:basename(PartialFileName),
{ok, Dir} = file:list_dir(DirName),
Dir0 = lists:filter(fun(X) ->
openflax.handler.file:is_accessible(filename:join([DirName, X]), Conf)
end, Dir),
Dir1 = lists:reverse(lists:sort(Dir0)),
complete(DirName, Dir1, BaseName, length(BaseName), []).
complete(DirName, [], PartialFileName, Length, Acc) -> Acc;
complete(DirName, [FileName | Tail], PartialFileName, Length, Acc) ->
Test = string:left(FileName, Length),
case string:left(FileName, Length) of
PartialFileName ->
FullFileName = filename:join([DirName, FileName]),
complete(DirName, Tail, PartialFileName, Length, [FullFileName | Acc]);
_ ->
complete(DirName, Tail, PartialFileName, Length, Acc)
end.
%%% END of openflax/handler/autocomplete.erl %%%