View | Details | Raw Unified | Return to bug 135593
Collapse All | Expand All

(-)Makefile (+1 lines)
Lines 7-12 Link Here
7
7
8
PORTNAME=	ejabberd
8
PORTNAME=	ejabberd
9
PORTVERSION=	2.0.5
9
PORTVERSION=	2.0.5
10
PORTREVISION=	1
10
CATEGORIES=	net-im
11
CATEGORIES=	net-im
11
MASTER_SITES=	http://www.process-one.net/downloads/ejabberd/${PORTVERSION}/
12
MASTER_SITES=	http://www.process-one.net/downloads/ejabberd/${PORTVERSION}/
12
13
(-)files/patch-919-fileioserver (+1219 lines)
Added Link Here
1
--- Makefile.in
2
+++ Makefile.in
3
@@ -131,6 +131,14 @@ $(BEAMS): $(ERLBEHAVBEAMS)
4
 
5
 all-recursive: $(ERLBEHAVBEAMS)
6
 
7
+# Workaround for R11, that is not capable of compiling the new file:
8
+ram_file_io_server.beam:
9
+	-@ERLC@ -W $(EFLAGS) ram_file_io_server.erl
10
+ram_file_io_server_old.beam:
11
+	@ERLC@ -W $(EFLAGS) ram_file_io_server_old.erl
12
+	[ -f ram_file_io_server.beam ] \
13
+		|| cp ram_file_io_server_old.beam ram_file_io_server.beam
14
+
15
 %.beam:       %.erl
16
 	@ERLC@ -W $(EFLAGS) $<
17
 
18
--- ejabberd_loglevel.erl
19
+++ ejabberd_loglevel.erl
20
@@ -68,7 +68,8 @@ compile_string(Mod, Str) ->
21
     end.
22
    
23
 open_ram_file(Fname) ->
24
-    ram_file_io_server:start(self(), Fname, [read,write]).
25
+    RamModule = get_ram_module(),
26
+    RamModule:start(self(), Fname, [read,write]).
27
 
28
 close_ram_file(Fd) ->
29
     file:close(Fd).
30
@@ -85,6 +86,14 @@ load_logger(Forms, Mod, Loglevel) ->
31
             ?CRITICAL_MSG("Error ~p~n", [Error])
32
     end.
33
 
34
+%% Workaround for R11 and R12, that don't support the new module
35
+get_ram_module() ->
36
+    [AS, BS, _] = string:tokens(erlang:system_info(version), "."),
37
+    case (list_to_integer(AS) >= 5) and (list_to_integer(BS) >= 7) of
38
+	true -> ram_file_io_server;
39
+	false -> ram_file_io_server_old
40
+    end.
41
+
42
 %% --------------------------------------------------------------
43
 %% Code of the ejabberd logger, dynamically compiled and loaded
44
 %% This allows to dynamically change log level while keeping a
45
--- ram_file_io_server.erl
46
+++ ram_file_io_server.erl
47
@@ -25,7 +26,9 @@
48
 -export([format_error/1]).
49
 -export([start/3, start_link/3]).
50
 
51
--record(state, {handle,owner,mref,buf,read_mode}).
52
+-export([count_and_find/3]).
53
+
54
+-record(state, {handle,owner,mref,buf,read_mode,unic}).
55
 
56
 -define(PRIM_FILE, ram_file).
57
 -define(READ_SIZE_LIST, 128).
58
@@ -44,11 +47,11 @@ format_error(ErrorId) ->
59
     erl_posix_msg:message(ErrorId).
60
 
61
 start(Owner, FileName, ModeList) 
62
-  when pid(Owner), list(FileName), list(ModeList) ->
63
+  when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
64
     do_start(spawn, Owner, FileName, ModeList).
65
 
66
 start_link(Owner, FileName, ModeList) 
67
-  when pid(Owner), list(FileName), list(ModeList) ->
68
+  when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
69
     do_start(spawn_link, Owner, FileName, ModeList).
70
 
71
 %%%-----------------------------------------------------------------
72
@@ -61,27 +64,27 @@ do_start(Spawn, Owner, FileName, ModeList) ->
73
 	erlang:Spawn(
74
 	  fun() ->
75
 		  %% process_flag(trap_exit, true),
76
-		  {ReadMode,Opts} = 
77
-		      case lists:member(binary, ModeList) of
78
-			  true ->
79
-			      {binary,ModeList};
80
-			  false ->
81
-			      {list,[binary|ModeList]}
82
-		      end,
83
-		  case ?PRIM_FILE:open(FileName, Opts) of
84
-		      {error, Reason} = Error ->
85
-			  Self ! {Ref, Error},
86
-			  exit(Reason);
87
-		      {ok, Handle} ->
88
-			  %% XXX must I handle R6 nodes here?
89
-			  M = erlang:monitor(process, Owner),
90
-			  Self ! {Ref, ok},
91
-			  server_loop(
92
-			    #state{handle    = Handle,
93
-				   owner     = Owner, 
94
-				   mref      = M, 
95
-				   buf       = <<>>,
96
-				   read_mode = ReadMode})
97
+		  case parse_options(ModeList) of
98
+		      {ReadMode, UnicodeMode, Opts} ->
99
+			  case ?PRIM_FILE:open(FileName, Opts) of
100
+			      {error, Reason} = Error ->
101
+				  Self ! {Ref, Error},
102
+				  exit(Reason);
103
+			      {ok, Handle} ->
104
+				  %% XXX must I handle R6 nodes here?
105
+				  M = erlang:monitor(process, Owner),
106
+				  Self ! {Ref, ok},
107
+				  server_loop(
108
+				    #state{handle    = Handle,
109
+					   owner     = Owner, 
110
+					   mref      = M, 
111
+					   buf       = <<>>,
112
+					   read_mode = ReadMode,
113
+					   unic = UnicodeMode})
114
+			  end;
115
+		      {error,Reason1} = Error1 ->
116
+			  Self ! {Ref, Error1},
117
+			  exit(Reason1)
118
 		  end
119
 	  end),
120
     Mref = erlang:monitor(process, Pid),
121
@@ -102,9 +105,61 @@ do_start(Spawn, Owner, FileName, ModeList) ->
122
 	    {error, Reason}
123
     end.
124
 
125
+%%% Returns {ReadMode, UnicodeMode, RealOpts}
126
+parse_options(List) ->
127
+    parse_options(expand_encoding(List), list, latin1, []).
128
+
129
+parse_options([],list,Uni,Acc) ->
130
+    {list,Uni,[binary|lists:reverse(Acc)]};
131
+parse_options([],binary,Uni,Acc) ->
132
+    {binary,Uni,lists:reverse(Acc)};
133
+parse_options([{encoding, Encoding}|T],RMode,_,Acc) ->
134
+    case valid_enc(Encoding) of 
135
+	{ok, ExpandedEnc} ->
136
+	    parse_options(T,RMode,ExpandedEnc,Acc);
137
+	{error,Reason} ->
138
+	    {error,Reason}
139
+    end;
140
+parse_options([binary|T],_,Uni,Acc) ->
141
+    parse_options(T,binary,Uni,[binary|Acc]);
142
+parse_options([H|T],R,U,Acc) ->
143
+    parse_options(T,R,U,[H|Acc]).
144
+
145
+expand_encoding([]) ->
146
+    [];
147
+expand_encoding([latin1 | T]) ->
148
+    [{encoding,latin1} | expand_encoding(T)];
149
+expand_encoding([unicode | T]) ->
150
+    [{encoding,unicode} | expand_encoding(T)];
151
+expand_encoding([H|T]) ->
152
+    [H|expand_encoding(T)].
153
+
154
+valid_enc(latin1) ->
155
+    {ok,latin1};
156
+valid_enc(utf8) ->
157
+    {ok,unicode};
158
+valid_enc(unicode) ->
159
+    {ok,unicode};
160
+valid_enc(utf16) ->
161
+    {ok,{utf16,big}};
162
+valid_enc({utf16,big}) ->
163
+    {ok,{utf16,big}};
164
+valid_enc({utf16,little}) ->
165
+    {ok,{utf16,little}};
166
+valid_enc(utf32) ->
167
+    {ok,{utf32,big}};
168
+valid_enc({utf32,big}) ->
169
+    {ok,{utf32,big}};
170
+valid_enc({utf32,little}) ->
171
+    {ok,{utf32,little}};
172
+valid_enc(_Other) ->
173
+    {error,badarg}.
174
+
175
+
176
+
177
 server_loop(#state{mref = Mref} = State) ->
178
     receive
179
-	{file_request, From, ReplyAs, Request} when pid(From) ->
180
+	{file_request, From, ReplyAs, Request} when is_pid(From) ->
181
 	    case file_request(Request, State) of
182
 		{reply, Reply, NewState} ->
183
 		    file_reply(From, ReplyAs, Reply),
184
@@ -118,7 +173,7 @@ server_loop(#state{mref = Mref} = State) ->
185
 		    file_reply(From, ReplyAs, Reply),
186
 		    exit(Reason)
187
 	    end;
188
-	{io_request, From, ReplyAs, Request} when pid(From) ->
189
+	{io_request, From, ReplyAs, Request} when is_pid(From) ->
190
 	    case io_request(Request, State) of
191
 		{reply, Reply, NewState} ->
192
 		    io_reply(From, ReplyAs, Reply),
193
@@ -152,7 +207,7 @@ file_request({pread,At,Sz},
194
     case position(Handle, At, Buf) of
195
 	{ok,_Offs} ->
196
 	    case ?PRIM_FILE:read(Handle, Sz) of
197
-		{ok,Bin} when ReadMode==list ->
198
+		{ok,Bin} when ReadMode =:= list ->
199
 		    std_reply({ok,binary_to_list(Bin)}, State);
200
 		Reply ->
201
 		    std_reply(Reply, State)
202
@@ -203,42 +258,61 @@ std_reply(Reply, State) ->
203
 %%%-----------------------------------------------------------------
204
 %%% I/O request 
205
 
206
-io_request({put_chars,Chars}, % binary(Chars) new in R9C
207
+%% New protocol with encoding tags (R13)
208
+io_request({put_chars, Enc, Chars}, 
209
 	   #state{buf= <<>>}=State) ->
210
-    put_chars(Chars, State);
211
-io_request({put_chars,Chars}, % binary(Chars) new in R9C
212
+    put_chars(Chars, Enc, State);
213
+io_request({put_chars, Enc, Chars}, 
214
 	   #state{handle=Handle,buf=Buf}=State) ->
215
     case position(Handle, cur, Buf) of
216
 	{error,_}=Reply ->
217
 	    {stop,normal,Reply,State#state{buf= <<>>}};
218
 	_ ->
219
-	    put_chars(Chars, State#state{buf= <<>>})
220
+	    put_chars(Chars, Enc, State#state{buf= <<>>})
221
     end;
222
-io_request({put_chars,Mod,Func,Args}, 
223
+io_request({put_chars,Enc,Mod,Func,Args}, 
224
 	   #state{}=State) ->
225
     case catch apply(Mod, Func, Args) of
226
-	Chars when list(Chars); binary(Chars) ->
227
-	    io_request({put_chars,Chars}, State);
228
+	Chars when is_list(Chars); is_binary(Chars) ->
229
+	    io_request({put_chars,Enc,Chars}, State);
230
 	_ ->
231
 	    {error,{error,Func},State}
232
     end;
233
-io_request({get_until,_Prompt,Mod,Func,XtraArgs}, 
234
-	   #state{}=State) ->
235
-    get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, State);
236
-io_request({get_chars,_Prompt,N}, % New in R9C
237
+
238
+
239
+io_request({get_until,Enc,_Prompt,Mod,Func,XtraArgs}, 
240
 	   #state{}=State) ->
241
-    get_chars(N, State);
242
-io_request({get_chars,_Prompt,Mod,Func,XtraArg}, % New in R9C
243
+    get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, Enc, State);
244
+io_request({get_chars,Enc,_Prompt,N}, 
245
 	   #state{}=State) ->
246
-    get_chars(Mod, Func, XtraArg, State);
247
-io_request({get_line,_Prompt}, % New in R9C
248
+    get_chars(N, Enc, State);
249
+io_request({get_line,Enc,_Prompt}, 
250
 	   #state{}=State) ->
251
-    get_chars(io_lib, collect_line, [], State);
252
-io_request({setopts, Opts}, % New in R9C
253
-	   #state{}=State) when list(Opts) ->
254
+    get_chars(io_lib, collect_line, [], Enc, State);
255
+
256
+
257
+io_request({setopts, Opts}, 
258
+	   #state{}=State) when is_list(Opts) ->
259
     setopts(Opts, State);
260
+
261
+io_request(getopts, 
262
+	   #state{}=State) ->
263
+    getopts(State);
264
+
265
+%% BC with pre-R13 nodes
266
+io_request({put_chars, Chars},#state{}=State) ->
267
+    io_request({put_chars, latin1, Chars},State);
268
+io_request({put_chars,Mod,Func,Args}, #state{}=State) ->
269
+    io_request({put_chars,latin1,Mod,Func,Args}, State);
270
+io_request({get_until,_Prompt,Mod,Func,XtraArgs}, #state{}=State) ->
271
+    io_request({get_until,latin1,_Prompt,Mod,Func,XtraArgs}, State);
272
+io_request({get_chars,_Prompt,N}, #state{}=State) ->
273
+    io_request({get_chars,latin1,_Prompt,N}, State);
274
+io_request({get_line,_Prompt}, #state{}=State) ->
275
+    io_request({get_line,latin1,_Prompt}, State);
276
+
277
 io_request({requests,Requests}, 
278
-	   #state{}=State) when list(Requests) ->
279
+	   #state{}=State) when is_list(Requests) ->
280
     io_request_loop(Requests, {reply,ok,State});
281
 io_request(Unknown, 
282
 	   #state{}=State) ->
283
@@ -265,76 +339,213 @@ io_request_loop([Request|Tail],
284
 
285
 %% I/O request put_chars
286
 %%
287
-put_chars(Chars, #state{handle=Handle}=State) ->
288
+put_chars(Chars, latin1, #state{handle=Handle, unic=latin1}=State) ->
289
     case ?PRIM_FILE:write(Handle, Chars) of
290
 	{error,_}=Reply ->
291
 	    {stop,normal,Reply,State};
292
 	Reply ->
293
 	    {reply,Reply,State}
294
+    end;
295
+put_chars(Chars, InEncoding, #state{handle=Handle, unic=OutEncoding}=State) ->
296
+    case unicode:characters_to_binary(Chars,InEncoding,OutEncoding) of
297
+	Bin when is_binary(Bin) ->
298
+	    case ?PRIM_FILE:write(Handle, Bin) of
299
+		{error,_}=Reply ->
300
+		    {stop,normal,Reply,State};
301
+		Reply ->
302
+		    {reply,Reply,State}
303
+	    end;
304
+	{error,_,_} ->
305
+	    {stop,normal,{error,{no_translation, InEncoding, OutEncoding}},State}
306
     end.
307
 
308
 
309
 %% Process the I/O request get_chars
310
 %%
311
-get_chars(0, #state{read_mode=ReadMode}=State) ->
312
-    {reply,cast(<<>>, ReadMode),State};
313
-get_chars(N, #state{buf=Buf,read_mode=ReadMode}=State) 
314
-  when integer(N), N > 0, N =< size(Buf) ->
315
+get_chars(0, Enc, #state{read_mode=ReadMode,unic=InEncoding}=State) ->
316
+    {reply,cast(<<>>, ReadMode,InEncoding, Enc),State};
317
+get_chars(N, Enc, #state{buf=Buf,read_mode=ReadMode,unic=latin1}=State) 
318
+  when is_integer(N), N > 0, N =< byte_size(Buf) ->
319
+    {B1,B2} = split_binary(Buf, N),
320
+    {reply,cast(B1, ReadMode,latin1,Enc),State#state{buf=B2}};
321
+get_chars(N, Enc, #state{buf=Buf,read_mode=ReadMode,unic=latin1}=State) 
322
+  when is_integer(N), N > 0, N =< byte_size(Buf) ->
323
     {B1,B2} = split_binary(Buf, N),
324
-    {reply,cast(B1, ReadMode),State#state{buf=B2}};
325
-get_chars(N, #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) 
326
-  when integer(N), N > 0 ->
327
-    BufSize = size(Buf),
328
+    {reply,cast(B1, ReadMode,latin1,Enc),State#state{buf=B2}};
329
+get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=latin1}=State) 
330
+  when is_integer(N), N > 0 ->
331
+    BufSize = byte_size(Buf),
332
     NeedSize = N-BufSize,
333
-    Size = max(NeedSize, ?READ_SIZE_BINARY),
334
+    Size = erlang:max(NeedSize, ?READ_SIZE_BINARY),
335
     case ?PRIM_FILE:read(Handle, Size) of
336
 	{ok, B} ->
337
-	    if BufSize+size(B) < N ->
338
-		    std_reply(cat(Buf, B, ReadMode), State);
339
+	    if BufSize+byte_size(B) < N ->
340
+		    std_reply(cat(Buf, B, ReadMode,latin1,OutEnc), State);
341
 	       true ->
342
 		    {B1,B2} = split_binary(B, NeedSize),
343
-		    {reply,cat(Buf, B1, ReadMode),State#state{buf=B2}}
344
+		    {reply,cat(Buf, B1, ReadMode, latin1,OutEnc),State#state{buf=B2}}
345
 	    end;
346
-	eof when BufSize==0 ->
347
+	eof when BufSize =:= 0 ->
348
 	    {reply,eof,State};
349
 	eof ->
350
-	    std_reply(cast(Buf, ReadMode), State);
351
+	    std_reply(cast(Buf, ReadMode,latin1,OutEnc), State);
352
 	{error,Reason}=Error ->
353
 	    {stop,Reason,Error,State#state{buf= <<>>}}
354
     end;
355
-get_chars(_N, #state{}=State) ->
356
+get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=InEncoding}=State) 
357
+  when is_integer(N), N > 0 ->
358
+    try
359
+	%% This is rather tricky, we need to count the actual number of characters 
360
+	%% in the buffer first as unicode characters are not constant in length
361
+	{BufCount, SplitPos} = count_and_find(Buf,N,InEncoding),
362
+	case BufCount >= N of
363
+	    true ->
364
+		{B1,B2} = case SplitPos of
365
+			      none -> {Buf,<<>>};
366
+			      _ ->split_binary(Buf,SplitPos)
367
+			  end,
368
+		{reply,cast(B1, ReadMode,InEncoding,OutEnc),State#state{buf=B2}};
369
+	    false ->
370
+		%% Need more, Try to read 4*needed in bytes...
371
+		NeedSize = (N - BufCount) * 4,
372
+		Size = erlang:max(NeedSize, ?READ_SIZE_BINARY),
373
+		case ?PRIM_FILE:read(Handle, Size) of
374
+		    {ok, B} ->
375
+			NewBuf = list_to_binary([Buf,B]),
376
+			{NewCount,NewSplit} = count_and_find(NewBuf,N,InEncoding),
377
+			case NewCount >= N of
378
+			    true ->
379
+				{B01,B02} = case NewSplit of
380
+						none -> {NewBuf,<<>>};
381
+						_ ->split_binary(NewBuf, NewSplit)
382
+					    end,
383
+				{reply,cast(B01, ReadMode,InEncoding,OutEnc),
384
+				 State#state{buf=B02}};
385
+			    false ->
386
+				%% Reached end of file
387
+				std_reply(cast(NewBuf, ReadMode,InEncoding,OutEnc), 
388
+					  State#state{buf = <<>>})
389
+			end;
390
+		    eof when BufCount =:= 0 ->
391
+			{reply,eof,State};
392
+		    eof ->
393
+			std_reply(cast(Buf, ReadMode,InEncoding,OutEnc), State#state{buf = <<>>});
394
+		    {error,Reason}=Error ->
395
+			{stop,Reason,Error,State#state{buf = <<>>}}
396
+		end
397
+	end
398
+    catch
399
+	exit:ExError ->
400
+	    {stop,ExError,{error,ExError},State#state{buf= <<>>}}
401
+    end;
402
+
403
+get_chars(_N, _, #state{}=State) ->
404
     {error,{error,get_chars},State}.
405
 
406
-get_chars(Mod, Func, XtraArg, #state{buf= <<>>}=State) ->
407
-    get_chars_empty(Mod, Func, XtraArg, start, State);
408
-get_chars(Mod, Func, XtraArg, #state{buf=Buf}=State) ->
409
-    get_chars_apply(Mod, Func, XtraArg, start, State#state{buf= <<>>}, Buf).
410
+get_chars(Mod, Func, XtraArg, OutEnc, #state{buf= <<>>}=State) ->
411
+    get_chars_empty(Mod, Func, XtraArg, start, OutEnc, State);
412
+get_chars(Mod, Func, XtraArg, OutEnc, #state{buf=Buf}=State) ->
413
+    get_chars_apply(Mod, Func, XtraArg, start, OutEnc, State#state{buf= <<>>}, Buf).
414
 
415
-get_chars_empty(Mod, Func, XtraArg, S, 
416
+get_chars_empty(Mod, Func, XtraArg, S, latin1,
417
+		#state{handle=Handle,read_mode=ReadMode, unic=latin1}=State) ->
418
+    case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
419
+	{ok,Bin} ->
420
+	    get_chars_apply(Mod, Func, XtraArg, S, latin1, State, Bin);
421
+	eof ->
422
+	    get_chars_apply(Mod, Func, XtraArg, S, latin1, State, eof);
423
+	{error,Reason}=Error ->
424
+	    {stop,Reason,Error,State}
425
+    end;
426
+get_chars_empty(Mod, Func, XtraArg, S, OutEnc,
427
 		#state{handle=Handle,read_mode=ReadMode}=State) ->
428
     case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
429
 	{ok,Bin} ->
430
-	    get_chars_apply(Mod, Func, XtraArg, S, State, Bin);
431
+	    get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, Bin);
432
+	eof ->
433
+	    get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof);
434
+	{error,Reason}=Error ->
435
+	    {stop,Reason,Error,State}
436
+    end.
437
+get_chars_notempty(Mod, Func, XtraArg, S, OutEnc,
438
+		   #state{handle=Handle,read_mode=ReadMode,buf = B}=State) ->
439
+    case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
440
+	{ok,Bin} ->
441
+	    get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, list_to_binary([Bin,B]));
442
 	eof ->
443
-	    get_chars_apply(Mod, Func, XtraArg, S, State, eof);
444
+	    case B of
445
+		<<>> ->
446
+		    get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof);
447
+		_ ->
448
+		    {stop,invalid_unicode,{error,invalid_unicode},State}
449
+	    end;
450
 	{error,Reason}=Error ->
451
 	    {stop,Reason,Error,State}
452
     end.
453
 
454
-get_chars_apply(Mod, Func, XtraArg, S0, 
455
-		#state{read_mode=ReadMode}=State, Data0) ->
456
+
457
+get_chars_apply(Mod, Func, XtraArg, S0, latin1,
458
+		#state{read_mode=ReadMode,unic=latin1}=State, Data0) ->
459
     Data1 = case ReadMode of
460
-	       list when binary(Data0) -> binary_to_list(Data0);
461
+	       list when is_binary(Data0) -> binary_to_list(Data0);
462
 	       _ -> Data0
463
 	    end,
464
-    case catch Mod:Func(S0, Data1, XtraArg) of
465
+    case catch Mod:Func(S0, Data1, latin1, XtraArg) of
466
 	{stop,Result,Buf} ->
467
 	    {reply,Result,State#state{buf=cast_binary(Buf)}};
468
 	{'EXIT',Reason} ->
469
 	    {stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
470
 	S1 ->
471
-	    get_chars_empty(Mod, Func, XtraArg, S1, State)
472
+	    get_chars_empty(Mod, Func, XtraArg, S1, latin1, State)
473
+    end;
474
+get_chars_apply(Mod, Func, XtraArg, S0, OutEnc,
475
+		#state{read_mode=ReadMode,unic=InEnc}=State, Data0) ->
476
+    try 
477
+	{Data1,NewBuff} = case ReadMode of
478
+			      list when is_binary(Data0) -> 
479
+				  case unicode:characters_to_list(Data0,InEnc) of
480
+				      {Tag,Decoded,Rest} when Decoded =/= [], Tag =:= error; Decoded =/= [], Tag =:= incomplete ->
481
+					  {Decoded,erlang:iolist_to_binary(Rest)};
482
+				      {Tag, [], _} when Tag =:= error; Tag =:= incomplete -> 
483
+					  exit(invalid_unicode);
484
+				      List when is_list(List) ->
485
+					  {List,<<>>}
486
+				  end;
487
+			      binary when is_binary(Data0) ->
488
+				  case unicode:characters_to_binary(Data0,InEnc,OutEnc) of
489
+				      {Tag2,Decoded2,Rest2} when Decoded2 =/= <<>>, Tag2 =:= error; Decoded2 =/= <<>>, Tag2 =:= incomplete ->
490
+					  {Decoded2,erlang:iolist_to_binary(Rest2)};
491
+				      {Tag2, <<>>, _} when Tag2 =:= error; Tag2 =:= incomplete ->
492
+					  exit(invalid_unicode);
493
+				      Binary when is_binary(Binary) ->
494
+					  {Binary,<<>>}
495
+				  end;
496
+			      _ -> %i.e. eof
497
+				  {Data0,<<>>}
498
+			  end,
499
+	case catch Mod:Func(S0, Data1, OutEnc, XtraArg) of
500
+	    {stop,Result,Buf} ->
501
+		{reply,Result,State#state{buf = (if
502
+						     is_binary(Buf) ->
503
+							 unicode:characters_to_binary(Buf,OutEnc,InEnc);
504
+						     is_list(Buf) ->
505
+							 unicode:characters_to_binary(Buf,unicode,InEnc);
506
+						     true ->
507
+							 <<>>
508
+						end)}};
509
+	    {'EXIT',Reason} ->
510
+		{stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
511
+	    S1 ->
512
+		get_chars_notempty(Mod, Func, XtraArg, S1, OutEnc, State#state{buf=NewBuff})
513
+	end
514
+    catch
515
+	exit:ExReason ->
516
+	   {stop,ExReason,{error,err_func(Mod, Func, XtraArg)},State};
517
+	error:ErrReason ->
518
+	   {stop,ErrReason,{error,err_func(Mod, Func, XtraArg)},State}
519
     end.
520
+	    
521
+
522
 
523
 %% Convert error code to make it look as before
524
 err_func(io_lib, get_until, {_,F,_}) ->
525
@@ -347,35 +558,100 @@ err_func(_, F, _) ->
526
 %% Process the I/O request setopts
527
 %%
528
 %% setopts
529
-setopts(Opts0, State) ->
530
-    Opts = proplists:substitute_negations([{list,binary}], Opts0),
531
-    case proplists:get_value(binary, Opts) of
532
+setopts(Opts0,State) ->
533
+    Opts = proplists:unfold(
534
+	     proplists:substitute_negations(
535
+	       [{list,binary}], 
536
+	       expand_encoding(Opts0))),
537
+    case check_valid_opts(Opts) of
538
 	true ->
539
-	    {ok,ok,State#state{read_mode=binary}};
540
+	    do_setopts(Opts,State);
541
 	false ->
542
-	    {ok,ok,State#state{read_mode=list}};
543
+	    {error,{error,enotsup},State}
544
+    end.
545
+check_valid_opts([]) ->
546
+    true;
547
+check_valid_opts([{binary,_}|T]) ->
548
+    check_valid_opts(T);
549
+check_valid_opts([{encoding,_Enc}|T]) ->
550
+    check_valid_opts(T);
551
+check_valid_opts(_) ->
552
+    false.
553
+do_setopts(Opts, State) ->
554
+    case valid_enc(proplists:get_value(encoding, Opts, State#state.unic)) of
555
+	{ok,NewUnic} ->
556
+	    case proplists:get_value(binary, Opts) of
557
+		true ->
558
+		    {reply,ok,State#state{read_mode=binary, unic=NewUnic}};
559
+		false ->
560
+		    {reply,ok,State#state{read_mode=list, unic=NewUnic}};
561
+		undefined ->
562
+		    {reply,ok,State#state{unic=NewUnic}}
563
+	    end;
564
 	_ ->
565
-	    {error,{error,badarg},State}
566
+	    {error,{error,badarg},State} 
567
     end.
568
 
569
-
570
+getopts(#state{read_mode=RM, unic=Unic} = State) ->
571
+    Bin = {binary, case RM of
572
+		       binary ->
573
+			   true;
574
+		       _ ->
575
+			   false
576
+		   end},
577
+    Uni = {encoding, Unic},
578
+    {reply,[Bin,Uni],State}.
579
+    
580
 
581
 %% Concatenate two binaries and convert the result to list or binary
582
-cat(B1, B2, binary) ->
583
+cat(B1, B2, binary,latin1,latin1) ->
584
     list_to_binary([B1,B2]);
585
-cat(B1, B2, list) ->
586
+cat(B1, B2, binary,InEncoding,OutEncoding) ->
587
+    case unicode:characters_to_binary([B1,B2],InEncoding,OutEncoding) of
588
+	Good when is_binary(Good) ->
589
+	    Good;
590
+	_ ->
591
+	    exit({no_translation,InEncoding,OutEncoding})
592
+    end;
593
+%% Dialyzer finds this is never used...                                                       
594
+%% cat(B1, B2, list, InEncoding, OutEncoding) when InEncoding =/= latin1 ->
595
+%%     % Catch i.e. unicode -> latin1 errors by using the outencoding although otherwise
596
+%%     % irrelevant for lists...
597
+%%     try
598
+%% 	unicode:characters_to_list(unicode:characters_to_binary([B1,B2],InEncoding,OutEncoding),
599
+%% 				   OutEncoding)
600
+%%     catch
601
+%% 	error:_ ->
602
+%% 	    exit({no_translation,InEncoding,OutEncoding})
603
+%%     end.
604
+cat(B1, B2, list, latin1,_) ->
605
     binary_to_list(B1)++binary_to_list(B2).
606
 
607
 %% Cast binary to list or binary
608
-cast(B, binary) ->
609
+cast(B, binary, latin1, latin1) ->
610
     B;
611
-cast(B, list) ->
612
-    binary_to_list(B).
613
+cast(B, binary, InEncoding, OutEncoding) ->
614
+    case unicode:characters_to_binary(B,InEncoding,OutEncoding) of
615
+	Good when is_binary(Good) ->
616
+	    Good;
617
+	_ ->
618
+	    exit({no_translation,InEncoding,OutEncoding})
619
+    end;
620
+cast(B, list, latin1, _) ->
621
+    binary_to_list(B);
622
+cast(B, list, InEncoding, OutEncoding) ->
623
+    try
624
+	unicode:characters_to_list(unicode:characters_to_binary(B,InEncoding,OutEncoding),
625
+				   OutEncoding)
626
+    catch
627
+	error:_ ->
628
+	    exit({no_translation,InEncoding,OutEncoding})
629
+    end.
630
 
631
 %% Convert buffer to binary
632
-cast_binary(Binary) when binary(Binary) ->
633
+cast_binary(Binary) when is_binary(Binary) ->
634
     Binary;
635
-cast_binary(List) when list(List) ->
636
+cast_binary(List) when is_list(List) ->
637
     list_to_binary(List);
638
 cast_binary(_EOF) ->
639
     <<>>.
640
@@ -386,10 +662,150 @@ read_size(binary) ->
641
 read_size(list) ->
642
     ?READ_SIZE_LIST.
643
 
644
-max(A, B) when A >= B ->
645
-    A;
646
-max(_, B) ->
647
-    B.
648
+%% Utf utility
649
+count_and_find(Bin,N,Encoding) ->
650
+    cafu(Bin,N,0,0,none,case Encoding of 
651
+			   unicode -> utf8;
652
+			   Oth -> Oth
653
+			end).
654
+
655
+cafu(<<>>,0,Count,ByteCount,_SavePos,_) ->
656
+    {Count,ByteCount};
657
+cafu(<<>>,_N,Count,_ByteCount,SavePos,_) ->
658
+    {Count,SavePos};
659
+cafu(<<_/utf8,Rest/binary>>, 0, Count, ByteCount, _SavePos, utf8) ->
660
+    cafu(Rest,-1,Count+1,0,ByteCount,utf8);
661
+cafu(<<_/utf8,Rest/binary>>, N, Count, _ByteCount, SavePos, utf8) when N < 0 ->
662
+    cafu(Rest,-1,Count+1,0,SavePos,utf8);
663
+cafu(<<_/utf8,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, utf8) ->
664
+    Delta = byte_size(Whole) - byte_size(Rest),
665
+    cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,utf8);
666
+cafu(<<_/utf16-big,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf16,big}) ->
667
+    cafu(Rest,-1,Count+1,0,ByteCount,{utf16,big});
668
+cafu(<<_/utf16-big,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf16,big}) when N < 0 ->
669
+    cafu(Rest,-1,Count+1,0,SavePos,{utf16,big});
670
+cafu(<<_/utf16-big,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf16,big}) ->
671
+    Delta = byte_size(Whole) - byte_size(Rest),
672
+    cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf16,big});
673
+cafu(<<_/utf16-little,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf16,little}) ->
674
+    cafu(Rest,-1,Count+1,0,ByteCount,{utf16,little});
675
+cafu(<<_/utf16-little,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf16,little}) when N < 0 ->
676
+    cafu(Rest,-1,Count+1,0,SavePos,{utf16,little});
677
+cafu(<<_/utf16-little,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf16,little}) ->
678
+    Delta = byte_size(Whole) - byte_size(Rest),
679
+    cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf16,little});
680
+cafu(<<_/utf32-big,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf32,big}) ->
681
+    cafu(Rest,-1,Count+1,0,ByteCount,{utf32,big});
682
+cafu(<<_/utf32-big,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf32,big}) when N < 0 ->
683
+    cafu(Rest,-1,Count+1,0,SavePos,{utf32,big});
684
+cafu(<<_/utf32-big,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf32,big}) ->
685
+    Delta = byte_size(Whole) - byte_size(Rest),
686
+    cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf32,big});
687
+cafu(<<_/utf32-little,Rest/binary>>, 0, Count, ByteCount, _SavePos, {utf32,little}) ->
688
+    cafu(Rest,-1,Count+1,0,ByteCount,{utf32,little});
689
+cafu(<<_/utf32-little,Rest/binary>>, N, Count, _ByteCount, SavePos, {utf32,little}) when N < 0 ->
690
+    cafu(Rest,-1,Count+1,0,SavePos,{utf32,little});
691
+cafu(<<_/utf32-little,Rest/binary>> = Whole, N, Count, ByteCount, SavePos, {utf32,little}) ->
692
+    Delta = byte_size(Whole) - byte_size(Rest),
693
+    cafu(Rest,N-1,Count+1,ByteCount+Delta,SavePos,{utf32,little});
694
+cafu(_Other,0,Count,ByteCount,_,_) -> % Non Unicode character, 
695
+                                     % but found our point, OK this time
696
+    {Count,ByteCount};
697
+cafu(Other,_N,Count,0,_SavePos,Enc) -> % Not enough, but valid chomped unicode
698
+                                       % at end.
699
+    case cbv(Enc,Other) of
700
+	false ->
701
+	    exit(invalid_unicode);
702
+	_ ->
703
+	    {Count,none}
704
+    end;
705
+cafu(Other,_N,Count,ByteCount,none,Enc) -> % Return what we'we got this far
706
+					   % although not complete, 
707
+					   % it's not (yet) in error
708
+    case cbv(Enc,Other) of
709
+	false ->
710
+	    exit(invalid_unicode);
711
+	_ ->
712
+	    {Count,ByteCount}
713
+    end;
714
+cafu(Other,_N,Count,_ByteCount,SavePos,Enc) -> % As above but we have 
715
+					       % found a position
716
+    case cbv(Enc,Other) of
717
+	false ->
718
+	    exit(invalid_unicode);
719
+	_ ->
720
+	    {Count,SavePos}
721
+    end.
722
+
723
+%%
724
+%% Bluntly stolen from stdlib/unicode.erl (cbv means can be valid?)
725
+%%
726
+cbv(utf8,<<1:1,1:1,0:1,_:5>>) -> 
727
+    1;
728
+cbv(utf8,<<1:1,1:1,1:1,0:1,_:4,R/binary>>) -> 
729
+    case R of
730
+	<<>> ->
731
+	    2;
732
+	<<1:1,0:1,_:6>> ->
733
+	    1;
734
+	_ ->
735
+	    false
736
+    end;
737
+cbv(utf8,<<1:1,1:1,1:1,1:1,0:1,_:3,R/binary>>) ->
738
+    case R of
739
+	<<>> ->
740
+	    3;
741
+	<<1:1,0:1,_:6>> ->
742
+	    2;
743
+	<<1:1,0:1,_:6,1:1,0:1,_:6>> ->
744
+	    1;
745
+	_ ->
746
+	    false
747
+    end;
748
+cbv(utf8,_) ->
749
+    false;
750
+
751
+cbv({utf16,big},<<A:8>>) when A =< 215; A >= 224 ->
752
+    1;
753
+cbv({utf16,big},<<54:6,_:2>>) ->
754
+    3;
755
+cbv({utf16,big},<<54:6,_:10>>) ->
756
+    2;
757
+cbv({utf16,big},<<54:6,_:10,55:6,_:2>>) ->
758
+    1;
759
+cbv({utf16,big},_) ->
760
+    false;
761
+cbv({utf16,little},<<_:8>>) ->
762
+    1; % or 3, we'll see
763
+cbv({utf16,little},<<_:8,54:6,_:2>>) ->
764
+    2;
765
+cbv({utf16,little},<<_:8,54:6,_:2,_:8>>) ->
766
+    1;
767
+cbv({utf16,little},_) ->
768
+    false;
769
+
770
+
771
+cbv({utf32,big}, <<0:8>>) ->
772
+    3;
773
+cbv({utf32,big}, <<0:8,X:8>>) when X =< 16 ->
774
+    2;
775
+cbv({utf32,big}, <<0:8,X:8,Y:8>>) 
776
+  when X =< 16, ((X > 0) or ((Y =< 215) or (Y >= 224))) ->
777
+    1;
778
+cbv({utf32,big},_) ->
779
+    false;
780
+cbv({utf32,little},<<_:8>>) ->
781
+    3;
782
+cbv({utf32,little},<<_:8,_:8>>) -> 
783
+    2;
784
+cbv({utf32,little},<<X:8,255:8,0:8>>) when X =:= 254; X =:= 255 ->
785
+    false;
786
+cbv({utf32,little},<<_:8,Y:8,X:8>>) 
787
+  when X =< 16, ((X > 0) or ((Y =< 215) or (Y >= 224))) ->
788
+    1;
789
+cbv({utf32,little},_) ->
790
+    false.
791
+
792
 
793
 %%%-----------------------------------------------------------------
794
 %%% ?PRIM_FILE helpers
795
@@ -399,10 +815,10 @@ max(_, B) ->
796
 
797
 position(Handle, cur, Buf) ->
798
     position(Handle, {cur, 0}, Buf);
799
-position(Handle, {cur, Offs}, Buf) when list(Buf) ->
800
+position(Handle, {cur, Offs}, Buf) when is_list(Buf) ->
801
     ?PRIM_FILE:position(Handle, {cur, Offs-length(Buf)});
802
-position(Handle, {cur, Offs}, Buf) when binary(Buf) ->
803
-    ?PRIM_FILE:position(Handle, {cur, Offs-size(Buf)});
804
+position(Handle, {cur, Offs}, Buf) when is_binary(Buf) ->
805
+    ?PRIM_FILE:position(Handle, {cur, Offs-byte_size(Buf)});
806
 position(Handle, At, _Buf) ->
807
     ?PRIM_FILE:position(Handle, At).
808
 
809
--- /dev/null
810
+++ ram_file_io_server_old.erl
811
@@ -0,0 +1,408 @@
812
+%% ``The contents of this file are subject to the Erlang Public License,
813
+%% Version 1.1, (the "License"); you may not use this file except in
814
+%% compliance with the License. You should have received a copy of the
815
+%% Erlang Public License along with this software. If not, it can be
816
+%% retrieved via the world wide web at http://www.erlang.org/.
817
+%% 
818
+%% Software distributed under the License is distributed on an "AS IS"
819
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
820
+%% the License for the specific language governing rights and limitations
821
+%% under the License.
822
+%% 
823
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
824
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
825
+%% AB. All Rights Reserved.''
826
+%% 
827
+%%     $Id$
828
+%% 
829
+%% This file is mostly copied from Erlang file_io_server.erl
830
+%% See: http://www.erlang.org/ml-archive/erlang-questions/200607/msg00080.html
831
+%% for details on ram_file_io_server.erl (Erlang OTP R11B-2)
832
+-module(ram_file_io_server_old).
833
+
834
+%% A simple file server for io to one file instance per server instance.
835
+
836
+-export([format_error/1]).
837
+-export([start/3, start_link/3]).
838
+
839
+-record(state, {handle,owner,mref,buf,read_mode}).
840
+
841
+-define(PRIM_FILE, ram_file).
842
+-define(READ_SIZE_LIST, 128).
843
+-define(READ_SIZE_BINARY, (8*1024)).
844
+
845
+-define(eat_message(M, T), receive M -> M after T -> timeout end).
846
+
847
+%%%-----------------------------------------------------------------
848
+%%% Exported functions
849
+
850
+format_error({_Line, ?MODULE, Reason}) ->
851
+    io_lib:format("~w", [Reason]);
852
+format_error({_Line, Mod, Reason}) ->
853
+    Mod:format_error(Reason);
854
+format_error(ErrorId) ->
855
+    erl_posix_msg:message(ErrorId).
856
+
857
+start(Owner, FileName, ModeList) 
858
+  when pid(Owner), list(FileName), list(ModeList) ->
859
+    do_start(spawn, Owner, FileName, ModeList).
860
+
861
+start_link(Owner, FileName, ModeList) 
862
+  when pid(Owner), list(FileName), list(ModeList) ->
863
+    do_start(spawn_link, Owner, FileName, ModeList).
864
+
865
+%%%-----------------------------------------------------------------
866
+%%% Server starter, dispatcher and helpers
867
+
868
+do_start(Spawn, Owner, FileName, ModeList) ->
869
+    Self = self(),
870
+    Ref = make_ref(),
871
+    Pid = 
872
+	erlang:Spawn(
873
+	  fun() ->
874
+		  %% process_flag(trap_exit, true),
875
+		  {ReadMode,Opts} = 
876
+		      case lists:member(binary, ModeList) of
877
+			  true ->
878
+			      {binary,ModeList};
879
+			  false ->
880
+			      {list,[binary|ModeList]}
881
+		      end,
882
+		  case ?PRIM_FILE:open(FileName, Opts) of
883
+		      {error, Reason} = Error ->
884
+			  Self ! {Ref, Error},
885
+			  exit(Reason);
886
+		      {ok, Handle} ->
887
+			  %% XXX must I handle R6 nodes here?
888
+			  M = erlang:monitor(process, Owner),
889
+			  Self ! {Ref, ok},
890
+			  server_loop(
891
+			    #state{handle    = Handle,
892
+				   owner     = Owner, 
893
+				   mref      = M, 
894
+				   buf       = <<>>,
895
+				   read_mode = ReadMode})
896
+		  end
897
+	  end),
898
+    Mref = erlang:monitor(process, Pid),
899
+    receive
900
+	{Ref, {error, _Reason} = Error} ->
901
+	    erlang:demonitor(Mref),
902
+	    receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end,
903
+	    Error;
904
+	{Ref, ok} ->
905
+	    erlang:demonitor(Mref),
906
+	    receive
907
+		{'DOWN', Mref, _, _, Reason} ->
908
+		    {error, Reason}
909
+	    after 0 ->
910
+		    {ok, Pid}
911
+	    end;
912
+	{'DOWN', Mref, _, _, Reason} ->
913
+	    {error, Reason}
914
+    end.
915
+
916
+server_loop(#state{mref = Mref} = State) ->
917
+    receive
918
+	{file_request, From, ReplyAs, Request} when pid(From) ->
919
+	    case file_request(Request, State) of
920
+		{reply, Reply, NewState} ->
921
+		    file_reply(From, ReplyAs, Reply),
922
+		    server_loop(NewState);
923
+		{error, Reply, NewState} ->
924
+		    %% error is the same as reply, except that
925
+		    %% it breaks the io_request_loop further down
926
+		    file_reply(From, ReplyAs, Reply),
927
+		    server_loop(NewState);
928
+		{stop, Reason, Reply, _NewState} ->
929
+		    file_reply(From, ReplyAs, Reply),
930
+		    exit(Reason)
931
+	    end;
932
+	{io_request, From, ReplyAs, Request} when pid(From) ->
933
+	    case io_request(Request, State) of
934
+		{reply, Reply, NewState} ->
935
+		    io_reply(From, ReplyAs, Reply),
936
+		    server_loop(NewState);
937
+		{error, Reply, NewState} ->
938
+		    %% error is the same as reply, except that
939
+		    %% it breaks the io_request_loop further down
940
+		    io_reply(From, ReplyAs, Reply),
941
+		    server_loop(NewState);
942
+		{stop, Reason, Reply, _NewState} ->
943
+		    io_reply(From, ReplyAs, Reply),
944
+		    exit(Reason)
945
+	    end;
946
+	{'DOWN', Mref, _, _, Reason} ->
947
+	    exit(Reason);
948
+	_ ->
949
+	    server_loop(State)
950
+    end.
951
+
952
+file_reply(From, ReplyAs, Reply) ->
953
+    From ! {file_reply, ReplyAs, Reply}.
954
+
955
+io_reply(From, ReplyAs, Reply) ->
956
+    From ! {io_reply, ReplyAs, Reply}.
957
+
958
+%%%-----------------------------------------------------------------
959
+%%% file requests
960
+
961
+file_request({pread,At,Sz}, 
962
+	     #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) ->
963
+    case position(Handle, At, Buf) of
964
+	{ok,_Offs} ->
965
+	    case ?PRIM_FILE:read(Handle, Sz) of
966
+		{ok,Bin} when ReadMode==list ->
967
+		    std_reply({ok,binary_to_list(Bin)}, State);
968
+		Reply ->
969
+		    std_reply(Reply, State)
970
+	    end;
971
+	Reply ->
972
+	    std_reply(Reply, State)
973
+    end;
974
+file_request({pwrite,At,Data}, 
975
+	     #state{handle=Handle,buf=Buf}=State) ->
976
+    case position(Handle, At, Buf) of
977
+	{ok,_Offs} ->
978
+	    std_reply(?PRIM_FILE:write(Handle, Data), State);
979
+	Reply ->
980
+	    std_reply(Reply, State)
981
+    end;
982
+file_request(sync, 
983
+	     #state{handle=Handle}=State) ->
984
+    case ?PRIM_FILE:sync(Handle) of
985
+	{error,_}=Reply ->
986
+	    {stop,normal,Reply,State};
987
+	Reply ->
988
+	    {reply,Reply,State}
989
+    end;
990
+file_request(close, 
991
+	     #state{handle=Handle}=State) ->
992
+    {stop,normal,?PRIM_FILE:close(Handle),State#state{buf= <<>>}};
993
+file_request({position,At}, 
994
+	     #state{handle=Handle,buf=Buf}=State) ->
995
+    std_reply(position(Handle, At, Buf), State);
996
+file_request(truncate, 
997
+	     #state{handle=Handle}=State) ->
998
+    case ?PRIM_FILE:truncate(Handle) of
999
+	{error,_Reason}=Reply ->
1000
+	    {stop,normal,Reply,State#state{buf= <<>>}};
1001
+	Reply ->
1002
+	    {reply,Reply,State}
1003
+    end;
1004
+file_request(Unknown, 
1005
+	     #state{}=State) ->
1006
+    Reason = {request, Unknown},
1007
+    {error,{error,Reason},State}.
1008
+
1009
+std_reply({error,_}=Reply, State) ->
1010
+    {error,Reply,State#state{buf= <<>>}};
1011
+std_reply(Reply, State) ->
1012
+    {reply,Reply,State#state{buf= <<>>}}.
1013
+
1014
+%%%-----------------------------------------------------------------
1015
+%%% I/O request 
1016
+
1017
+io_request({put_chars,Chars}, % binary(Chars) new in R9C
1018
+	   #state{buf= <<>>}=State) ->
1019
+    put_chars(Chars, State);
1020
+io_request({put_chars,Chars}, % binary(Chars) new in R9C
1021
+	   #state{handle=Handle,buf=Buf}=State) ->
1022
+    case position(Handle, cur, Buf) of
1023
+	{error,_}=Reply ->
1024
+	    {stop,normal,Reply,State#state{buf= <<>>}};
1025
+	_ ->
1026
+	    put_chars(Chars, State#state{buf= <<>>})
1027
+    end;
1028
+io_request({put_chars,Mod,Func,Args}, 
1029
+	   #state{}=State) ->
1030
+    case catch apply(Mod, Func, Args) of
1031
+	Chars when list(Chars); binary(Chars) ->
1032
+	    io_request({put_chars,Chars}, State);
1033
+	_ ->
1034
+	    {error,{error,Func},State}
1035
+    end;
1036
+io_request({get_until,_Prompt,Mod,Func,XtraArgs}, 
1037
+	   #state{}=State) ->
1038
+    get_chars(io_lib, get_until, {Mod, Func, XtraArgs}, State);
1039
+io_request({get_chars,_Prompt,N}, % New in R9C
1040
+	   #state{}=State) ->
1041
+    get_chars(N, State);
1042
+io_request({get_chars,_Prompt,Mod,Func,XtraArg}, % New in R9C
1043
+	   #state{}=State) ->
1044
+    get_chars(Mod, Func, XtraArg, State);
1045
+io_request({get_line,_Prompt}, % New in R9C
1046
+	   #state{}=State) ->
1047
+    get_chars(io_lib, collect_line, [], State);
1048
+io_request({setopts, Opts}, % New in R9C
1049
+	   #state{}=State) when list(Opts) ->
1050
+    setopts(Opts, State);
1051
+io_request({requests,Requests}, 
1052
+	   #state{}=State) when list(Requests) ->
1053
+    io_request_loop(Requests, {reply,ok,State});
1054
+io_request(Unknown, 
1055
+	   #state{}=State) ->
1056
+    Reason = {request,Unknown},
1057
+    {error,{error,Reason},State}.
1058
+
1059
+
1060
+
1061
+%% Process a list of requests as long as the results are ok.
1062
+
1063
+io_request_loop([], Result) ->
1064
+    Result;
1065
+io_request_loop([_Request|_Tail], 
1066
+		{stop,_Reason,_Reply,_State}=Result) ->
1067
+    Result;
1068
+io_request_loop([_Request|_Tail],
1069
+		{error,_Reply,_State}=Result) ->
1070
+    Result;
1071
+io_request_loop([Request|Tail], 
1072
+		{reply,_Reply,State}) ->
1073
+    io_request_loop(Tail, io_request(Request, State)).
1074
+
1075
+
1076
+
1077
+%% I/O request put_chars
1078
+%%
1079
+put_chars(Chars, #state{handle=Handle}=State) ->
1080
+    case ?PRIM_FILE:write(Handle, Chars) of
1081
+	{error,_}=Reply ->
1082
+	    {stop,normal,Reply,State};
1083
+	Reply ->
1084
+	    {reply,Reply,State}
1085
+    end.
1086
+
1087
+
1088
+%% Process the I/O request get_chars
1089
+%%
1090
+get_chars(0, #state{read_mode=ReadMode}=State) ->
1091
+    {reply,cast(<<>>, ReadMode),State};
1092
+get_chars(N, #state{buf=Buf,read_mode=ReadMode}=State) 
1093
+  when integer(N), N > 0, N =< size(Buf) ->
1094
+    {B1,B2} = split_binary(Buf, N),
1095
+    {reply,cast(B1, ReadMode),State#state{buf=B2}};
1096
+get_chars(N, #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) 
1097
+  when integer(N), N > 0 ->
1098
+    BufSize = size(Buf),
1099
+    NeedSize = N-BufSize,
1100
+    Size = max(NeedSize, ?READ_SIZE_BINARY),
1101
+    case ?PRIM_FILE:read(Handle, Size) of
1102
+	{ok, B} ->
1103
+	    if BufSize+size(B) < N ->
1104
+		    std_reply(cat(Buf, B, ReadMode), State);
1105
+	       true ->
1106
+		    {B1,B2} = split_binary(B, NeedSize),
1107
+		    {reply,cat(Buf, B1, ReadMode),State#state{buf=B2}}
1108
+	    end;
1109
+	eof when BufSize==0 ->
1110
+	    {reply,eof,State};
1111
+	eof ->
1112
+	    std_reply(cast(Buf, ReadMode), State);
1113
+	{error,Reason}=Error ->
1114
+	    {stop,Reason,Error,State#state{buf= <<>>}}
1115
+    end;
1116
+get_chars(_N, #state{}=State) ->
1117
+    {error,{error,get_chars},State}.
1118
+
1119
+get_chars(Mod, Func, XtraArg, #state{buf= <<>>}=State) ->
1120
+    get_chars_empty(Mod, Func, XtraArg, start, State);
1121
+get_chars(Mod, Func, XtraArg, #state{buf=Buf}=State) ->
1122
+    get_chars_apply(Mod, Func, XtraArg, start, State#state{buf= <<>>}, Buf).
1123
+
1124
+get_chars_empty(Mod, Func, XtraArg, S, 
1125
+		#state{handle=Handle,read_mode=ReadMode}=State) ->
1126
+    case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of
1127
+	{ok,Bin} ->
1128
+	    get_chars_apply(Mod, Func, XtraArg, S, State, Bin);
1129
+	eof ->
1130
+	    get_chars_apply(Mod, Func, XtraArg, S, State, eof);
1131
+	{error,Reason}=Error ->
1132
+	    {stop,Reason,Error,State}
1133
+    end.
1134
+
1135
+get_chars_apply(Mod, Func, XtraArg, S0, 
1136
+		#state{read_mode=ReadMode}=State, Data0) ->
1137
+    Data1 = case ReadMode of
1138
+	       list when binary(Data0) -> binary_to_list(Data0);
1139
+	       _ -> Data0
1140
+	    end,
1141
+    case catch Mod:Func(S0, Data1, XtraArg) of
1142
+	{stop,Result,Buf} ->
1143
+	    {reply,Result,State#state{buf=cast_binary(Buf)}};
1144
+	{'EXIT',Reason} ->
1145
+	    {stop,Reason,{error,err_func(Mod, Func, XtraArg)},State};
1146
+	S1 ->
1147
+	    get_chars_empty(Mod, Func, XtraArg, S1, State)
1148
+    end.
1149
+
1150
+%% Convert error code to make it look as before
1151
+err_func(io_lib, get_until, {_,F,_}) ->
1152
+    F;
1153
+err_func(_, F, _) ->
1154
+    F.
1155
+
1156
+
1157
+
1158
+%% Process the I/O request setopts
1159
+%%
1160
+%% setopts
1161
+setopts(Opts0, State) ->
1162
+    Opts = proplists:substitute_negations([{list,binary}], Opts0),
1163
+    case proplists:get_value(binary, Opts) of
1164
+	true ->
1165
+	    {ok,ok,State#state{read_mode=binary}};
1166
+	false ->
1167
+	    {ok,ok,State#state{read_mode=list}};
1168
+	_ ->
1169
+	    {error,{error,badarg},State}
1170
+    end.
1171
+
1172
+
1173
+
1174
+%% Concatenate two binaries and convert the result to list or binary
1175
+cat(B1, B2, binary) ->
1176
+    list_to_binary([B1,B2]);
1177
+cat(B1, B2, list) ->
1178
+    binary_to_list(B1)++binary_to_list(B2).
1179
+
1180
+%% Cast binary to list or binary
1181
+cast(B, binary) ->
1182
+    B;
1183
+cast(B, list) ->
1184
+    binary_to_list(B).
1185
+
1186
+%% Convert buffer to binary
1187
+cast_binary(Binary) when binary(Binary) ->
1188
+    Binary;
1189
+cast_binary(List) when list(List) ->
1190
+    list_to_binary(List);
1191
+cast_binary(_EOF) ->
1192
+    <<>>.
1193
+
1194
+%% Read size for different read modes
1195
+read_size(binary) ->
1196
+    ?READ_SIZE_BINARY;
1197
+read_size(list) ->
1198
+    ?READ_SIZE_LIST.
1199
+
1200
+max(A, B) when A >= B ->
1201
+    A;
1202
+max(_, B) ->
1203
+    B.
1204
+
1205
+%%%-----------------------------------------------------------------
1206
+%%% ?PRIM_FILE helpers
1207
+
1208
+%% Compensates ?PRIM_FILE:position/2 for the number of bytes 
1209
+%% we have buffered
1210
+
1211
+position(Handle, cur, Buf) ->
1212
+    position(Handle, {cur, 0}, Buf);
1213
+position(Handle, {cur, Offs}, Buf) when list(Buf) ->
1214
+    ?PRIM_FILE:position(Handle, {cur, Offs-length(Buf)});
1215
+position(Handle, {cur, Offs}, Buf) when binary(Buf) ->
1216
+    ?PRIM_FILE:position(Handle, {cur, Offs-size(Buf)});
1217
+position(Handle, At, _Buf) ->
1218
+    ?PRIM_FILE:position(Handle, At).
1219
+

Return to bug 135593