/deps/iconv/src/iconv.erl

http://github.com/zotonic/zotonic · Erlang · 218 lines · 128 code · 33 blank · 57 comment · 0 complexity · f146395dc9826630bf8aa99997d704b3 MD5 · raw file

  1. -module(iconv).
  2. %%%----------------------------------------------------------------------
  3. %%% File : iconv.erl
  4. %%% Author : Torbjorn Tornkvist <tobbe@bluetail.com>
  5. %%% Purpose : iconv support
  6. %%% Created : 23 Mar 2004 by <tobbe@bluetail.com>
  7. %%%
  8. %%% $Id$
  9. %%%----------------------------------------------------------------------
  10. -behaviour(gen_server).
  11. -export([start/0, start_link/0, stop/0, open/2, conv/2, close/1]).
  12. %% gen_server callbacks
  13. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  14. terminate/2, code_change/3]).
  15. -record(state, {port}).
  16. -ifdef(TEST).
  17. -include_lib("eunit/include/eunit.hrl").
  18. -endif.
  19. %%% op codes
  20. -define(IV_OPEN, $o).
  21. -define(IV_CONV, $v).
  22. -define(IV_CLOSE, $c).
  23. -define(INBUF_SZ, 512).
  24. -define(DRV_NAME, "iconv_drv").
  25. -define(SERVER, ?MODULE).
  26. %%%----------------------------------------------------------------------
  27. %%% API
  28. %%%----------------------------------------------------------------------
  29. start() ->
  30. gen_server:start({local, ?SERVER}, ?MODULE, [], []).
  31. start_link() ->
  32. gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
  33. stop() ->
  34. gen_server:call(?SERVER, stop).
  35. %%open(To, From) -> {ok, ballen};
  36. open(To, From) ->
  37. gen_server:call(?SERVER, {open, l2b(To), l2b(From)}, infinity).
  38. %%conv(Cd, String) -> {ok, l2b(String)};
  39. conv(Cd, String) when is_binary(Cd) ->
  40. gen_server:call(?SERVER, {conv, Cd, l2b(String)}, infinity).
  41. %%close(Cd) -> ok;
  42. close(Cd) when is_binary(Cd) ->
  43. gen_server:call(?SERVER, {close, Cd}, infinity).
  44. %%%----------------------------------------------------------------------
  45. %%% Callback functions from gen_server
  46. %%%----------------------------------------------------------------------
  47. %%----------------------------------------------------------------------
  48. %% Func: init/1
  49. %% Returns: {ok, State} |
  50. %% {ok, State, Timeout} |
  51. %% ignore |
  52. %% {stop, Reason}
  53. %%----------------------------------------------------------------------
  54. init([]) ->
  55. erl_ddll:start(),
  56. Path = case code:priv_dir(iconv) of
  57. {error, _} ->
  58. case load_path(?DRV_NAME++".so") of
  59. {error, _} ->
  60. error;
  61. {ok, P} ->
  62. P
  63. end;
  64. P ->
  65. P
  66. end,
  67. case Path of
  68. error ->
  69. {stop, no_driver};
  70. Path ->
  71. case erl_ddll:load_driver(Path, ?DRV_NAME) of
  72. ok ->
  73. Port = open_port({spawn, ?DRV_NAME}, [binary]),
  74. {ok, #state{port = Port}};
  75. {error, Error} ->
  76. error_logger:format("Error loading driver: " ++ erl_ddll:format_error(Error), []),
  77. {stop, bad_driver}
  78. end
  79. end.
  80. %%----------------------------------------------------------------------
  81. %% Func: handle_call/3
  82. %% Returns: {reply, Reply, State} |
  83. %% {reply, Reply, State, Timeout} |
  84. %% {noreply, State} |
  85. %% {noreply, State, Timeout} |
  86. %% {stop, Reason, Reply, State} | (terminate/2 is called)
  87. %% {stop, Reason, State} (terminate/2 is called)
  88. %%----------------------------------------------------------------------
  89. handle_call({open, To, From}, _, S) ->
  90. ToLen = byte_size(To),
  91. FromLen = byte_size(From),
  92. Msg = <<?IV_OPEN,ToLen:16,To/binary,FromLen:16,From/binary>>,
  93. Reply = call_drv(S#state.port, Msg),
  94. {reply, Reply, S};
  95. %%
  96. handle_call({conv, Cd, Buf}, _, S) ->
  97. CdLen = byte_size(Cd),
  98. BufLen = byte_size(Buf),
  99. Msg = <<?IV_CONV,CdLen:16,Cd/binary,BufLen:16,Buf/binary>>,
  100. Reply = call_drv(S#state.port, Msg),
  101. {reply, Reply, S};
  102. %%
  103. handle_call({close, Cd}, _, S) ->
  104. CdLen = byte_size(Cd),
  105. Msg = <<?IV_CLOSE,CdLen:16,Cd/binary>>,
  106. Reply = call_drv(S#state.port, Msg),
  107. {reply, Reply, S};
  108. handle_call(stop, _, S) ->
  109. {stop, normal, ok, S}.
  110. call_drv(Port, Msg) ->
  111. erlang:port_command(Port, [Msg]),
  112. recv(Port).
  113. recv(Port) ->
  114. receive
  115. {Port, ok} ->
  116. ok;
  117. {Port, value, Bin} ->
  118. {ok,Bin};
  119. {Port, error, ErrAtom} ->
  120. {error, ErrAtom}
  121. end.
  122. %%----------------------------------------------------------------------
  123. %% Func: handle_cast/2
  124. %% Returns: {noreply, State} |
  125. %% {noreply, State, Timeout} |
  126. %% {stop, Reason, State} (terminate/2 is called)
  127. %%----------------------------------------------------------------------
  128. handle_cast(_Msg, State) ->
  129. {noreply, State}.
  130. %%----------------------------------------------------------------------
  131. %% Func: handle_info/2
  132. %% Returns: {noreply, State} |
  133. %% {noreply, State, Timeout} |
  134. %% {stop, Reason, State} (terminate/2 is called)
  135. %%----------------------------------------------------------------------
  136. handle_info(_Info, State) ->
  137. {noreply, State}.
  138. %%----------------------------------------------------------------------
  139. %% Func: terminate/2
  140. %% Purpose: Shutdown the server
  141. %% Returns: any (ignored by gen_server)
  142. %%----------------------------------------------------------------------
  143. terminate(_Reason, _State) ->
  144. ok.
  145. code_change(_, _, _) ->
  146. ok.
  147. %%%----------------------------------------------------------------------
  148. %%% Internal functions
  149. %%%----------------------------------------------------------------------
  150. load_path(File) ->
  151. case lists:zf(fun(Ebin) ->
  152. Priv = Ebin ++ "/../priv/",
  153. case file:read_file_info(Priv ++ File) of
  154. {ok, _} -> {true, Priv};
  155. _ -> false
  156. end
  157. end, code:get_path()) of
  158. [Dir|_] ->
  159. {ok, Dir};
  160. [] ->
  161. error_logger:format("Error: ~s not found in code path\n", [File]),
  162. {error, enoent}
  163. end.
  164. l2b(L) when is_list(L) -> list_to_binary(L);
  165. l2b(B) when is_binary(B) -> B.
  166. -ifdef(TEST).
  167. smtp_session_test_() ->
  168. {setup,
  169. fun() ->
  170. iconv:start()
  171. end,
  172. fun(_) ->
  173. iconv:stop()
  174. end,
  175. [
  176. {"Convert from latin-1 to utf-8",
  177. fun() ->
  178. {ok, CD} = iconv:open("utf-8", "ISO-8859-1"),
  179. ?assertEqual({ok, <<"hello world">>}, iconv:conv(CD, "hello world")),
  180. iconv:close(CD)
  181. end
  182. }
  183. ]
  184. }.
  185. -endif.