From owner-freebsd-ports-bugs@FreeBSD.ORG Wed Jul 22 12:40:06 2009 Return-Path: Delivered-To: freebsd-ports-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 77A6A106566C for ; Wed, 22 Jul 2009 12:40:06 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 62A3F8FC0A for ; Wed, 22 Jul 2009 12:40:06 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id n6MCe6lr086518 for ; Wed, 22 Jul 2009 12:40:06 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id n6MCe6HZ086517; Wed, 22 Jul 2009 12:40:06 GMT (envelope-from gnats) Date: Wed, 22 Jul 2009 12:40:06 GMT Message-Id: <200907221240.n6MCe6HZ086517@freefall.freebsd.org> To: freebsd-ports-bugs@FreeBSD.org From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= Cc: Subject: Re: ports/135593: net-im/ejabberd: ram_file_io_server makes ejabberdevel to not run with R13B_logl X-BeenThere: freebsd-ports-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Ulrich =?utf-8?B?U3DDtnJsZWlu?= List-Id: Ports bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Jul 2009 12:40:06 -0000 The following reply was made to PR ports/135593; it has been noted by GNATS. From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= To: bug-followup@FreeBSD.org, admin@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: ++ -@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},<>) 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},<>) 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--