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 |
+ |