Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Jul 2009 12:40:06 GMT
From:      Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs@spoerlein.net>
To:        freebsd-ports-bugs@FreeBSD.org
Subject:   Re: ports/135593: net-im/ejabberd: ram_file_io_server makes ejabberdevel to not run with R13B_logl
Message-ID:  <200907221240.n6MCe6HZ086517@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR ports/135593; it has been noted by GNATS.

From: Ulrich =?utf-8?B?U3DDtnJsZWlu?= <uqs@spoerlein.net>
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},<<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--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200907221240.n6MCe6HZ086517>