ports/135593: net-im/ejabberd: ram_file_io_server makes ejabberdevel to not run with R13B_logl
Ulrich Spörlein
uqs at spoerlein.net
Wed Jul 22 12:40:06 UTC 2009
The following reply was made to PR ports/135593; it has been noted by GNATS.
From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs at spoerlein.net>
To: bug-followup at FreeBSD.org, admin at kkip.pl
Cc:
Subject: Re: ports/135593: net-im/ejabberd: ram_file_io_server makes
ejabberdevel to not run with R13B_logl
Date: Wed, 22 Jul 2009 14:35:20 +0200
--k+w/mQv8wyuph6w0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hello Bartosz,
providing a patch against the port, not some random patch against a
distfile usually improves the chances of someone comitting it.
Attached is a test patch against the current port, it works both with
the old erlang port (v12) and new current one (v13). It is the official
patch from https://support.process-one.net/browse/EJAB-919 and will come
out with ejaberd 2.1.0.
Regards,
Uli
--k+w/mQv8wyuph6w0
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="ejabberd.diff"
? work
Index: Makefile
===================================================================
RCS file: /tank/ncvs/ports/net-im/ejabberd/Makefile,v
retrieving revision 1.30
diff -u -p -r1.30 Makefile
--- Makefile 19 Jul 2009 18:59:39 -0000 1.30
+++ Makefile 22 Jul 2009 12:31:59 -0000
@@ -7,6 +7,7 @@
PORTNAME= ejabberd
PORTVERSION= 2.0.5
+PORTREVISION= 1
CATEGORIES= net-im
MASTER_SITES= http://www.process-one.net/downloads/ejabberd/${PORTVERSION}/
Index: files/patch-919-fileioserver
===================================================================
RCS file: files/patch-919-fileioserver
diff -N files/patch-919-fileioserver
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ files/patch-919-fileioserver 22 Jul 2009 12:31:59 -0000
@@ -0,0 +1,1219 @@
+--- Makefile.in
++++ Makefile.in
+@@ -131,6 +131,14 @@ $(BEAMS): $(ERLBEHAVBEAMS)
+
+ all-recursive: $(ERLBEHAVBEAMS)
+
++# Workaround for R11, that is not capable of compiling the new file:
++ram_file_io_server.beam:
++ - at ERLC@ -W $(EFLAGS) ram_file_io_server.erl
++ram_file_io_server_old.beam:
++ @ERLC@ -W $(EFLAGS) ram_file_io_server_old.erl
++ [ -f ram_file_io_server.beam ] \
++ || cp ram_file_io_server_old.beam ram_file_io_server.beam
++
+ %.beam: %.erl
+ @ERLC@ -W $(EFLAGS) $<
+
+--- ejabberd_loglevel.erl
++++ ejabberd_loglevel.erl
+@@ -68,7 +68,8 @@ compile_string(Mod, Str) ->
+ end.
+
+ open_ram_file(Fname) ->
+- ram_file_io_server:start(self(), Fname, [read,write]).
++ RamModule = get_ram_module(),
++ RamModule:start(self(), Fname, [read,write]).
+
+ close_ram_file(Fd) ->
+ file:close(Fd).
+@@ -85,6 +86,14 @@ load_logger(Forms, Mod, Loglevel) ->
+ ?CRITICAL_MSG("Error ~p~n", [Error])
+ end.
+
++%% Workaround for R11 and R12, that don't support the new module
++get_ram_module() ->
++ [AS, BS, _] = string:tokens(erlang:system_info(version), "."),
++ case (list_to_integer(AS) >= 5) and (list_to_integer(BS) >= 7) of
++ true -> ram_file_io_server;
++ false -> ram_file_io_server_old
++ end.
++
+ %% --------------------------------------------------------------
+ %% Code of the ejabberd logger, dynamically compiled and loaded
+ %% This allows to dynamically change log level while keeping a
+--- ram_file_io_server.erl
++++ ram_file_io_server.erl
+@@ -25,7 +26,9 @@
+ -export([format_error/1]).
+ -export([start/3, start_link/3]).
+
+--record(state, {handle,owner,mref,buf,read_mode}).
++-export([count_and_find/3]).
++
++-record(state, {handle,owner,mref,buf,read_mode,unic}).
+
+ -define(PRIM_FILE, ram_file).
+ -define(READ_SIZE_LIST, 128).
+@@ -44,11 +47,11 @@ format_error(ErrorId) ->
+ erl_posix_msg:message(ErrorId).
+
+ start(Owner, FileName, ModeList)
+- when pid(Owner), list(FileName), list(ModeList) ->
++ when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
+ do_start(spawn, Owner, FileName, ModeList).
+
+ start_link(Owner, FileName, ModeList)
+- when pid(Owner), list(FileName), list(ModeList) ->
++ when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
+ do_start(spawn_link, Owner, FileName, ModeList).
+
+ %%%-----------------------------------------------------------------
+@@ -61,27 +64,27 @@ do_start(Spawn, Owner, FileName, ModeList) ->
+ erlang:Spawn(
+ fun() ->
+ %% process_flag(trap_exit, true),
+- {ReadMode,Opts} =
+- case lists:member(binary, ModeList) of
+- true ->
+- {binary,ModeList};
+- false ->
+- {list,[binary|ModeList]}
+- end,
+- case ?PRIM_FILE:open(FileName, Opts) of
+- {error, Reason} = Error ->
+- Self ! {Ref, Error},
+- exit(Reason);
+- {ok, Handle} ->
+- %% XXX must I handle R6 nodes here?
+- M = erlang:monitor(process, Owner),
+- Self ! {Ref, ok},
+- server_loop(
+- #state{handle = Handle,
+- owner = Owner,
+- mref = M,
+- buf = <<>>,
+- read_mode = ReadMode})
++ case parse_options(ModeList) of
++ {ReadMode, UnicodeMode, Opts} ->
++ case ?PRIM_FILE:open(FileName, Opts) of
++ {error, Reason} = Error ->
++ Self ! {Ref, Error},
++ exit(Reason);
++ {ok, Handle} ->
++ %% XXX must I handle R6 nodes here?
++ M = erlang:monitor(process, Owner),
++ Self ! {Ref, ok},
++ server_loop(
++ #state{handle = Handle,
++ owner = Owner,
++ mref = M,
++ buf = <<>>,
++ read_mode = ReadMode,
++ unic = UnicodeMode})
++ end;
++ {error,Reason1} = Error1 ->
++ Self ! {Ref, Error1},
++ exit(Reason1)
+ end
+ end),
+ Mref = erlang:monitor(process, Pid),
+@@ -102,9 +105,61 @@ do_start(Spawn, Owner, FileName, ModeList) ->
+ {error, Reason}
+ end.
+
++%%% Returns {ReadMode, UnicodeMode, RealOpts}
++parse_options(List) ->
++ parse_options(expand_encoding(List), list, latin1, []).
++
++parse_options([],list,Uni,Acc) ->
++ {list,Uni,[binary|lists:reverse(Acc)]};
++parse_options([],binary,Uni,Acc) ->
++ {binary,Uni,lists:reverse(Acc)};
++parse_options([{encoding, Encoding}|T],RMode,_,Acc) ->
++ case valid_enc(Encoding) of
++ {ok, ExpandedEnc} ->
++ parse_options(T,RMode,ExpandedEnc,Acc);
++ {error,Reason} ->
++ {error,Reason}
++ end;
++parse_options([binary|T],_,Uni,Acc) ->
++ parse_options(T,binary,Uni,[binary|Acc]);
++parse_options([H|T],R,U,Acc) ->
++ parse_options(T,R,U,[H|Acc]).
++
++expand_encoding([]) ->
++ [];
++expand_encoding([latin1 | T]) ->
++ [{encoding,latin1} | expand_encoding(T)];
++expand_encoding([unicode | T]) ->
++ [{encoding,unicode} | expand_encoding(T)];
++expand_encoding([H|T]) ->
++ [H|expand_encoding(T)].
++
++valid_enc(latin1) ->
++ {ok,latin1};
++valid_enc(utf8) ->
++ {ok,unicode};
++valid_enc(unicode) ->
++ {ok,unicode};
++valid_enc(utf16) ->
++ {ok,{utf16,big}};
++valid_enc({utf16,big}) ->
++ {ok,{utf16,big}};
++valid_enc({utf16,little}) ->
++ {ok,{utf16,little}};
++valid_enc(utf32) ->
++ {ok,{utf32,big}};
++valid_enc({utf32,big}) ->
++ {ok,{utf32,big}};
++valid_enc({utf32,little}) ->
++ {ok,{utf32,little}};
++valid_enc(_Other) ->
++ {error,badarg}.
++
++
++
+ server_loop(#state{mref = Mref} = State) ->
+ receive
+- {file_request, From, ReplyAs, Request} when pid(From) ->
++ {file_request, From, ReplyAs, Request} when is_pid(From) ->
+ case file_request(Request, State) of
+ {reply, Reply, NewState} ->
+ file_reply(From, ReplyAs, Reply),
+@@ -118,7 +173,7 @@ server_loop(#state{mref = Mref} = State) ->
+ file_reply(From, ReplyAs, Reply),
+ exit(Reason)
+ end;
+- {io_request, From, ReplyAs, Request} when pid(From) ->
++ {io_request, From, ReplyAs, Request} when is_pid(From) ->
+ case io_request(Request, State) of
+ {reply, Reply, NewState} ->
+ io_reply(From, ReplyAs, Reply),
+@@ -152,7 +207,7 @@ file_request({pread,At,Sz},
+ case position(Handle, At, Buf) of
+ {ok,_Offs} ->
+ case ?PRIM_FILE:read(Handle, Sz) of
+- {ok,Bin} when ReadMode==list ->
++ {ok,Bin} when ReadMode =:= list ->
+ std_reply({ok,binary_to_list(Bin)}, State);
+ Reply ->
+ std_reply(Reply, State)
+@@ -203,42 +258,61 @@ std_reply(Reply, State) ->
+ %%%-----------------------------------------------------------------
+ %%% I/O request
+
+-io_request({put_chars,Chars}, % binary(Chars) new in R9C
++%% New protocol with encoding tags (R13)
++io_request({put_chars, Enc, Chars},
+ #state{buf= <<>>}=State) ->
+- put_chars(Chars, State);
+-io_request({put_chars,Chars}, % binary(Chars) new in R9C
++ put_chars(Chars, Enc, State);
++io_request({put_chars, Enc, Chars},
+ #state{handle=Handle,buf=Buf}=State) ->
+ case position(Handle, cur, Buf) of
+ {error,_}=Reply ->
+ {stop,normal,Reply,State#state{buf= <<>>}};
+ _ ->
+- put_chars(Chars, State#state{buf= <<>>})
++ put_chars(Chars, Enc, State#state{buf= <<>>})
+ end;
+-io_request({put_chars,Mod,Func,Args},
++io_request({put_chars,Enc,Mod,Func,Args},
+ #state{}=State) ->
+ case catch apply(Mod, Func, Args) of
+- Chars when list(Chars); binary(Chars) ->
+- io_request({put_chars,Chars}, State);
++ Chars when is_list(Chars); is_binary(Chars) ->
++ io_request({put_chars,Enc,Chars}, State);
+ _ ->
+ {error,{error,Func},State}
+ end;
+-io_request({get_until,_Prompt,Mod,Func,XtraArgs},
+- #state{}=State) ->
+- get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, State);
+-io_request({get_chars,_Prompt,N}, % New in R9C
++
++
++io_request({get_until,Enc,_Prompt,Mod,Func,XtraArgs},
+ #state{}=State) ->
+- get_chars(N, State);
+-io_request({get_chars,_Prompt,Mod,Func,XtraArg}, % New in R9C
++ get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, Enc, State);
++io_request({get_chars,Enc,_Prompt,N},
+ #state{}=State) ->
+- get_chars(Mod, Func, XtraArg, State);
+-io_request({get_line,_Prompt}, % New in R9C
++ get_chars(N, Enc, State);
++io_request({get_line,Enc,_Prompt},
+ #state{}=State) ->
+- get_chars(io_lib, collect_line, [], State);
+-io_request({setopts, Opts}, % New in R9C
+- #state{}=State) when list(Opts) ->
++ get_chars(io_lib, collect_line, [], Enc, State);
++
++
++io_request({setopts, Opts},
++ #state{}=State) when is_list(Opts) ->
+ setopts(Opts, State);
++
++io_request(getopts,
++ #state{}=State) ->
++ getopts(State);
++
++%% BC with pre-R13 nodes
++io_request({put_chars, Chars},#state{}=State) ->
++ io_request({put_chars, latin1, Chars},State);
++io_request({put_chars,Mod,Func,Args}, #state{}=State) ->
++ io_request({put_chars,latin1,Mod,Func,Args}, State);
++io_request({get_until,_Prompt,Mod,Func,XtraArgs}, #state{}=State) ->
++ io_request({get_until,latin1,_Prompt,Mod,Func,XtraArgs}, State);
++io_request({get_chars,_Prompt,N}, #state{}=State) ->
++ io_request({get_chars,latin1,_Prompt,N}, State);
++io_request({get_line,_Prompt}, #state{}=State) ->
++ io_request({get_line,latin1,_Prompt}, State);
++
+ io_request({requests,Requests},
+- #state{}=State) when list(Requests) ->
++ #state{}=State) when is_list(Requests) ->
+ io_request_loop(Requests, {reply,ok,State});
+ io_request(Unknown,
+ #state{}=State) ->
+@@ -265,76 +339,213 @@ io_request_loop([Request|Tail],
+
+ %% I/O request put_chars
+ %%
+-put_chars(Chars, #state{handle=Handle}=State) ->
++put_chars(Chars, latin1, #state{handle=Handle, unic=latin1}=State) ->
+ case ?PRIM_FILE:write(Handle, Chars) of
+ {error,_}=Reply ->
+ {stop,normal,Reply,State};
+ Reply ->
+ {reply,Reply,State}
++ end;
++put_chars(Chars, InEncoding, #state{handle=Handle, unic=OutEncoding}=State) ->
++ case unicode:characters_to_binary(Chars,InEncoding,OutEncoding) of
++ Bin when is_binary(Bin) ->
++ case ?PRIM_FILE:write(Handle, Bin) of
++ {error,_}=Reply ->
++ {stop,normal,Reply,State};
++ Reply ->
++ {reply,Reply,State}
++ end;
++ {error,_,_} ->
++ {stop,normal,{error,{no_translation, InEncoding, OutEncoding}},State}
+ end.
+
+
+ %% Process the I/O request get_chars
+ %%
+-get_chars(0, #state{read_mode=ReadMode}=State) ->
+- {reply,cast(<<>>, ReadMode),State};
+-get_chars(N, #state{buf=Buf,read_mode=ReadMode}=State)
+- when integer(N), N > 0, N =< size(Buf) ->
++get_chars(0, Enc, #state{read_mode=ReadMode,unic=InEncoding}=State) ->
++ {reply,cast(<<>>, ReadMode,InEncoding, Enc),State};
++get_chars(N, Enc, #state{buf=Buf,read_mode=ReadMode,unic=latin1}=State)
++ when is_integer(N), N > 0, N =< byte_size(Buf) ->
++ {B1,B2} = split_binary(Buf, N),
++ {reply,cast(B1, ReadMode,latin1,Enc),State#state{buf=B2}};
++get_chars(N, Enc, #state{buf=Buf,read_mode=ReadMode,unic=latin1}=State)
++ when is_integer(N), N > 0, N =< byte_size(Buf) ->
+ {B1,B2} = split_binary(Buf, N),
+- {reply,cast(B1, ReadMode),State#state{buf=B2}};
+-get_chars(N, #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State)
+- when integer(N), N > 0 ->
+- BufSize = size(Buf),
++ {reply,cast(B1, ReadMode,latin1,Enc),State#state{buf=B2}};
++get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=latin1}=State)
++ when is_integer(N), N > 0 ->
++ BufSize = byte_size(Buf),
+ NeedSize = N-BufSize,
+- Size = max(NeedSize, ?READ_SIZE_BINARY),
++ Size = erlang:max(NeedSize, ?READ_SIZE_BINARY),
+ case ?PRIM_FILE:read(Handle, Size) of
+ {ok, B} ->
+- if BufSize+size(B) < N ->
+- std_reply(cat(Buf, B, ReadMode), State);
++ if BufSize+byte_size(B) < N ->
++ std_reply(cat(Buf, B, ReadMode,latin1,OutEnc), State);
+ true ->
+ {B1,B2} = split_binary(B, NeedSize),
+- {reply,cat(Buf, B1, ReadMode),State#state{buf=B2}}
++ {reply,cat(Buf, B1, ReadMode, latin1,OutEnc),State#state{buf=B2}}
+ end;
+- eof when BufSize==0 ->
++ eof when BufSize =:= 0 ->
+ {reply,eof,State};
+ eof ->
+- std_reply(cast(Buf, ReadMode), State);
++ std_reply(cast(Buf, ReadMode,latin1,OutEnc), State);
+ {error,Reason}=Error ->
+ {stop,Reason,Error,State#state{buf= <<>>}}
+ end;
+-get_chars(_N, #state{}=State) ->
++get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=InEncoding}=State)
++ when is_integer(N), N > 0 ->
++ try
++ %% This is rather tricky, we need to count the actual number of characters
++ %% in the buffer first as unicode characters are not constant in length
++ {BufCount, SplitPos} = count_and_find(Buf,N,InEncoding),
++ case BufCount >= N of
++ true ->
++ {B1,B2} = case SplitPos of
++ none -> {Buf,<<>>};
++ _ ->split_binary(Buf,SplitPos)
++ end,
++ {reply,cast(B1, ReadMode,InEncoding,OutEnc),State#state{buf=B2}};
++ false ->
++ %% Need more, Try to read 4*needed in bytes...
++ NeedSize = (N - BufCount) * 4,
++ Size = erlang:max(NeedSize, ?READ_SIZE_BINARY),
++ case ?PRIM_FILE:read(Handle, Size) of
++ {ok, B} ->
++ NewBuf = list_to_binary([Buf,B]),
++ {NewCount,NewSplit} = count_and_find(NewBuf,N,InEncoding),
++ case NewCount >= N of
++ true ->
++ {B01,B02} = case NewSplit of
++ none -> {NewBuf,<<>>};
++ _ ->split_binary(NewBuf, NewSplit)
++ end,
++ {reply,cast(B01, ReadMode,InEncoding,OutEnc),
++ State#state{buf=B02}};
++ false ->
++ %% Reached end of file
++ std_reply(cast(NewBuf, ReadMode,InEncoding,OutEnc),
++ State#state{buf = <<>>})
++ end;
++ eof when BufCount =:= 0 ->
++ {reply,eof,State};
++ eof ->
++ std_reply(cast(Buf, ReadMode,InEncoding,OutEnc), State#state{buf = <<>>});
++ {error,Reason}=Error ->
++ {stop,Reason,Error,State#state{buf = <<>>}}
++ end
++ end
++ catch
++ exit:ExError ->
++ {stop,ExError,{error,ExError},State#state{buf= <<>>}}
++ end;
++
++get_chars(_N, _, #state{}=State) ->
+ {error,{error,get_chars},State}.
+
+-get_chars(Mod, Func, XtraArg, #state{buf= <<>>}=State) ->
+- get_chars_empty(Mod, Func, XtraArg, start, State);
+-get_chars(Mod, Func, XtraArg, #state{buf=Buf}=State) ->
+- get_chars_apply(Mod, Func, XtraArg, start, State#state{buf= <<>>}, Buf).
++get_chars(Mod, Func, XtraArg, OutEnc, #state{buf= <<>>}=State) ->
++ get_chars_empty(Mod, Func, XtraArg, start, OutEnc, State);
++get_chars(Mod, Func, XtraArg, OutEnc, #state{buf=Buf}=State) ->
++ get_chars_apply(Mod, Func, XtraArg, start, OutEnc, State#state{buf= <<>>}, Buf).
+
+-get_chars_empty(Mod, Func, XtraArg, S,
++get_chars_empty(Mod, Func, XtraArg, S, latin1,
++ #state{handle=Handle,read_mode=ReadMode, unic=latin1}=State) ->
++ case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
++ {ok,Bin} ->
++ get_chars_apply(Mod, Func, XtraArg, S, latin1, State, Bin);
++ eof ->
++ get_chars_apply(Mod, Func, XtraArg, S, latin1, State, eof);
++ {error,Reason}=Error ->
++ {stop,Reason,Error,State}
++ end;
++get_chars_empty(Mod, Func, XtraArg, S, OutEnc,
+ #state{handle=Handle,read_mode=ReadMode}=State) ->
+ case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
+ {ok,Bin} ->
+- get_chars_apply(Mod, Func, XtraArg, S, State, Bin);
++ get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, Bin);
++ eof ->
++ get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof);
++ {error,Reason}=Error ->
++ {stop,Reason,Error,State}
++ end.
++get_chars_notempty(Mod, Func, XtraArg, S, OutEnc,
++ #state{handle=Handle,read_mode=ReadMode,buf = B}=State) ->
++ case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
++ {ok,Bin} ->
++ get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, list_to_binary([Bin,B]));
+ eof ->
+- get_chars_apply(Mod, Func, XtraArg, S, State, eof);
++ case B of
++ <<>> ->
++ get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof);
++ _ ->
++ {stop,invalid_unicode,{error,invalid_unicode},State}
++ end;
+ {error,Reason}=Error ->
+ {stop,Reason,Error,State}
+ end.
+
+-get_chars_apply(Mod, Func, XtraArg, S0,
+- #state{read_mode=ReadMode}=State, Data0) ->
++
++get_chars_apply(Mod, Func, XtraArg, S0, latin1,
++ #state{read_mode=ReadMode,unic=latin1}=State, Data0) ->
+ Data1 = case ReadMode of
+- list when binary(Data0) -> binary_to_list(Data0);
++ list when is_binary(Data0) -> binary_to_list(Data0);
+ _ -> Data0
+ end,
+- case catch Mod:Func(S0, Data1, XtraArg) of
++ case catch Mod:Func(S0, Data1, latin1, XtraArg) of
+ {stop,Result,Buf} ->
+ {reply,Result,State#state{buf=cast_binary(Buf)}};
+ {'EXIT',Reason} ->
+ {stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
+ S1 ->
+- get_chars_empty(Mod, Func, XtraArg, S1, State)
++ get_chars_empty(Mod, Func, XtraArg, S1, latin1, State)
++ end;
++get_chars_apply(Mod, Func, XtraArg, S0, OutEnc,
++ #state{read_mode=ReadMode,unic=InEnc}=State, Data0) ->
++ try
++ {Data1,NewBuff} = case ReadMode of
++ list when is_binary(Data0) ->
++ case unicode:characters_to_list(Data0,InEnc) of
++ {Tag,Decoded,Rest} when Decoded =/= [], Tag =:= error; Decoded =/= [], Tag =:= incomplete ->
++ {Decoded,erlang:iolist_to_binary(Rest)};
++ {Tag, [], _} when Tag =:= error; Tag =:= incomplete ->
++ exit(invalid_unicode);
++ List when is_list(List) ->
++ {List,<<>>}
++ end;
++ binary when is_binary(Data0) ->
++ case unicode:characters_to_binary(Data0,InEnc,OutEnc) of
++ {Tag2,Decoded2,Rest2} when Decoded2 =/= <<>>, Tag2 =:= error; Decoded2 =/= <<>>, Tag2 =:= incomplete ->
++ {Decoded2,erlang:iolist_to_binary(Rest2)};
++ {Tag2, <<>>, _} when Tag2 =:= error; Tag2 =:= incomplete ->
++ exit(invalid_unicode);
++ Binary when is_binary(Binary) ->
++ {Binary,<<>>}
++ end;
++ _ -> %i.e. eof
++ {Data0,<<>>}
++ end,
++ case catch Mod:Func(S0, Data1, OutEnc, XtraArg) of
++ {stop,Result,Buf} ->
++ {reply,Result,State#state{buf = (if
++ is_binary(Buf) ->
++ unicode:characters_to_binary(Buf,OutEnc,InEnc);
++ is_list(Buf) ->
++ unicode:characters_to_binary(Buf,unicode,InEnc);
++ true ->
++ <<>>
++ end)}};
++ {'EXIT',Reason} ->
++ {stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
++ S1 ->
++ get_chars_notempty(Mod, Func, XtraArg, S1, OutEnc, State#state{buf=NewBuff})
++ end
++ catch
++ exit:ExReason ->
++ {stop,ExReason,{error,err_func(Mod, Func, XtraArg)},State};
++ error:ErrReason ->
++ {stop,ErrReason,{error,err_func(Mod, Func, XtraArg)},State}
+ end.
++
++
+
+ %% Convert error code to make it look as before
+ err_func(io_lib, get_until, {_,F,_}) ->
+@@ -347,35 +558,100 @@ err_func(_, F, _) ->
+ %% Process the I/O request setopts
+ %%
+ %% setopts
+-setopts(Opts0, State) ->
+- Opts = proplists:substitute_negations([{list,binary}], Opts0),
+- case proplists:get_value(binary, Opts) of
++setopts(Opts0,State) ->
++ Opts = proplists:unfold(
++ proplists:substitute_negations(
++ [{list,binary}],
++ expand_encoding(Opts0))),
++ case check_valid_opts(Opts) of
+ true ->
+- {ok,ok,State#state{read_mode=binary}};
++ do_setopts(Opts,State);
+ false ->
+- {ok,ok,State#state{read_mode=list}};
++ {error,{error,enotsup},State}
++ end.
++check_valid_opts([]) ->
++ true;
++check_valid_opts([{binary,_}|T]) ->
++ check_valid_opts(T);
++check_valid_opts([{encoding,_Enc}|T]) ->
++ check_valid_opts(T);
++check_valid_opts(_) ->
++ false.
++do_setopts(Opts, State) ->
++ case valid_enc(proplists:get_value(encoding, Opts, State#state.unic)) of
++ {ok,NewUnic} ->
++ case proplists:get_value(binary, Opts) of
++ true ->
++ {reply,ok,State#state{read_mode=binary, unic=NewUnic}};
++ false ->
++ {reply,ok,State#state{read_mode=list, unic=NewUnic}};
++ undefined ->
++ {reply,ok,State#state{unic=NewUnic}}
++ end;
+ _ ->
+- {error,{error,badarg},State}
++ {error,{error,badarg},State}
+ end.
+
+-
++getopts(#state{read_mode=RM, unic=Unic} = State) ->
++ Bin = {binary, case RM of
++ binary ->
++ true;
++ _ ->
++ false
++ end},
++ Uni = {encoding, Unic},
++ {reply,[Bin,Uni],State}.
++
+
+ %% Concatenate two binaries and convert the result to list or binary
+-cat(B1, B2, binary) ->
++cat(B1, B2, binary,latin1,latin1) ->
+ list_to_binary([B1,B2]);
+-cat(B1, B2, list) ->
++cat(B1, B2, binary,InEncoding,OutEncoding) ->
++ case unicode:characters_to_binary([B1,B2],InEncoding,OutEncoding) of
++ Good when is_binary(Good) ->
++ Good;
++ _ ->
++ exit({no_translation,InEncoding,OutEncoding})
++ end;
++%% Dialyzer finds this is never used...
++%% cat(B1, B2, list, InEncoding, OutEncoding) when InEncoding =/= latin1 ->
++%% % Catch i.e. unicode -> latin1 errors by using the outencoding although otherwise
++%% % irrelevant for lists...
++%% try
++%% unicode:characters_to_list(unicode:characters_to_binary([B1,B2],InEncoding,OutEncoding),
++%% OutEncoding)
++%% catch
++%% error:_ ->
++%% exit({no_translation,InEncoding,OutEncoding})
++%% end.
++cat(B1, B2, list, latin1,_) ->
+ binary_to_list(B1)++binary_to_list(B2).
+
+ %% Cast binary to list or binary
+-cast(B, binary) ->
++cast(B, binary, latin1, latin1) ->
+ B;
+-cast(B, list) ->
+- binary_to_list(B).
++cast(B, binary, InEncoding, OutEncoding) ->
++ case unicode:characters_to_binary(B,InEncoding,OutEncoding) of
++ Good when is_binary(Good) ->
++ Good;
++ _ ->
++ exit({no_translation,InEncoding,OutEncoding})
++ end;
++cast(B, list, latin1, _) ->
++ binary_to_list(B);
++cast(B, list, InEncoding, OutEncoding) ->
++ try
++ unicode:characters_to_list(unicode:characters_to_binary(B,InEncoding,OutEncoding),
++ OutEncoding)
++ catch
++ error:_ ->
++ exit({no_translation,InEncoding,OutEncoding})
++ end.
+
+ %% Convert buffer to binary
+-cast_binary(Binary) when binary(Binary) ->
++cast_binary(Binary) when is_binary(Binary) ->
+ Binary;
+-cast_binary(List) when list(List) ->
++cast_binary(List) when is_list(List) ->
+ list_to_binary(List);
+ cast_binary(_EOF) ->
+ <<>>.
+@@ -386,10 +662,150 @@ read_size(binary) ->
+ read_size(list) ->
+ ?READ_SIZE_LIST.
+
+-max(A, B) when A >= B ->
+- A;
+-max(_, B) ->
+- B.
++%% Utf utility
++count_and_find(Bin,N,Encoding) ->
++ cafu(Bin,N,0,0,none,case Encoding of
++ unicode -> utf8;
++ Oth -> Oth
++ end).
++
++cafu(<<>>,0,Count,ByteCount,_SavePos,_) ->
++ {Count,ByteCount};
++cafu(<<>>,_N,Count,_ByteCount,SavePos,_) ->
++ {Count,SavePos};
++cafu(<<_/utf8,Rest/binary>>, 0, Count, ByteCount, _SavePos, utf8) ->
++ cafu(Rest,-1,Count+1,0,ByteCount,utf8);
++cafu(<<_/utf8,Rest/binary>>, N, Count, _ByteCount, SavePos, utf8) when N < 0 ->
++ cafu(Rest,-1,Count+1,0,SavePos,utf8);
++cafu(<<_/utf8,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, utf8) ->
++ Delta = byte_size(Whole) - byte_size(Rest),
++ cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,utf8);
++cafu(<<_/utf16-big,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf16,big}) ->
++ cafu(Rest,-1,Count+1,0,ByteCount,{utf16,big});
++cafu(<<_/utf16-big,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf16,big}) when N < 0 ->
++ cafu(Rest,-1,Count+1,0,SavePos,{utf16,big});
++cafu(<<_/utf16-big,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf16,big}) ->
++ Delta = byte_size(Whole) - byte_size(Rest),
++ cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf16,big});
++cafu(<<_/utf16-little,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf16,little}) ->
++ cafu(Rest,-1,Count+1,0,ByteCount,{utf16,little});
++cafu(<<_/utf16-little,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf16,little}) when N < 0 ->
++ cafu(Rest,-1,Count+1,0,SavePos,{utf16,little});
++cafu(<<_/utf16-little,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf16,little}) ->
++ Delta = byte_size(Whole) - byte_size(Rest),
++ cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf16,little});
++cafu(<<_/utf32-big,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf32,big}) ->
++ cafu(Rest,-1,Count+1,0,ByteCount,{utf32,big});
++cafu(<<_/utf32-big,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf32,big}) when N < 0 ->
++ cafu(Rest,-1,Count+1,0,SavePos,{utf32,big});
++cafu(<<_/utf32-big,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf32,big}) ->
++ Delta = byte_size(Whole) - byte_size(Rest),
++ cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf32,big});
++cafu(<<_/utf32-little,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf32,little}) ->
++ cafu(Rest,-1,Count+1,0,ByteCount,{utf32,little});
++cafu(<<_/utf32-little,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf32,little}) when N < 0 ->
++ cafu(Rest,-1,Count+1,0,SavePos,{utf32,little});
++cafu(<<_/utf32-little,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf32,little}) ->
++ Delta = byte_size(Whole) - byte_size(Rest),
++ cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf32,little});
++cafu(_Other,0,Count,ByteCount,_,_) -> % Non Unicode character,
++ % but found our point, OK this time
++ {Count,ByteCount};
++cafu(Other,_N,Count,0,_SavePos,Enc) -> % Not enough, but valid chomped unicode
++ % at end.
++ case cbv(Enc,Other) of
++ false ->
++ exit(invalid_unicode);
++ _ ->
++ {Count,none}
++ end;
++cafu(Other,_N,Count,ByteCount,none,Enc) -> % Return what we'we got this far
++ % although not complete,
++ % it's not (yet) in error
++ case cbv(Enc,Other) of
++ false ->
++ exit(invalid_unicode);
++ _ ->
++ {Count,ByteCount}
++ end;
++cafu(Other,_N,Count,_ByteCount,SavePos,Enc) -> % As above but we have
++ % found a position
++ case cbv(Enc,Other) of
++ false ->
++ exit(invalid_unicode);
++ _ ->
++ {Count,SavePos}
++ end.
++
++%%
++%% Bluntly stolen from stdlib/unicode.erl (cbv means can be valid?)
++%%
++cbv(utf8,<<1:1,1:1,0:1,_:5>>) ->
++ 1;
++cbv(utf8,<<1:1,1:1,1:1,0:1,_:4,R/binary>>) ->
++ case R of
++ <<>> ->
++ 2;
++ <<1:1,0:1,_:6>> ->
++ 1;
++ _ ->
++ false
++ end;
++cbv(utf8,<<1:1,1:1,1:1,1:1,0:1,_:3,R/binary>>) ->
++ case R of
++ <<>> ->
++ 3;
++ <<1:1,0:1,_:6>> ->
++ 2;
++ <<1:1,0:1,_:6,1:1,0:1,_:6>> ->
++ 1;
++ _ ->
++ false
++ end;
++cbv(utf8,_) ->
++ false;
++
++cbv({utf16,big},<<A:8>>) when A =< 215; A >= 224 ->
++ 1;
++cbv({utf16,big},<<54:6,_:2>>) ->
++ 3;
++cbv({utf16,big},<<54:6,_:10>>) ->
++ 2;
++cbv({utf16,big},<<54:6,_:10,55:6,_:2>>) ->
++ 1;
++cbv({utf16,big},_) ->
++ false;
++cbv({utf16,little},<<_:8>>) ->
++ 1; % or 3, we'll see
++cbv({utf16,little},<<_:8,54:6,_:2>>) ->
++ 2;
++cbv({utf16,little},<<_:8,54:6,_:2,_:8>>) ->
++ 1;
++cbv({utf16,little},_) ->
++ false;
++
++
++cbv({utf32,big}, <<0:8>>) ->
++ 3;
++cbv({utf32,big}, <<0:8,X:8>>) when X =< 16 ->
++ 2;
++cbv({utf32,big}, <<0:8,X:8,Y:8>>)
++ when X =< 16, ((X > 0) or ((Y =< 215) or (Y >= 224))) ->
++ 1;
++cbv({utf32,big},_) ->
++ false;
++cbv({utf32,little},<<_:8>>) ->
++ 3;
++cbv({utf32,little},<<_:8,_:8>>) ->
++ 2;
++cbv({utf32,little},<<X:8,255:8,0:8>>) when X =:= 254; X =:= 255 ->
++ false;
++cbv({utf32,little},<<_:8,Y:8,X:8>>)
++ when X =< 16, ((X > 0) or ((Y =< 215) or (Y >= 224))) ->
++ 1;
++cbv({utf32,little},_) ->
++ false.
++
+
+ %%%-----------------------------------------------------------------
+ %%% ?PRIM_FILE helpers
+@@ -399,10 +815,10 @@ max(_, B) ->
+
+ position(Handle, cur, Buf) ->
+ position(Handle, {cur, 0}, Buf);
+-position(Handle, {cur, Offs}, Buf) when list(Buf) ->
++position(Handle, {cur, Offs}, Buf) when is_list(Buf) ->
+ ?PRIM_FILE:position(Handle, {cur, Offs-length(Buf)});
+-position(Handle, {cur, Offs}, Buf) when binary(Buf) ->
+- ?PRIM_FILE:position(Handle, {cur, Offs-size(Buf)});
++position(Handle, {cur, Offs}, Buf) when is_binary(Buf) ->
++ ?PRIM_FILE:position(Handle, {cur, Offs-byte_size(Buf)});
+ position(Handle, At, _Buf) ->
+ ?PRIM_FILE:position(Handle, At).
+
+--- /dev/null
++++ ram_file_io_server_old.erl
+@@ -0,0 +1,408 @@
++%% ``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 via the world wide web 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.
++%%
++%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
++%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
++%% AB. All Rights Reserved.''
++%%
++%% $Id$
++%%
++%% This file is mostly copied from Erlang file_io_server.erl
++%% See: http://www.erlang.org/ml-archive/erlang-questions/200607/msg00080.html
++%% for details on ram_file_io_server.erl (Erlang OTP R11B-2)
++-module(ram_file_io_server_old).
++
++%% A simple file server for io to one file instance per server instance.
++
++-export([format_error/1]).
++-export([start/3, start_link/3]).
++
++-record(state, {handle,owner,mref,buf,read_mode}).
++
++-define(PRIM_FILE, ram_file).
++-define(READ_SIZE_LIST, 128).
++-define(READ_SIZE_BINARY, (8*1024)).
++
++-define(eat_message(M, T), receive M -> M after T -> timeout end).
++
++%%%-----------------------------------------------------------------
++%%% Exported functions
++
++format_error({_Line, ?MODULE, Reason}) ->
++ io_lib:format("~w", [Reason]);
++format_error({_Line, Mod, Reason}) ->
++ Mod:format_error(Reason);
++format_error(ErrorId) ->
++ erl_posix_msg:message(ErrorId).
++
++start(Owner, FileName, ModeList)
++ when pid(Owner), list(FileName), list(ModeList) ->
++ do_start(spawn, Owner, FileName, ModeList).
++
++start_link(Owner, FileName, ModeList)
++ when pid(Owner), list(FileName), list(ModeList) ->
++ do_start(spawn_link, Owner, FileName, ModeList).
++
++%%%-----------------------------------------------------------------
++%%% Server starter, dispatcher and helpers
++
++do_start(Spawn, Owner, FileName, ModeList) ->
++ Self = self(),
++ Ref = make_ref(),
++ Pid =
++ erlang:Spawn(
++ fun() ->
++ %% process_flag(trap_exit, true),
++ {ReadMode,Opts} =
++ case lists:member(binary, ModeList) of
++ true ->
++ {binary,ModeList};
++ false ->
++ {list,[binary|ModeList]}
++ end,
++ case ?PRIM_FILE:open(FileName, Opts) of
++ {error, Reason} = Error ->
++ Self ! {Ref, Error},
++ exit(Reason);
++ {ok, Handle} ->
++ %% XXX must I handle R6 nodes here?
++ M = erlang:monitor(process, Owner),
++ Self ! {Ref, ok},
++ server_loop(
++ #state{handle = Handle,
++ owner = Owner,
++ mref = M,
++ buf = <<>>,
++ read_mode = ReadMode})
++ end
++ end),
++ Mref = erlang:monitor(process, Pid),
++ receive
++ {Ref, {error, _Reason} = Error} ->
++ erlang:demonitor(Mref),
++ receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end,
++ Error;
++ {Ref, ok} ->
++ erlang:demonitor(Mref),
++ receive
++ {'DOWN', Mref, _, _, Reason} ->
++ {error, Reason}
++ after 0 ->
++ {ok, Pid}
++ end;
++ {'DOWN', Mref, _, _, Reason} ->
++ {error, Reason}
++ end.
++
++server_loop(#state{mref = Mref} = State) ->
++ receive
++ {file_request, From, ReplyAs, Request} when pid(From) ->
++ case file_request(Request, State) of
++ {reply, Reply, NewState} ->
++ file_reply(From, ReplyAs, Reply),
++ server_loop(NewState);
++ {error, Reply, NewState} ->
++ %% error is the same as reply, except that
++ %% it breaks the io_request_loop further down
++ file_reply(From, ReplyAs, Reply),
++ server_loop(NewState);
++ {stop, Reason, Reply, _NewState} ->
++ file_reply(From, ReplyAs, Reply),
++ exit(Reason)
++ end;
++ {io_request, From, ReplyAs, Request} when pid(From) ->
++ case io_request(Request, State) of
++ {reply, Reply, NewState} ->
++ io_reply(From, ReplyAs, Reply),
++ server_loop(NewState);
++ {error, Reply, NewState} ->
++ %% error is the same as reply, except that
++ %% it breaks the io_request_loop further down
++ io_reply(From, ReplyAs, Reply),
++ server_loop(NewState);
++ {stop, Reason, Reply, _NewState} ->
++ io_reply(From, ReplyAs, Reply),
++ exit(Reason)
++ end;
++ {'DOWN', Mref, _, _, Reason} ->
++ exit(Reason);
++ _ ->
++ server_loop(State)
++ end.
++
++file_reply(From, ReplyAs, Reply) ->
++ From ! {file_reply, ReplyAs, Reply}.
++
++io_reply(From, ReplyAs, Reply) ->
++ From ! {io_reply, ReplyAs, Reply}.
++
++%%%-----------------------------------------------------------------
++%%% file requests
++
++file_request({pread,At,Sz},
++ #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) ->
++ case position(Handle, At, Buf) of
++ {ok,_Offs} ->
++ case ?PRIM_FILE:read(Handle, Sz) of
++ {ok,Bin} when ReadMode==list ->
++ std_reply({ok,binary_to_list(Bin)}, State);
++ Reply ->
++ std_reply(Reply, State)
++ end;
++ Reply ->
++ std_reply(Reply, State)
++ end;
++file_request({pwrite,At,Data},
++ #state{handle=Handle,buf=Buf}=State) ->
++ case position(Handle, At, Buf) of
++ {ok,_Offs} ->
++ std_reply(?PRIM_FILE:write(Handle, Data), State);
++ Reply ->
++ std_reply(Reply, State)
++ end;
++file_request(sync,
++ #state{handle=Handle}=State) ->
++ case ?PRIM_FILE:sync(Handle) of
++ {error,_}=Reply ->
++ {stop,normal,Reply,State};
++ Reply ->
++ {reply,Reply,State}
++ end;
++file_request(close,
++ #state{handle=Handle}=State) ->
++ {stop,normal,?PRIM_FILE:close(Handle),State#state{buf= <<>>}};
++file_request({position,At},
++ #state{handle=Handle,buf=Buf}=State) ->
++ std_reply(position(Handle, At, Buf), State);
++file_request(truncate,
++ #state{handle=Handle}=State) ->
++ case ?PRIM_FILE:truncate(Handle) of
++ {error,_Reason}=Reply ->
++ {stop,normal,Reply,State#state{buf= <<>>}};
++ Reply ->
++ {reply,Reply,State}
++ end;
++file_request(Unknown,
++ #state{}=State) ->
++ Reason = {request, Unknown},
++ {error,{error,Reason},State}.
++
++std_reply({error,_}=Reply, State) ->
++ {error,Reply,State#state{buf= <<>>}};
++std_reply(Reply, State) ->
++ {reply,Reply,State#state{buf= <<>>}}.
++
++%%%-----------------------------------------------------------------
++%%% I/O request
++
++io_request({put_chars,Chars}, % binary(Chars) new in R9C
++ #state{buf= <<>>}=State) ->
++ put_chars(Chars, State);
++io_request({put_chars,Chars}, % binary(Chars) new in R9C
++ #state{handle=Handle,buf=Buf}=State) ->
++ case position(Handle, cur, Buf) of
++ {error,_}=Reply ->
++ {stop,normal,Reply,State#state{buf= <<>>}};
++ _ ->
++ put_chars(Chars, State#state{buf= <<>>})
++ end;
++io_request({put_chars,Mod,Func,Args},
++ #state{}=State) ->
++ case catch apply(Mod, Func, Args) of
++ Chars when list(Chars); binary(Chars) ->
++ io_request({put_chars,Chars}, State);
++ _ ->
++ {error,{error,Func},State}
++ end;
++io_request({get_until,_Prompt,Mod,Func,XtraArgs},
++ #state{}=State) ->
++ get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, State);
++io_request({get_chars,_Prompt,N}, % New in R9C
++ #state{}=State) ->
++ get_chars(N, State);
++io_request({get_chars,_Prompt,Mod,Func,XtraArg}, % New in R9C
++ #state{}=State) ->
++ get_chars(Mod, Func, XtraArg, State);
++io_request({get_line,_Prompt}, % New in R9C
++ #state{}=State) ->
++ get_chars(io_lib, collect_line, [], State);
++io_request({setopts, Opts}, % New in R9C
++ #state{}=State) when list(Opts) ->
++ setopts(Opts, State);
++io_request({requests,Requests},
++ #state{}=State) when list(Requests) ->
++ io_request_loop(Requests, {reply,ok,State});
++io_request(Unknown,
++ #state{}=State) ->
++ Reason = {request,Unknown},
++ {error,{error,Reason},State}.
++
++
++
++%% Process a list of requests as long as the results are ok.
++
++io_request_loop([], Result) ->
++ Result;
++io_request_loop([_Request|_Tail],
++ {stop,_Reason,_Reply,_State}=Result) ->
++ Result;
++io_request_loop([_Request|_Tail],
++ {error,_Reply,_State}=Result) ->
++ Result;
++io_request_loop([Request|Tail],
++ {reply,_Reply,State}) ->
++ io_request_loop(Tail, io_request(Request, State)).
++
++
++
++%% I/O request put_chars
++%%
++put_chars(Chars, #state{handle=Handle}=State) ->
++ case ?PRIM_FILE:write(Handle, Chars) of
++ {error,_}=Reply ->
++ {stop,normal,Reply,State};
++ Reply ->
++ {reply,Reply,State}
++ end.
++
++
++%% Process the I/O request get_chars
++%%
++get_chars(0, #state{read_mode=ReadMode}=State) ->
++ {reply,cast(<<>>, ReadMode),State};
++get_chars(N, #state{buf=Buf,read_mode=ReadMode}=State)
++ when integer(N), N > 0, N =< size(Buf) ->
++ {B1,B2} = split_binary(Buf, N),
++ {reply,cast(B1, ReadMode),State#state{buf=B2}};
++get_chars(N, #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State)
++ when integer(N), N > 0 ->
++ BufSize = size(Buf),
++ NeedSize = N-BufSize,
++ Size = max(NeedSize, ?READ_SIZE_BINARY),
++ case ?PRIM_FILE:read(Handle, Size) of
++ {ok, B} ->
++ if BufSize+size(B) < N ->
++ std_reply(cat(Buf, B, ReadMode), State);
++ true ->
++ {B1,B2} = split_binary(B, NeedSize),
++ {reply,cat(Buf, B1, ReadMode),State#state{buf=B2}}
++ end;
++ eof when BufSize==0 ->
++ {reply,eof,State};
++ eof ->
++ std_reply(cast(Buf, ReadMode), State);
++ {error,Reason}=Error ->
++ {stop,Reason,Error,State#state{buf= <<>>}}
++ end;
++get_chars(_N, #state{}=State) ->
++ {error,{error,get_chars},State}.
++
++get_chars(Mod, Func, XtraArg, #state{buf= <<>>}=State) ->
++ get_chars_empty(Mod, Func, XtraArg, start, State);
++get_chars(Mod, Func, XtraArg, #state{buf=Buf}=State) ->
++ get_chars_apply(Mod, Func, XtraArg, start, State#state{buf= <<>>}, Buf).
++
++get_chars_empty(Mod, Func, XtraArg, S,
++ #state{handle=Handle,read_mode=ReadMode}=State) ->
++ case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
++ {ok,Bin} ->
++ get_chars_apply(Mod, Func, XtraArg, S, State, Bin);
++ eof ->
++ get_chars_apply(Mod, Func, XtraArg, S, State, eof);
++ {error,Reason}=Error ->
++ {stop,Reason,Error,State}
++ end.
++
++get_chars_apply(Mod, Func, XtraArg, S0,
++ #state{read_mode=ReadMode}=State, Data0) ->
++ Data1 = case ReadMode of
++ list when binary(Data0) -> binary_to_list(Data0);
++ _ -> Data0
++ end,
++ case catch Mod:Func(S0, Data1, XtraArg) of
++ {stop,Result,Buf} ->
++ {reply,Result,State#state{buf=cast_binary(Buf)}};
++ {'EXIT',Reason} ->
++ {stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
++ S1 ->
++ get_chars_empty(Mod, Func, XtraArg, S1, State)
++ end.
++
++%% Convert error code to make it look as before
++err_func(io_lib, get_until, {_,F,_}) ->
++ F;
++err_func(_, F, _) ->
++ F.
++
++
++
++%% Process the I/O request setopts
++%%
++%% setopts
++setopts(Opts0, State) ->
++ Opts = proplists:substitute_negations([{list,binary}], Opts0),
++ case proplists:get_value(binary, Opts) of
++ true ->
++ {ok,ok,State#state{read_mode=binary}};
++ false ->
++ {ok,ok,State#state{read_mode=list}};
++ _ ->
++ {error,{error,badarg},State}
++ end.
++
++
++
++%% Concatenate two binaries and convert the result to list or binary
++cat(B1, B2, binary) ->
++ list_to_binary([B1,B2]);
++cat(B1, B2, list) ->
++ binary_to_list(B1)++binary_to_list(B2).
++
++%% Cast binary to list or binary
++cast(B, binary) ->
++ B;
++cast(B, list) ->
++ binary_to_list(B).
++
++%% Convert buffer to binary
++cast_binary(Binary) when binary(Binary) ->
++ Binary;
++cast_binary(List) when list(List) ->
++ list_to_binary(List);
++cast_binary(_EOF) ->
++ <<>>.
++
++%% Read size for different read modes
++read_size(binary) ->
++ ?READ_SIZE_BINARY;
++read_size(list) ->
++ ?READ_SIZE_LIST.
++
++max(A, B) when A >= B ->
++ A;
++max(_, B) ->
++ B.
++
++%%%-----------------------------------------------------------------
++%%% ?PRIM_FILE helpers
++
++%% Compensates ?PRIM_FILE:position/2 for the number of bytes
++%% we have buffered
++
++position(Handle, cur, Buf) ->
++ position(Handle, {cur, 0}, Buf);
++position(Handle, {cur, Offs}, Buf) when list(Buf) ->
++ ?PRIM_FILE:position(Handle, {cur, Offs-length(Buf)});
++position(Handle, {cur, Offs}, Buf) when binary(Buf) ->
++ ?PRIM_FILE:position(Handle, {cur, Offs-size(Buf)});
++position(Handle, At, _Buf) ->
++ ?PRIM_FILE:position(Handle, At).
++
--k+w/mQv8wyuph6w0--
More information about the freebsd-ports-bugs
mailing list