1% -----------------------------------------------------------------------------
2% unijp.erl
3% -----------------------------------------------------------------------------
4% Mastering programmed by YAMASHINA Hio
5%
6% Copyright 2007 YAMASHINA Hio
7% -----------------------------------------------------------------------------
8
9% -----------------------------------------------------------------------------
10%% @author    YAMASHINA Hio <hio@hio.jp>
11%% @copyright 2008 YAMASHINA Hio
12%% @version   0.01
13%% @doc       Unicode::Japanese binding
14%% == SUPPORTED ENCODINGS ==
15%% These options are available for `Icode' argument of {@link new/3} and {@link set/2}.
16%% <ul>
17%% <li>`utf8'</li>
18%% <li>`sjis'</li>
19%% <li>`eucjp'</li>
20%% <li>`jis'</li>
21%% </ul>
22%% @end
23% -----------------------------------------------------------------------------
24
25-module(unijp).
26-export([start/0, stop/0]).
27-export([version_str/0]).
28-export([version_tuple/0]).
29-export([conv/3]).
30-export([test/0]).
31
32-define(SHAREDLIB, "unijp_driver").
33-define(PKGNAME, unijp).
34-define(REGNAME, unijp).
35
36-define(PORT_VERSION_STR,   1).
37-define(PORT_VERSION_TUPLE, 2).
38-define(PORT_CONV_3,        3).
39
40test(Name,Fun) ->
41  io:format("~s ...~n", [Name]),
42  Ret = Fun(),
43  io:format("~s: ~p~n", [Name, Ret]),
44  Ret.
45test() ->
46  test(start, fun()-> start() end),
47  test(version_str,   fun()-> version_str()                end),
48  test(version_tuple, fun()-> version_tuple()              end),
49  test(conv,          fun()-> conv("utf8", "utf8", "text") end),
50  test(conv,          fun()-> conv("utf8", "ucs4", "ts") end),
51  test(conv,          fun()-> conv("utf8", "ucs4", "text") end),
52  io:format("ok.~n"),
53  ok.
54
55% -----------------------------------------------------------------------------
56% version_str().
57%% @spec version_str() -> string()
58%% @doc get version number as string.
59version_str() ->
60	Result = erlang:port_call(whereis(?REGNAME), ?PORT_VERSION_STR, []),
61	{ok, VersionStr} = Result,
62	VersionStr.
63
64% -----------------------------------------------------------------------------
65% version_tuple().
66%% @spec version_tuple() -> {int(),int(),int()}
67%% @doc get version number as tuple of integers.
68version_tuple() ->
69	Result = erlang:port_call(whereis(?REGNAME), ?PORT_VERSION_TUPLE, []),
70	{ok, {Major,Minor,Patch}} = Result,
71	{Major,Minor,Patch}.
72
73% -----------------------------------------------------------------------------
74% conv(From, To, Source).
75%% @spec conv(From, To, Source) -> string()
76%%   From   = atom()
77%%   To     = atom()
78%%   Source = iolist()
79%% @doc convert string Source from From to To.
80conv(From, To, Source) ->
81	Bin = iolist_to_binary(Source),
82	Result = erlang:port_call(whereis(?REGNAME), ?PORT_CONV_3, {From,To,Bin}),
83	{ok, Converted} = Result,
84	Converted.
85
86% -----------------------------------------------------------------------------
87% start.
88%
89%% @spec start()->term()
90%% @doc  start port driver
91start() ->
92	my_start(whereis(?REGNAME)).
93
94%% @spec my_start(Port)->pod()
95%%   Port = undefined | port()
96%% @private
97my_start(undefined) ->
98	% io:format("start: begin trans..~n"),
99	global:trans({unijp_start, self()}, fun()->
100		case whereis(?REGNAME) of
101		undefined ->
102			% io:format("start: real start~n"),
103			Pid = my_spawn_server(),
104			% io:format("start: register: ~p (registered:~p)~n", [Pid, whereis(?REGNAME)]),
105			Pid;
106		Pid ->
107			% io:format("start: found in tran ~p~n", [Pid]),
108			Pid
109		end
110	end);
111my_start(Pid) ->
112	% io:format("start: exists: ~p~n", [Pid]),
113	Pid.
114
115%% @spec my_spawn_server()->pid()
116%% @private
117%% @doc  spawn server process.
118my_spawn_server() ->
119	% io:format("spawn server ...~n"),
120	Parent = self(),
121	Daemon = spawn(fun()->my_server(Parent) end),
122	Port = receive
123		{Daemon, Recv} -> Recv
124		% after 3000     -> exit(timeout)
125		after 3000*60     -> exit(timeout)
126	end,
127	Port.
128
129%% @spec my_server(Parent)->void()
130%%   Parent = pid()
131%% @private
132%% @doc  unijp daemon, main.
133my_server(Parent) ->
134	% io:format("server proc ...~n"),
135	Port = my_open_port(),
136	register(?REGNAME, Port),
137	register(unijp_daemon, self()),
138	Parent ! {self(), Port},
139	my_server_loop([]).
140
141%% @spec my_open_port() -> port()
142%% @private
143%% @doc  open port procedure.
144my_open_port()->
145	% io:format("load_driver ...~n"),
146	case erl_ddll:load_driver(code:priv_dir(?PKGNAME), ?SHAREDLIB) of
147		ok -> ok;
148		{error, already_loaded} -> ok;
149		Any -> exit({error, {could_not_load_driver, Any}})
150	end,
151	% io:format("open_port ...~n"),
152	Port = open_port({spawn, ?SHAREDLIB}, [binary]),
153	% io:format("open_port ~p on ~p~n", [Port, self()]),
154	Port.
155
156%% @spec my_server_loop(PortList)->void()
157%%   PortList = [port()]
158%% @private
159%% @doc  loop forever to keep driver/port instances.
160my_server_loop(PortList)->
161	% io:format("my_server_loop ~p(~p)~n", [self(), PortList]),
162	receive
163	{ Caller, close } when is_pid(Caller) ->
164		Caller ! ok;
165	{ Caller, find_port } when is_pid(Caller) ->
166		my_server_find_port(Caller, PortList);
167	{ Caller, release_port, Port } when is_pid(Caller) ->
168		my_server_loop([Port|PortList]);
169	_Any ->
170		my_server_loop(PortList)
171	end.
172
173my_server_find_port(Caller, []) ->
174	my_server_find_port(Caller, [my_open_port()]);
175my_server_find_port(Caller, [Port|PortList]) ->
176	Caller ! { find_port, Port },
177	my_server_loop(PortList).
178
179% -----------------------------------------------------------------------------
180% stop.
181%
182%% @spec stop() -> ok
183%% @doc  stop unijp port service.
184stop() ->
185	case whereis(unijp_daemon) of
186	undefined ->
187		ok;
188	Port ->
189		Port ! { self(), close },
190		receive
191			ok -> ok
192		after 10000 ->
193			exit(noreply)
194		end
195	end.
196
197% -----------------------------------------------------------------------------
198% End of Module.
199% -----------------------------------------------------------------------------
200