diff options
author | Alexandru Scvortov <alexandru@rabbitmq.com> | 2011-09-14 15:45:32 +0100 |
---|---|---|
committer | Alexandru Scvortov <alexandru@rabbitmq.com> | 2011-09-14 15:45:32 +0100 |
commit | 658a26b16f40e191eb1f7a4a32c2257949fbe028 (patch) | |
tree | 4d6de587c5d5bb64e1d9583064b3c81b6352912a | |
parent | 06604ac076bf9e2c8f1d01b65a7dd9bec0aafcc2 (diff) | |
download | rabbitmq-server-658a26b16f40e191eb1f7a4a32c2257949fbe028.tar.gz |
add file and filelib form R13B03
-rw-r--r-- | src/file2.erl | 1077 | ||||
-rw-r--r-- | src/filelib2.erl | 443 |
2 files changed, 1520 insertions, 0 deletions
diff --git a/src/file2.erl b/src/file2.erl new file mode 100644 index 00000000..49672b30 --- /dev/null +++ b/src/file2.erl @@ -0,0 +1,1077 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(file2). + +%% Interface module for the file server and the file io servers. + + + +%%% External exports + +-export([format_error/1]). +%% File system and metadata. +-export([get_cwd/0, get_cwd/1, set_cwd/1, delete/1, rename/2, + make_dir/1, del_dir/1, list_dir/1, + read_file_info/1, write_file_info/2, + altname/1, + read_link_info/1, read_link/1, + make_link/2, make_symlink/2, + read_file/1, write_file/2, write_file/3]). +%% Specialized +-export([ipread_s32bu_p32bu/3]). +%% Generic file contents. +-export([open/2, close/1, + read/2, write/2, + pread/2, pread/3, pwrite/2, pwrite/3, + read_line/1, + position/2, truncate/1, sync/1, + copy/2, copy/3]). +%% High level operations +-export([consult/1, path_consult/2]). +-export([eval/1, eval/2, path_eval/2, path_eval/3, path_open/3]). +-export([script/1, script/2, path_script/2, path_script/3]). +-export([change_owner/2, change_owner/3, change_group/2, + change_mode/2, change_time/2, change_time/3]). + +-export([pid2name/1]). + +%%% Obsolete exported functions + +-export([raw_read_file_info/1, raw_write_file_info/2]). + +%% Internal export to prim_file and ram_file until they implement +%% an efficient copy themselves. +-export([copy_opened/3]). + +-export([ipread_s32bu_p32bu_int/3]). + + +%%% Includes and defines +-include("file.hrl"). + +-define(FILE_IO_SERVER_TABLE, file_io_servers). + +-define(FILE_SERVER, file_server_2). % Registered name +-define(PRIM_FILE, prim_file). % Module +-define(RAM_FILE, ram_file). % Module + +%% data types +-type filename() :: string(). +-type io_device() :: pid() | #file_descriptor{}. +-type location() :: integer() | {'bof', integer()} | {'cur', integer()} + | {'eof', integer()} | 'bof' | 'cur' | 'eof'. +-type mode() :: 'read' | 'write' | 'append' | 'raw' | 'binary' | + {'delayed_write', non_neg_integer(), non_neg_integer()} | + 'delayed_write' | {'read_ahead', pos_integer()} | + 'read_ahead' | 'compressed'. +-type bindings() :: any(). + +%%%----------------------------------------------------------------- +%%% General functions + +-spec format_error(Reason :: posix() | {integer(), atom(), any()}) -> + string(). + +format_error({_Line, ?MODULE, undefined_script}) -> + "no value returned from script"; +format_error({Line, ?MODULE, {Class, Reason, Stacktrace}}) -> + io_lib:format("~w: evaluation failed with reason ~w:~w and stacktrace ~w", + [Line, Class, Reason, Stacktrace]); +format_error({Line, ?MODULE, {Reason, Stacktrace}}) -> + io_lib:format("~w: evaluation failed with reason ~w and stacktrace ~w", + [Line, Reason, Stacktrace]); +format_error({Line, Mod, Reason}) -> + io_lib:format("~w: ~s", [Line, Mod:format_error(Reason)]); +format_error(badarg) -> + "bad argument"; +format_error(system_limit) -> + "a system limit was hit, probably not enough ports"; +format_error(terminated) -> + "the file server process is terminated"; +format_error(ErrorId) -> + erl_posix_msg:message(ErrorId). + +-spec pid2name(Pid :: pid()) -> {'ok', filename()} | 'undefined'. + +pid2name(Pid) when is_pid(Pid) -> + case whereis(?FILE_SERVER) of + undefined -> + undefined; + _ -> + case ets:lookup(?FILE_IO_SERVER_TABLE, Pid) of + [{_, Name} | _] -> + {ok, Name}; + _ -> + undefined + end + end. + +%%%----------------------------------------------------------------- +%%% File server functions. +%%% Functions that do not operate on a single open file. +%%% Stateless. +-spec get_cwd() -> {'ok', filename()} | {'error', posix()}. + +get_cwd() -> + call(get_cwd, []). + +-spec get_cwd(Drive :: string()) -> {'ok', filename()} | {'error', posix()}. + +get_cwd(Drive) -> + check_and_call(get_cwd, [file_name(Drive)]). + +-spec set_cwd(Dirname :: name()) -> 'ok' | {'error', posix()}. + +set_cwd(Dirname) -> + check_and_call(set_cwd, [file_name(Dirname)]). + +-spec delete(Name :: name()) -> 'ok' | {'error', posix()}. + +delete(Name) -> + check_and_call(delete, [file_name(Name)]). + +-spec rename(From :: name(), To :: name()) -> 'ok' | {'error', posix()}. + +rename(From, To) -> + check_and_call(rename, [file_name(From), file_name(To)]). + +-spec make_dir(Name :: name()) -> 'ok' | {'error', posix()}. + +make_dir(Name) -> + check_and_call(make_dir, [file_name(Name)]). + +-spec del_dir(Name :: name()) -> 'ok' | {'error', posix()}. + +del_dir(Name) -> + check_and_call(del_dir, [file_name(Name)]). + +-spec read_file_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}. + +read_file_info(Name) -> + check_and_call(read_file_info, [file_name(Name)]). + +-spec altname(Name :: name()) -> any(). + +altname(Name) -> + check_and_call(altname, [file_name(Name)]). + +-spec read_link_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}. + +read_link_info(Name) -> + check_and_call(read_link_info, [file_name(Name)]). + +-spec read_link(Name :: name()) -> {'ok', filename()} | {'error', posix()}. + +read_link(Name) -> + check_and_call(read_link, [file_name(Name)]). + +-spec write_file_info(Name :: name(), Info :: #file_info{}) -> + 'ok' | {'error', posix()}. + +write_file_info(Name, Info = #file_info{}) -> + check_and_call(write_file_info, [file_name(Name), Info]). + +-spec list_dir(Name :: name()) -> {'ok', [filename()]} | {'error', posix()}. + +list_dir(Name) -> + check_and_call(list_dir, [file_name(Name)]). + +-spec read_file(Name :: name()) -> {'ok', binary()} | {'error', posix()}. + +read_file(Name) -> + check_and_call(read_file, [file_name(Name)]). + +-spec make_link(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. + +make_link(Old, New) -> + check_and_call(make_link, [file_name(Old), file_name(New)]). + +-spec make_symlink(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. + +make_symlink(Old, New) -> + check_and_call(make_symlink, [file_name(Old), file_name(New)]). + +-spec write_file(Name :: name(), Bin :: binary()) -> 'ok' | {'error', posix()}. + +write_file(Name, Bin) -> + check_and_call(write_file, [file_name(Name), make_binary(Bin)]). + +%% This whole operation should be moved to the file_server and prim_file +%% when it is time to change file server protocol again. +%% Meanwhile, it is implemented here, slihtly less efficient. +%% + +-spec write_file(Name :: name(), Bin :: binary(), Modes :: [mode()]) -> + 'ok' | {'error', posix()}. + +write_file(Name, Bin, ModeList) when is_list(ModeList) -> + case make_binary(Bin) of + B when is_binary(B) -> + case open(Name, [binary, write | + lists:delete(binary, + lists:delete(write, ModeList))]) of + {ok, Handle} -> + case write(Handle, B) of + ok -> + close(Handle); + E1 -> + close(Handle), + E1 + end; + E2 -> + E2 + end; + E3 -> + E3 + end. + +%% Obsolete, undocumented, local node only, don't use!. +%% XXX to be removed. +raw_read_file_info(Name) -> + Args = [file_name(Name)], + case check_args(Args) of + ok -> + [FileName] = Args, + ?PRIM_FILE:read_file_info(FileName); + Error -> + Error + end. + +%% Obsolete, undocumented, local node only, don't use!. +%% XXX to be removed. +raw_write_file_info(Name, #file_info{} = Info) -> + Args = [file_name(Name)], + case check_args(Args) of + ok -> + [FileName] = Args, + ?PRIM_FILE:write_file_info(FileName, Info); + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% File io server functions. +%%% They operate on a single open file. +%%% Stateful. + +%% Contemporary mode specification - list of options + +-spec open(Name :: name(), Modes :: [mode()]) -> + {'ok', io_device()} | {'error', posix()}. + +open(Item, ModeList) when is_list(ModeList) -> + case lists:member(raw, ModeList) of + %% Raw file, use ?PRIM_FILE to handle this file + true -> + %% check if raw file mode is disabled + case catch application:get_env(kernel, raw_files) of + {ok,false} -> + open(Item, lists:delete(raw, ModeList)); + _ -> % undefined | {ok,true} + Args = [file_name(Item) | ModeList], + case check_args(Args) of + ok -> + [FileName | _] = Args, + %% We rely on the returned Handle (in {ok, Handle}) + %% being a pid() or a #file_descriptor{} + ?PRIM_FILE:open(FileName, ModeList); + Error -> + Error + end + end; + false -> + case lists:member(ram, ModeList) of + %% RAM file, use ?RAM_FILE to handle this file + true -> + case check_args(ModeList) of + ok -> + ?RAM_FILE:open(Item, ModeList); + Error -> + Error + end; + %% File server file + false -> + Args = [file_name(Item) | ModeList], + case check_args(Args) of + ok -> + [FileName | _] = Args, + call(open, [FileName, ModeList]); + Error -> + Error + end + end + end; +%% Old obsolete mode specification in atom or 2-tuple format +open(Item, Mode) -> + open(Item, mode_list(Mode)). + +%%%----------------------------------------------------------------- +%%% The following interface functions operate on open files. +%%% The File argument must be either a Pid or a handle +%%% returned from ?PRIM_FILE:open. + +-spec close(File :: io_device()) -> 'ok' | {'error', posix()}. + +close(File) when is_pid(File) -> + R = file_request(File, close), + case wait_file_reply(File, R) of + {error, terminated} -> + ok; + Other -> + Other + end; +%% unlink(File), +%% exit(File, close), +%% ok; +close(#file_descriptor{module = Module} = Handle) -> + Module:close(Handle); +close(_) -> + {error, badarg}. + +-spec read(File :: io_device(), Size :: non_neg_integer()) -> + 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. + +read(File, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> + case io:request(File, {get_chars, '', Sz}) of + Data when is_list(Data); is_binary(Data) -> + {ok, Data}; + Other -> + Other + end; +read(#file_descriptor{module = Module} = Handle, Sz) + when is_integer(Sz), Sz >= 0 -> + Module:read(Handle, Sz); +read(_, _) -> + {error, badarg}. + +-spec read_line(File :: io_device()) -> + 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. + +read_line(File) when is_pid(File) -> + case io:request(File, {get_line, ''}) of + Data when is_list(Data); is_binary(Data) -> + {ok, Data}; + Other -> + Other + end; +read_line(#file_descriptor{module = Module} = Handle) -> + Module:read_line(Handle); +read_line(_) -> + {error, badarg}. + +-spec pread(File :: io_device(), + LocationNumbers :: [{location(), non_neg_integer()}]) -> + {'ok', [string() | binary() | 'eof']} | {'error', posix()}. + +pread(File, L) when is_pid(File), is_list(L) -> + pread_int(File, L, []); +pread(#file_descriptor{module = Module} = Handle, L) when is_list(L) -> + Module:pread(Handle, L); +pread(_, _) -> + {error, badarg}. + +pread_int(_File, [], R) -> + {ok, lists:reverse(R)}; +pread_int(File, [{At, Sz} | T], R) when is_integer(Sz), Sz >= 0 -> + case pread(File, At, Sz) of + {ok, Data} -> + pread_int(File, T, [Data | R]); + eof -> + pread_int(File, T, [eof | R]); + {error, _} = Error -> + Error + end; +pread_int(_, _, _) -> + {error, badarg}. + +-spec pread(File :: io_device(), + Location :: location(), + Size :: non_neg_integer()) -> + 'eof' | {'ok', string() | binary()} | {'error', posix()}. + +pread(File, At, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> + R = file_request(File, {pread, At, Sz}), + wait_file_reply(File, R); +pread(#file_descriptor{module = Module} = Handle, Offs, Sz) + when is_integer(Sz), Sz >= 0 -> + Module:pread(Handle, Offs, Sz); +pread(_, _, _) -> + {error, badarg}. + +-spec write(File :: io_device(), Byte :: iodata()) -> + 'ok' | {'error', posix()}. + +write(File, Bytes) when is_pid(File) -> + case make_binary(Bytes) of + Bin when is_binary(Bin) -> + io:request(File, {put_chars,Bin}); + Error -> + Error + end; +write(#file_descriptor{module = Module} = Handle, Bytes) -> + Module:write(Handle, Bytes); +write(_, _) -> + {error, badarg}. + +-spec pwrite(File :: io_device(), L :: [{location(), iodata()}]) -> + 'ok' | {'error', {non_neg_integer(), posix()}}. + +pwrite(File, L) when is_pid(File), is_list(L) -> + pwrite_int(File, L, 0); +pwrite(#file_descriptor{module = Module} = Handle, L) when is_list(L) -> + Module:pwrite(Handle, L); +pwrite(_, _) -> + {error, badarg}. + +pwrite_int(_File, [], _R) -> + ok; +pwrite_int(File, [{At, Bytes} | T], R) -> + case pwrite(File, At, Bytes) of + ok -> + pwrite_int(File, T, R+1); + {error, Reason} -> + {error, {R, Reason}} + end; +pwrite_int(_, _, _) -> + {error, badarg}. + +-spec pwrite(File :: io_device(), + Location :: location(), + Bytes :: iodata()) -> + 'ok' | {'error', posix()}. + +pwrite(File, At, Bytes) when is_pid(File) -> + R = file_request(File, {pwrite, At, Bytes}), + wait_file_reply(File, R); +pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) -> + Module:pwrite(Handle, Offs, Bytes); +pwrite(_, _, _) -> + {error, badarg}. + +-spec sync(File :: io_device()) -> 'ok' | {'error', posix()}. + +sync(File) when is_pid(File) -> + R = file_request(File, sync), + wait_file_reply(File, R); +sync(#file_descriptor{module = Module} = Handle) -> + Module:sync(Handle); +sync(_) -> + {error, badarg}. + +-spec position(File :: io_device(), Location :: location()) -> + {'ok',integer()} | {'error', posix()}. + +position(File, At) when is_pid(File) -> + R = file_request(File, {position,At}), + wait_file_reply(File, R); +position(#file_descriptor{module = Module} = Handle, At) -> + Module:position(Handle, At); +position(_, _) -> + {error, badarg}. + +-spec truncate(File :: io_device()) -> 'ok' | {'error', posix()}. + +truncate(File) when is_pid(File) -> + R = file_request(File, truncate), + wait_file_reply(File, R); +truncate(#file_descriptor{module = Module} = Handle) -> + Module:truncate(Handle); +truncate(_) -> + {error, badarg}. + +-spec copy(Source :: io_device() | name() | {name(), [mode()]}, + Destination :: io_device() | name() | {name(), [mode()]}) -> + {'ok', non_neg_integer()} | {'error', posix()}. + +copy(Source, Dest) -> + copy_int(Source, Dest, infinity). + +-spec copy(Source :: io_device() | name() | {name(), [mode()]}, + Destination :: io_device() | name() | {name(), [mode()]}, + Length :: non_neg_integer() | 'infinity') -> + {'ok', non_neg_integer()} | {'error', posix()}. + +copy(Source, Dest, Length) + when is_integer(Length), Length >= 0; + is_atom(Length) -> + copy_int(Source, Dest, Length); +copy(_, _, _) -> + {error, badarg}. + +%% Here we know that Length is either an atom or an integer >= 0 +%% (by the way, atoms > integers) +%% +%% Copy between open files. +copy_int(Source, Dest, Length) + when is_pid(Source), is_pid(Dest); + is_pid(Source), is_record(Dest, file_descriptor); + is_record(Source, file_descriptor), is_pid(Dest) -> + copy_opened_int(Source, Dest, Length, 0); +%% Copy between open raw files, both handled by the same module +copy_int(#file_descriptor{module = Module} = Source, + #file_descriptor{module = Module} = Dest, + Length) -> + Module:copy(Source, Dest, Length); +%% Copy between open raw files of different modules +copy_int(#file_descriptor{} = Source, + #file_descriptor{} = Dest, Length) -> + copy_opened_int(Source, Dest, Length, 0); +%% Copy between filenames, let the server do the copy +copy_int({SourceName, SourceOpts}, {DestName, DestOpts}, Length) + when is_list(SourceOpts), is_list(DestOpts) -> + check_and_call(copy, + [file_name(SourceName), SourceOpts, + file_name(DestName), DestOpts, + Length]); +%% Filename -> open file; must open Source and do client copy +copy_int({SourceName, SourceOpts}, Dest, Length) + when is_list(SourceOpts), is_pid(Dest); + is_list(SourceOpts), is_record(Dest, file_descriptor) -> + case file_name(SourceName) of + {error, _} = Error -> + Error; + Source -> + case open(Source, [read | SourceOpts]) of + {ok, Handle} -> + Result = copy_opened_int(Handle, Dest, Length, 0), + close(Handle), + Result; + {error, _} = Error -> + Error + end + end; +%% Open file -> filename; must open Dest and do client copy +copy_int(Source, {DestName, DestOpts}, Length) + when is_pid(Source), is_list(DestOpts); + is_record(Source, file_descriptor), is_list(DestOpts) -> + case file_name(DestName) of + {error, _} = Error -> + Error; + Dest -> + case open(Dest, [write | DestOpts]) of + {ok, Handle} -> + Result = copy_opened_int(Source, Handle, Length, 0), + close(Handle), + Result; + {error, _} = Error -> + Error + end + end; +%% +%% That was all combinations of {Name, Opts} tuples +%% and open files. At least one of Source and Dest has +%% to be a bare filename. +%% +%% If Source is not a bare filename; Dest must be +copy_int(Source, Dest, Length) + when is_pid(Source); + is_record(Source, file_descriptor) -> + copy_int(Source, {Dest, []}, Length); +copy_int({_SourceName, SourceOpts} = Source, Dest, Length) + when is_list(SourceOpts) -> + copy_int(Source, {Dest, []}, Length); +%% If Dest is not a bare filename; Source must be +copy_int(Source, Dest, Length) + when is_pid(Dest); + is_record(Dest, file_descriptor) -> + copy_int({Source, []}, Dest, Length); +copy_int(Source, {_DestName, DestOpts} = Dest, Length) + when is_list(DestOpts) -> + copy_int({Source, []}, Dest, Length); +%% Both must be bare filenames. If they are not, +%% the filename check in the copy operation will yell. +copy_int(Source, Dest, Length) -> + copy_int({Source, []}, {Dest, []}, Length). + + + +copy_opened(Source, Dest, Length) + when is_integer(Length), Length >= 0; + is_atom(Length) -> + copy_opened_int(Source, Dest, Length); +copy_opened(_, _, _) -> + {error, badarg}. + +%% Here we know that Length is either an atom or an integer >= 0 +%% (by the way, atoms > integers) + +copy_opened_int(Source, Dest, Length) + when is_pid(Source), is_pid(Dest) -> + copy_opened_int(Source, Dest, Length, 0); +copy_opened_int(Source, Dest, Length) + when is_pid(Source), is_record(Dest, file_descriptor) -> + copy_opened_int(Source, Dest, Length, 0); +copy_opened_int(Source, Dest, Length) + when is_record(Source, file_descriptor), is_pid(Dest) -> + copy_opened_int(Source, Dest, Length, 0); +copy_opened_int(Source, Dest, Length) + when is_record(Source, file_descriptor), is_record(Dest, file_descriptor) -> + copy_opened_int(Source, Dest, Length, 0); +copy_opened_int(_, _, _) -> + {error, badarg}. + +%% Here we know that Source and Dest are handles to open files, Length is +%% as above, and Copied is an integer >= 0 + +%% Copy loop in client process +copy_opened_int(_, _, Length, Copied) when Length =< 0 -> % atom() > integer() + {ok, Copied}; +copy_opened_int(Source, Dest, Length, Copied) -> + N = if Length > 65536 -> 65536; true -> Length end, % atom() > integer() ! + case read(Source, N) of + {ok, Data} -> + M = if is_binary(Data) -> byte_size(Data); + is_list(Data) -> length(Data) + end, + case write(Dest, Data) of + ok -> + if M < N -> + %% Got less than asked for - must be end of file + {ok, Copied+M}; + true -> + %% Decrement Length (might be an atom (infinity)) + NewLength = if is_atom(Length) -> Length; + true -> Length-M + end, + copy_opened_int(Source, Dest, NewLength, Copied+M) + end; + {error, _} = Error -> + Error + end; + eof -> + {ok, Copied}; + {error, _} = Error -> + Error + end. + + +%% Special indirect pread function. Introduced for Dets. +%% Reads a header from pos 'Pos', the header is first a size encoded as +%% 32 bit big endian unsigned and then a position also encoded as +%% 32 bit big endian. Finally it preads the data from that pos and size +%% in the file. + +ipread_s32bu_p32bu(File, Pos, MaxSize) when is_pid(File) -> + ipread_s32bu_p32bu_int(File, Pos, MaxSize); +ipread_s32bu_p32bu(#file_descriptor{module = Module} = Handle, Pos, MaxSize) -> + Module:ipread_s32bu_p32bu(Handle, Pos, MaxSize); +ipread_s32bu_p32bu(_, _, _) -> + {error, badarg}. + +ipread_s32bu_p32bu_int(File, Pos, Infinity) when is_atom(Infinity) -> + ipread_s32bu_p32bu_int(File, Pos, (1 bsl 31)-1); +ipread_s32bu_p32bu_int(File, Pos, MaxSize) + when is_integer(MaxSize), MaxSize >= 0 -> + if + MaxSize < (1 bsl 31) -> + case pread(File, Pos, 8) of + {ok, Header} -> + ipread_s32bu_p32bu_2(File, Header, MaxSize); + Error -> + Error + end; + true -> + {error, einval} + end; +ipread_s32bu_p32bu_int(_File, _Pos, _MaxSize) -> + {error, badarg}. + +ipread_s32bu_p32bu_2(_File, + <<0:32/big-unsigned, Pos:32/big-unsigned>>, + _MaxSize) -> + {ok, {0, Pos, eof}}; +ipread_s32bu_p32bu_2(File, + <<Size:32/big-unsigned, Pos:32/big-unsigned>>, + MaxSize) + when Size =< MaxSize -> + case pread(File, Pos, Size) of + {ok, Data} -> + {ok, {Size, Pos, Data}}; + eof -> + {ok, {Size, Pos, eof}}; + Error -> + Error + end; +ipread_s32bu_p32bu_2(_File, + <<_:8/binary>>, + _MaxSize) -> + eof; +ipread_s32bu_p32bu_2(_File, + <<_/binary>>, + _MaxSize) -> + eof; +ipread_s32bu_p32bu_2(File, + Header, + MaxSize) when is_list(Header) -> + ipread_s32bu_p32bu_2(File, list_to_binary(Header), MaxSize). + + + +%%%----------------------------------------------------------------- +%%% The following functions, built upon the other interface functions, +%%% provide a higher-lever interface to files. + +-spec consult(File :: name()) -> + {'ok', list()} | {'error', posix() | {integer(), atom(), any()}}. + +consult(File) -> + case open(File, [read]) of + {ok, Fd} -> + R = consult_stream(Fd), + close(Fd), + R; + Error -> + Error + end. + +-spec path_consult(Paths :: [name()], File :: name()) -> + {'ok', list(), filename()} | {'error', posix() | {integer(), atom(), any()}}. + +path_consult(Path, File) -> + case path_open(Path, File, [read]) of + {ok, Fd, Full} -> + case consult_stream(Fd) of + {ok, List} -> + close(Fd), + {ok, List, Full}; + E1 -> + close(Fd), + E1 + end; + E2 -> + E2 + end. + +-spec eval(File :: name()) -> 'ok' | {'error', posix()}. + +eval(File) -> + eval(File, erl_eval:new_bindings()). + +-spec eval(File :: name(), Bindings :: bindings()) -> + 'ok' | {'error', posix()}. + +eval(File, Bs) -> + case open(File, [read]) of + {ok, Fd} -> + R = eval_stream(Fd, ignore, Bs), + close(Fd), + R; + Error -> + Error + end. + +-spec path_eval(Paths :: [name()], File :: name()) -> + {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. + +path_eval(Path, File) -> + path_eval(Path, File, erl_eval:new_bindings()). + +-spec path_eval(Paths :: [name()], File :: name(), Bindings :: bindings()) -> + {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. + +path_eval(Path, File, Bs) -> + case path_open(Path, File, [read]) of + {ok, Fd, Full} -> + case eval_stream(Fd, ignore, Bs) of + ok -> + close(Fd), + {ok, Full}; + E1 -> + close(Fd), + E1 + end; + E2 -> + E2 + end. + +-spec script(File :: name()) -> + {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. + +script(File) -> + script(File, erl_eval:new_bindings()). + +-spec script(File :: name(), Bindings :: bindings()) -> + {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. + +script(File, Bs) -> + case open(File, [read]) of + {ok, Fd} -> + R = eval_stream(Fd, return, Bs), + close(Fd), + R; + Error -> + Error + end. + +-spec path_script/2 :: (Paths :: [name()], File :: name()) -> + {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. + +path_script(Path, File) -> + path_script(Path, File, erl_eval:new_bindings()). + +-spec path_script(Paths :: [name()], + File :: name(), + Bindings :: bindings()) -> + {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. + +path_script(Path, File, Bs) -> + case path_open(Path, File, [read]) of + {ok,Fd,Full} -> + case eval_stream(Fd, return, Bs) of + {ok,R} -> + close(Fd), + {ok, R, Full}; + E1 -> + close(Fd), + E1 + end; + E2 -> + E2 + end. + + +%% path_open(Paths, Filename, Mode) -> +%% {ok,FileDescriptor,FullName} +%% {error,Reason} +%% +%% Searches the Paths for file Filename which can be opened with Mode. +%% The path list is ignored if Filename contains an absolute path. + +-spec path_open(Paths :: [name()], Name :: name(), Modes :: [mode()]) -> + {'ok', io_device(), filename()} | {'error', posix()}. + +path_open(PathList, Name, Mode) -> + case file_name(Name) of + {error, _} = Error -> + Error; + FileName -> + case filename:pathtype(FileName) of + relative -> + path_open_first(PathList, FileName, Mode, enoent); + _ -> + case open(Name, Mode) of + {ok, Fd} -> + {ok, Fd, Name}; + Error -> + Error + end + end + end. + +-spec change_mode(Name :: name(), Mode :: integer()) -> + 'ok' | {'error', posix()}. + +change_mode(Name, Mode) + when is_integer(Mode) -> + write_file_info(Name, #file_info{mode=Mode}). + +-spec change_owner(Name :: name(), OwnerId :: integer()) -> + 'ok' | {'error', posix()}. + +change_owner(Name, OwnerId) + when is_integer(OwnerId) -> + write_file_info(Name, #file_info{uid=OwnerId}). + +-spec change_owner(Name :: name(), + OwnerId :: integer(), + GroupId :: integer()) -> + 'ok' | {'error', posix()}. + +change_owner(Name, OwnerId, GroupId) + when is_integer(OwnerId), is_integer(GroupId) -> + write_file_info(Name, #file_info{uid=OwnerId, gid=GroupId}). + +-spec change_group(Name :: name(), GroupId :: integer()) -> + 'ok' | {'error', posix()}. + +change_group(Name, GroupId) + when is_integer(GroupId) -> + write_file_info(Name, #file_info{gid=GroupId}). + +-spec change_time(Name :: name(), Time :: date_time()) -> + 'ok' | {'error', posix()}. + +change_time(Name, Time) + when is_tuple(Time) -> + write_file_info(Name, #file_info{mtime=Time}). + +-spec change_time(Name :: name(), + ATime :: date_time(), + MTime :: date_time()) -> + 'ok' | {'error', posix()}. + +change_time(Name, Atime, Mtime) + when is_tuple(Atime), is_tuple(Mtime) -> + write_file_info(Name, #file_info{atime=Atime, mtime=Mtime}). + +%%%----------------------------------------------------------------- +%%% Helpers + +consult_stream(Fd) -> + consult_stream(Fd, 1, []). + +consult_stream(Fd, Line, Acc) -> + case io:read(Fd, '', Line) of + {ok,Term,EndLine} -> + consult_stream(Fd, EndLine, [Term|Acc]); + {error,Error,_Line} -> + {error,Error}; + {eof,_Line} -> + {ok,lists:reverse(Acc)} + end. + +eval_stream(Fd, Handling, Bs) -> + eval_stream(Fd, Handling, 1, undefined, [], Bs). + +eval_stream(Fd, H, Line, Last, E, Bs) -> + eval_stream2(io:parse_erl_exprs(Fd, '', Line), Fd, H, Last, E, Bs). + +eval_stream2({ok,Form,EndLine}, Fd, H, Last, E, Bs0) -> + try erl_eval:exprs(Form, Bs0) of + {value,V,Bs} -> + eval_stream(Fd, H, EndLine, {V}, E, Bs) + catch Class:Reason -> + Error = {EndLine,?MODULE,{Class,Reason,erlang:get_stacktrace()}}, + eval_stream(Fd, H, EndLine, Last, [Error|E], Bs0) + end; +eval_stream2({error,What,EndLine}, Fd, H, Last, E, Bs) -> + eval_stream(Fd, H, EndLine, Last, [What | E], Bs); +eval_stream2({eof,EndLine}, _Fd, H, Last, E, _Bs) -> + case {H, Last, E} of + {return, {Val}, []} -> + {ok, Val}; + {return, undefined, E} -> + {error, hd(lists:reverse(E, [{EndLine,?MODULE,undefined_script}]))}; + {ignore, _, []} -> + ok; + {_, _, [_|_] = E} -> + {error, hd(lists:reverse(E))} + end. + +path_open_first([Path|Rest], Name, Mode, LastError) -> + case file_name(Path) of + {error, _} = Error -> + Error; + FilePath -> + FileName = filename:join(FilePath, Name), + case open(FileName, Mode) of + {ok, Fd} -> + {ok, Fd, FileName}; + {error, enoent} -> + path_open_first(Rest, Name, Mode, LastError); + Error -> + Error + end + end; +path_open_first([], _Name, _Mode, LastError) -> + {error, LastError}. + +%%%----------------------------------------------------------------- +%%% Utility functions. + +%% file_name(FileName) +%% Generates a flat file name from a deep list of atoms and +%% characters (integers). + +file_name(N) -> + try + file_name_1(N) + catch Reason -> + {error, Reason} + end. + +file_name_1([C|T]) when is_integer(C), C > 0, C =< 255 -> + [C|file_name_1(T)]; +file_name_1([H|T]) -> + file_name_1(H) ++ file_name_1(T); +file_name_1([]) -> + []; +file_name_1(N) when is_atom(N) -> + atom_to_list(N); +file_name_1(_) -> + throw(badarg). + +make_binary(Bin) when is_binary(Bin) -> + Bin; +make_binary(List) -> + %% Convert the list to a binary in order to avoid copying a list + %% to the file server. + try + erlang:iolist_to_binary(List) + catch error:Reason -> + {error, Reason} + end. + +mode_list(read) -> + [read]; +mode_list(write) -> + [write]; +mode_list(read_write) -> + [read, write]; +mode_list({binary, Mode}) when is_atom(Mode) -> + [binary | mode_list(Mode)]; +mode_list({character, Mode}) when is_atom(Mode) -> + mode_list(Mode); +mode_list(_) -> + [{error, badarg}]. + +%%----------------------------------------------------------------- +%% Functions for communicating with the file server + +call(Command, Args) when is_list(Args) -> + gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), infinity). + +check_and_call(Command, Args) when is_list(Args) -> + case check_args(Args) of + ok -> + call(Command, Args); + Error -> + Error + end. + +check_args([{error, _}=Error|_Rest]) -> + Error; +check_args([_Name|Rest]) -> + check_args(Rest); +check_args([]) -> + ok. + +%%----------------------------------------------------------------- +%% Functions for communicating with a file io server. +%% The messages sent have the following formats: +%% +%% {file_request,From,ReplyAs,Request} +%% {file_reply,ReplyAs,Reply} + +file_request(Io, Request) -> + R = erlang:monitor(process, Io), + Io ! {file_request,self(),Io,Request}, + R. + +wait_file_reply(From, Ref) -> + receive + {file_reply,From,Reply} -> + erlang:demonitor(Ref), + receive {'DOWN', Ref, _, _, _} -> ok after 0 -> ok end, + %% receive {'EXIT', From, _} -> ok after 0 -> ok end, + Reply; + {'DOWN', Ref, _, _, _} -> + %% receive {'EXIT', From, _} -> ok after 0 -> ok end, + {error, terminated} + end. diff --git a/src/filelib2.erl b/src/filelib2.erl new file mode 100644 index 00000000..c9d1ad04 --- /dev/null +++ b/src/filelib2.erl @@ -0,0 +1,443 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(filelib2). + +%% File utilities. + +-export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1, + compile_wildcard/1]). +-export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]). + +-export([wildcard/3, is_dir/2, is_file/2, is_regular/2]). +-export([fold_files/6, last_modified/2, file_size/2]). + +-include_lib("kernel/include/file.hrl"). + +-define(HANDLE_ERROR(Expr), + try + Expr + catch + error:{badpattern,_}=UnUsUalVaRiAbLeNaMe -> + %% Get the stack backtrace correct. + erlang:error(UnUsUalVaRiAbLeNaMe) + end). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec wildcard(name()) -> [file:filename()]. +wildcard(Pattern) when is_list(Pattern) -> + ?HANDLE_ERROR(do_wildcard(Pattern, file)). + +-spec wildcard(name(), name() | atom()) -> [file:filename()]. +wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) -> + ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); +wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> + ?HANDLE_ERROR(do_wildcard(Pattern, Mod)). + +-spec wildcard(name(), name(), atom()) -> [file:filename()]. +wildcard(Pattern, Cwd, Mod) + when is_list(Pattern), is_list(Cwd), is_atom(Mod) -> + ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). + +-spec is_dir(name()) -> boolean(). +is_dir(Dir) -> + do_is_dir(Dir, file). + +-spec is_dir(name(), atom()) -> boolean(). +is_dir(Dir, Mod) when is_atom(Mod) -> + do_is_dir(Dir, Mod). + +-spec is_file(name()) -> boolean(). +is_file(File) -> + do_is_file(File, file). + +-spec is_file(name(), atom()) -> boolean(). +is_file(File, Mod) when is_atom(Mod) -> + do_is_file(File, Mod). + +-spec is_regular(name()) -> boolean(). +is_regular(File) -> + do_is_regular(File, file). + +-spec is_regular(name(), atom()) -> boolean(). +is_regular(File, Mod) when is_atom(Mod) -> + do_is_regular(File, Mod). + +-spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _) -> _. +fold_files(Dir, RegExp, Recursive, Fun, Acc) -> + do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file). + +-spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _. +fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) -> + do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod). + +-spec last_modified(name()) -> date_time() | 0. +last_modified(File) -> + do_last_modified(File, file). + +-spec last_modified(name(), atom()) -> date_time() | 0. +last_modified(File, Mod) when is_atom(Mod) -> + do_last_modified(File, Mod). + +-spec file_size(name()) -> non_neg_integer(). +file_size(File) -> + do_file_size(File, file). + +-spec file_size(name(), atom()) -> non_neg_integer(). +file_size(File, Mod) when is_atom(Mod) -> + do_file_size(File, Mod). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +do_wildcard(Pattern, Mod) when is_list(Pattern) -> + do_wildcard_comp(do_compile_wildcard(Pattern), Mod). + +do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) -> + case eval_read_file_info(File, Mod) of + {ok,_} -> [File]; + _ -> [] + end; +do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) -> + do_wildcard_1([Base], Rest, Mod). + +do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), is_list(Cwd) -> + do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod). + +do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) -> + case eval_read_file_info(filename:absname(File, Cwd), Mod) of + {ok,_} -> [File]; + _ -> [] + end; +do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) -> + Cwd = filename:join([Cwd0]), %Slash away redundant slashes. + PrefixLen = length(Cwd)+1, + [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, Mod)]; +do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) -> + do_wildcard_1([Base], Rest, Mod). + +do_is_dir(Dir, Mod) -> + case eval_read_file_info(Dir, Mod) of + {ok, #file_info{type=directory}} -> + true; + _ -> + false + end. + +do_is_file(File, Mod) -> + case eval_read_file_info(File, Mod) of + {ok, #file_info{type=regular}} -> + true; + {ok, #file_info{type=directory}} -> + true; + _ -> + false + end. + +do_is_regular(File, Mod) -> + case eval_read_file_info(File, Mod) of + {ok, #file_info{type=regular}} -> + true; + _ -> + false + end. + +%% fold_files(Dir, RegExp, Recursive, Fun, AccIn). + +%% folds the function Fun(F, Acc) -> Acc1 over +%% all files <F> in <Dir> that match the regular expression <RegExp> +%% If <Recursive> is true all sub-directories to <Dir> are processed + +do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) -> + {ok, Re1} = re:compile(RegExp), + do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod). + +do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) -> + case eval_list_dir(Dir, Mod) of + {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod); + {error, _} -> Acc + end. + +do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) -> + Acc; +do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) -> + FullName = filename:join(Dir, File), + case do_is_regular(FullName, Mod) of + true -> + case re:run(File, RegExp, [{capture,none}]) of + match -> + Acc = Fun(FullName, Acc0), + do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod); + nomatch -> + do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + end; + false -> + case Recursive andalso do_is_dir(FullName, Mod) of + true -> + Acc1 = do_fold_files1(FullName, RegExp, Recursive, + Fun, Acc0, Mod), + do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod); + false -> + do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + end + end. + +do_last_modified(File, Mod) -> + case eval_read_file_info(File, Mod) of + {ok, Info} -> + Info#file_info.mtime; + _ -> + 0 + end. + +do_file_size(File, Mod) -> + case eval_read_file_info(File, Mod) of + {ok, Info} -> + Info#file_info.size; + _ -> + 0 + end. + +%%---------------------------------------------------------------------- +%% +type ensure_dir(X) -> ok | {error, Reason}. +%% +type X = filename() | dirname() +%% ensures that the directory name required to create D exists + +-spec ensure_dir(name()) -> 'ok' | {'error', posix()}. +ensure_dir("/") -> + ok; +ensure_dir(F) -> + Dir = filename:dirname(F), + case do_is_dir(Dir, file) of + true -> + ok; + false -> + ensure_dir(Dir), + file:make_dir(Dir) + end. + + +%%% +%%% Pattern matching using a compiled wildcard. +%%% + +do_wildcard_1(Files, Pattern, Mod) -> + do_wildcard_2(Files, Pattern, [], Mod). + +do_wildcard_2([File|Rest], Pattern, Result, Mod) -> + do_wildcard_2(Rest, Pattern, do_wildcard_3(File, Pattern, Result, Mod), Mod); +do_wildcard_2([], _, Result, _Mod) -> + Result. + +do_wildcard_3(Base, [Pattern|Rest], Result, Mod) -> + case do_list_dir(Base, Mod) of + {ok, Files0} -> + Files = lists:sort(Files0), + Matches = wildcard_4(Pattern, Files, Base, []), + do_wildcard_2(Matches, Rest, Result, Mod); + _ -> + Result + end; +do_wildcard_3(Base, [], Result, _Mod) -> + [Base|Result]. + +wildcard_4(Pattern, [File|Rest], Base, Result) -> + case wildcard_5(Pattern, File) of + true -> + wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]); + false -> + wildcard_4(Pattern, Rest, Base, Result) + end; +wildcard_4(_Patt, [], _Base, Result) -> + Result. + +wildcard_5([question|Rest1], [_|Rest2]) -> + wildcard_5(Rest1, Rest2); +wildcard_5([accept], _) -> + true; +wildcard_5([star|Rest], File) -> + do_star(Rest, File); +wildcard_5([{one_of, Ordset}|Rest], [C|File]) -> + case ordsets:is_element(C, Ordset) of + true -> wildcard_5(Rest, File); + false -> false + end; +wildcard_5([{alt, Alts}], File) -> + do_alt(Alts, File); +wildcard_5([C|Rest1], [C|Rest2]) when is_integer(C) -> + wildcard_5(Rest1, Rest2); +wildcard_5([X|_], [Y|_]) when is_integer(X), is_integer(Y) -> + false; +wildcard_5([], []) -> + true; +wildcard_5([], [_|_]) -> + false; +wildcard_5([_|_], []) -> + false. + +do_star(Pattern, [X|Rest]) -> + case wildcard_5(Pattern, [X|Rest]) of + true -> true; + false -> do_star(Pattern, Rest) + end; +do_star(Pattern, []) -> + wildcard_5(Pattern, []). + +do_alt([Alt|Rest], File) -> + case wildcard_5(Alt, File) of + true -> true; + false -> do_alt(Rest, File) + end; +do_alt([], _File) -> + false. + +do_list_dir(current, Mod) -> eval_list_dir(".", Mod); +do_list_dir(Dir, Mod) -> eval_list_dir(Dir, Mod). + +join(current, File) -> File; +join(Base, File) -> filename:join(Base, File). + + +%%% Compiling a wildcard. + +compile_wildcard(Pattern) -> + ?HANDLE_ERROR(do_compile_wildcard(Pattern)). + +do_compile_wildcard(Pattern) -> + {compiled_wildcard,compile_wildcard_1(Pattern)}. + +compile_wildcard_1(Pattern) -> + [Root|Rest] = filename:split(Pattern), + case filename:pathtype(Root) of + relative -> + compile_wildcard_2([Root|Rest], current); + _ -> + compile_wildcard_2(Rest, [Root]) + end. + +compile_wildcard_2([Part|Rest], Root) -> + case compile_part(Part) of + Part -> + compile_wildcard_2(Rest, join(Root, Part)); + Pattern -> + compile_wildcard_3(Rest, [Pattern,Root]) + end; +compile_wildcard_2([], Root) -> {exists,Root}. + +compile_wildcard_3([Part|Rest], Result) -> + compile_wildcard_3(Rest, [compile_part(Part)|Result]); +compile_wildcard_3([], Result) -> + lists:reverse(Result). + +compile_part(Part) -> + compile_part(Part, false, []). + +compile_part_to_sep(Part) -> + compile_part(Part, true, []). + +compile_part([], true, _) -> + error(missing_delimiter); +compile_part([$,|Rest], true, Result) -> + {ok, $,, lists:reverse(Result), Rest}; +compile_part([$}|Rest], true, Result) -> + {ok, $}, lists:reverse(Result), Rest}; +compile_part([$?|Rest], Upto, Result) -> + compile_part(Rest, Upto, [question|Result]); +compile_part([$*], Upto, Result) -> + compile_part([], Upto, [accept|Result]); +compile_part([$*|Rest], Upto, Result) -> + compile_part(Rest, Upto, [star|Result]); +compile_part([$[|Rest], Upto, Result) -> + case compile_charset(Rest, ordsets:new()) of + {ok, Charset, Rest1} -> + compile_part(Rest1, Upto, [Charset|Result]); + error -> + compile_part(Rest, Upto, [$[|Result]) + end; +compile_part([${|Rest], Upto, Result) -> + case compile_alt(Rest) of + {ok, Alt} -> + lists:reverse(Result, [Alt]); + error -> + compile_part(Rest, Upto, [${|Result]) + end; +compile_part([X|Rest], Upto, Result) -> + compile_part(Rest, Upto, [X|Result]); +compile_part([], _Upto, Result) -> + lists:reverse(Result). + +compile_charset([$]|Rest], Ordset) -> + compile_charset1(Rest, ordsets:add_element($], Ordset)); +compile_charset([$-|Rest], Ordset) -> + compile_charset1(Rest, ordsets:add_element($-, Ordset)); +compile_charset([], _Ordset) -> + error; +compile_charset(List, Ordset) -> + compile_charset1(List, Ordset). + +compile_charset1([Lower, $-, Upper|Rest], Ordset) when Lower =< Upper -> + compile_charset1(Rest, compile_range(Lower, Upper, Ordset)); +compile_charset1([$]|Rest], Ordset) -> + {ok, {one_of, Ordset}, Rest}; +compile_charset1([X|Rest], Ordset) -> + compile_charset1(Rest, ordsets:add_element(X, Ordset)); +compile_charset1([], _Ordset) -> + error. + +compile_range(Lower, Current, Ordset) when Lower =< Current -> + compile_range(Lower, Current-1, ordsets:add_element(Current, Ordset)); +compile_range(_, _, Ordset) -> + Ordset. + +compile_alt(Pattern) -> + compile_alt(Pattern, []). + +compile_alt(Pattern, Result) -> + case compile_part_to_sep(Pattern) of + {ok, $,, AltPattern, Rest} -> + compile_alt(Rest, [AltPattern|Result]); + {ok, $}, AltPattern, Rest} -> + NewResult = [AltPattern|Result], + RestPattern = compile_part(Rest), + {ok, {alt, [Alt++RestPattern || Alt <- NewResult]}}; + Pattern -> + error + end. + +error(Reason) -> + erlang:error({badpattern,Reason}). + +eval_read_file_info(File, file) -> + file:read_file_info(File); +eval_read_file_info(File, erl_prim_loader) -> + case erl_prim_loader:read_file_info(File) of + error -> {error, erl_prim_loader}; + Res-> Res + end; +eval_read_file_info(File, Mod) -> + Mod:read_file_info(File). + +eval_list_dir(Dir, file) -> + file:list_dir(Dir); +eval_list_dir(Dir, erl_prim_loader) -> + case erl_prim_loader:list_dir(Dir) of + error -> {error, erl_prim_loader}; + Res-> Res + end; +eval_list_dir(Dir, Mod) -> + Mod:list_dir(Dir). |