aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/inet_parse.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/src/inet_parse.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/kernel/src/inet_parse.erl')
-rw-r--r--lib/kernel/src/inet_parse.erl755
1 files changed, 755 insertions, 0 deletions
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
new file mode 100644
index 0000000000..62d44fb723
--- /dev/null
+++ b/lib/kernel/src/inet_parse.erl
@@ -0,0 +1,755 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(inet_parse).
+
+%% Parser for all kinds of ineternet configuration files
+
+-export([hosts/1, hosts/2]).
+-export([hosts_vxworks/1]).
+-export([protocols/1, protocols/2]).
+-export([netmasks/1, netmasks/2]).
+-export([networks/1, networks/2]).
+-export([services/1, services/2]).
+-export([rpc/1, rpc/2]).
+-export([resolv/1, resolv/2]).
+-export([host_conf_linux/1, host_conf_linux/2]).
+-export([host_conf_freebsd/1, host_conf_freebsd/2]).
+-export([host_conf_bsdos/1, host_conf_bsdos/2]).
+-export([nsswitch_conf/1, nsswitch_conf/2]).
+
+-export([ipv4_address/1, ipv6_address/1]).
+-export([address/1]).
+-export([visible_string/1, domain/1]).
+-export([ntoa/1, dots/1]).
+-export([split_line/1]).
+
+-import(lists, [reverse/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+%% --------------------------------------------------------------------------
+%% Parse services internet style
+%% Syntax:
+%% Name Port/Protocol [Aliases] \n
+%% # comment
+%% --------------------------------------------------------------------------
+
+services(File) ->
+ services(noname, File).
+
+services(Fname, File) ->
+ Fn = fun([Name, PortProto | Aliases]) ->
+ {Proto,Port} = port_proto(PortProto, 0),
+ {Name,Proto,Port,Aliases}
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse rpc program names
+%% Syntax:
+%% Name Program [Aliases] \n |
+%% # comment
+%% --------------------------------------------------------------------------
+
+rpc(File) ->
+ rpc(noname, File).
+
+rpc(Fname, File) ->
+ Fn = fun([Name,Program | Aliases]) ->
+ Prog = list_to_integer(Program),
+ {Name,Prog,Aliases}
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse hosts file unix style
+%% Syntax:
+%% IP Name [Aliases] \n |
+%% # comment
+%% --------------------------------------------------------------------------
+hosts(File) ->
+ hosts(noname,File).
+
+hosts(Fname,File) ->
+ Fn = fun([Address, Name | Aliases]) ->
+ %% XXX Fix for link-local IPv6 addresses that specify
+ %% interface with a %if suffix. These kind of
+ %% addresses maybe need to be gracefully handled
+ %% throughout inet* and inet_drv.
+ case string:tokens(Address, "%") of
+ [Addr,_] ->
+ {ok,_} = address(Addr),
+ skip;
+ _ ->
+ {ok,IP} = address(Address),
+ {IP, Name, Aliases}
+ end
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse hostShow vxworks style
+%% Syntax:
+%% Name IP [Aliases] \n
+%% --------------------------------------------------------------------------
+hosts_vxworks(Hosts) ->
+ Fn = fun([Name, Address | Aliases]) ->
+ {ok,IP} = address(Address),
+ {IP, Name, Aliases}
+ end,
+ parse_file(Hosts, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse resolv file unix style
+%% Syntax:
+%% domain Domain \n
+%% nameserver IP \n
+%% search Dom1 Dom2 ... \n
+%% lookup Method1 Method2 Method3 \n
+%% # comment
+%% --------------------------------------------------------------------------
+
+resolv(File) ->
+ resolv(noname,File).
+
+resolv(Fname, File) ->
+ Fn = fun(["domain", Domain]) ->
+ {domain, Domain};
+ (["nameserver", Address]) ->
+ {ok,IP} = address(Address),
+ {nameserver,IP};
+ (["search" | List]) ->
+ {search, List};
+ (["lookup" | Types]) ->
+ {lookup, Types};
+ (_) ->
+ skip %% there are too many local options, we MUST skip
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%%
+%% Parse Linux host.conf file
+%% find "order" only.
+%%
+%% --------------------------------------------------------------------------
+host_conf_linux(File) ->
+ host_conf_linux(noname,File).
+
+host_conf_linux(Fname, File) ->
+ Fn = fun(["order" | Order]) ->
+ %% XXX remove ',' between entries
+ {lookup, split_comma(Order)};
+ (_) ->
+ skip
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%%
+%% Parse Freebsd/Netbsd host.conf file
+%% find "order" only.
+%%
+%% --------------------------------------------------------------------------
+host_conf_freebsd(File) ->
+ host_conf_freebsd(noname,File).
+
+host_conf_freebsd(Fname, File) ->
+ Fn = fun([Type]) -> Type end,
+ case parse_file(Fname, File, Fn) of
+ {ok, Ls} -> {ok, [{lookup, Ls}]};
+ Error -> Error
+ end.
+
+
+
+%% --------------------------------------------------------------------------
+%%
+%% Parse BSD/OS irs.conf file
+%% find "hosts" only and ignore options.
+%%
+%% Syntax:
+%% Map AccessMethod [,AccessMethod] [continue|merge [,merge|,continue]] \n
+%% # comment
+
+%% --------------------------------------------------------------------------
+host_conf_bsdos(File) ->
+ host_conf_bsdos(noname,File).
+
+host_conf_bsdos(Fname, File) ->
+ Fn = fun(["hosts" | List]) ->
+ delete_options(split_comma(List));
+ (_) ->
+ skip
+ end,
+ case parse_file(Fname, File, Fn) of
+ {ok, Ls} ->
+ {ok, [{lookup, lists:append(Ls)}]};
+ Error -> Error
+ end.
+
+delete_options(["continue"|T]) ->
+ delete_options(T);
+delete_options(["merge"|T]) ->
+ delete_options(T);
+delete_options([H|T]) ->
+ [H|delete_options(T)];
+delete_options([]) ->
+ [].
+
+
+%% --------------------------------------------------------------------------
+%%
+%% Parse Solaris nsswitch.conf
+%% find "hosts:" only
+%%
+%% --------------------------------------------------------------------------
+
+nsswitch_conf(File) ->
+ nsswitch_conf(noname,File).
+
+nsswitch_conf(Fname, File) ->
+ Fn = fun(["hosts:" | Types]) ->
+ {lookup, Types};
+ (_) -> skip
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse protocol file unix style
+%% Syntax:
+%% name protocol number name \n
+%% # comment
+%% --------------------------------------------------------------------------
+
+protocols(File) ->
+ protocols(noname,File).
+
+protocols(Fname, File) ->
+ Fn = fun([Name, Number, DName]) ->
+ {list_to_atom(Name), list_to_integer(Number), DName}
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse netmasks file unix style
+%% Syntax:
+%% Network Subnetmask
+%% # comment
+%% --------------------------------------------------------------------------
+
+netmasks(File) ->
+ netmasks(noname, File).
+
+netmasks(Fname, File) ->
+ Fn = fun([Net, Subnetmask]) ->
+ {ok, NetIP} = address(Net),
+ {ok, Mask} = address(Subnetmask),
+ {NetIP, Mask}
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%% Parse networks file unix style
+%% Syntax:
+%% network-name network-number aliases ...
+%% # comment
+%% --------------------------------------------------------------------------
+
+networks(File) ->
+ networks(noname, File).
+
+networks(Fname, File) ->
+ Fn = fun([NetName, NetNumber]) ->
+ Number = list_to_integer(NetNumber),
+ {NetName, Number}
+ end,
+ parse_file(Fname, File, Fn).
+
+%% --------------------------------------------------------------------------
+%%
+%% Simple Line by Line parser
+%%
+%% --------------------------------------------------------------------------
+
+parse_file(File, Fn) ->
+ parse_file(noname, File, Fn).
+
+parse_file(Fname, {fd,Fd}, Fn) ->
+ parse_fd(Fname,Fd, 1, Fn, []);
+parse_file(Fname, {chars,Cs}, Fn) when is_list(Cs) ->
+ parse_cs(Fname, Cs, 1, Fn, []);
+parse_file(Fname, {chars,Cs}, Fn) when is_binary(Cs) ->
+ parse_cs(Fname, binary_to_list(Cs), 1, Fn, []);
+parse_file(_, File, Fn) ->
+ case file:open(File, [read]) of
+ {ok, Fd} ->
+ Result = parse_fd(File,Fd, 1, Fn, []),
+ file:close(Fd),
+ Result;
+ Error -> Error
+ end.
+
+parse_fd(Fname,Fd, Line, Fun, Ls) ->
+ case read_line(Fd) of
+ eof -> {ok, reverse(Ls)};
+ Cs ->
+ case split_line(Cs) of
+ [] -> parse_fd(Fname, Fd, Line+1, Fun, Ls);
+ Toks ->
+ case catch Fun(Toks) of
+ {'EXIT',_} ->
+ error("~p:~p: erroneous line, SKIPPED~n",[Fname,Line]),
+ parse_fd(Fname, Fd,Line+1,Fun,Ls);
+ {warning,Wlist,Val} ->
+ warning("~p:~p: warning! strange domain name(s) ~p ~n",[Fname,Line,Wlist]),
+ parse_fd(Fname, Fd,Line+1,Fun,[Val|Ls]);
+
+ skip ->
+ parse_fd(Fname, Fd, Line+1, Fun, Ls);
+ Val -> parse_fd(Fname, Fd, Line+1, Fun, [Val|Ls])
+ end
+ end
+ end.
+
+parse_cs(Fname, Chars, Line, Fun, Ls) ->
+ case get_line(Chars) of
+ eof -> {ok, reverse(Ls)};
+ {Cs,Chars1} ->
+ case split_line(Cs) of
+ [] -> parse_cs(Fname, Chars1, Line+1, Fun, Ls);
+ Toks ->
+ case catch Fun(Toks) of
+ {'EXIT',_} ->
+ error("~p:~p: erroneous line, SKIPPED~n",[Fname,Line]),
+ parse_cs(Fname, Chars1, Line+1, Fun, Ls);
+ {warning,Wlist,Val} ->
+ warning("~p:~p: warning! strange domain name(s) ~p ~n",[Fname,Line,Wlist]),
+ parse_cs(Fname, Chars1, Line+1, Fun, [Val|Ls]);
+
+ skip -> parse_cs(Fname, Chars1, Line+1, Fun, Ls);
+ Val -> parse_cs(Fname, Chars1, Line+1, Fun, [Val|Ls])
+ end
+ end
+ end.
+
+get_line([]) -> eof;
+get_line(Chars) -> get_line(Chars,[]).
+
+get_line([], Acc) -> {reverse(Acc), []};
+get_line([$\r, $\n | Cs], Acc) -> {reverse([$\n|Acc]), Cs};
+get_line([$\n | Cs], Acc) -> {reverse([$\n|Acc]), Cs};
+get_line([C | Cs], Acc) -> get_line(Cs, [C|Acc]).
+
+%%
+%% Read a line
+%%
+read_line(Fd) when is_pid(Fd) -> io:get_line(Fd, '');
+read_line(Fd = #file_descriptor{}) ->
+ collect_line(Fd, []).
+
+collect_line(Fd, Cs) ->
+ case file:read(Fd, 80) of
+ {ok, Line} when is_binary(Line) ->
+ collect_line(Fd, byte_size(Line), binary_to_list(Line), Cs);
+ {ok, Line} ->
+ collect_line(Fd, length(Line), Line, Cs);
+ eof when Cs =:= [] ->
+ eof;
+ eof -> reverse(Cs)
+ end.
+
+collect_line(Fd, N, [$\r, $\n|_], Cs) ->
+ {ok, _} = file:position(Fd, {cur,-(N-2)}),
+ reverse([$\n|Cs]);
+collect_line(Fd, N, [$\n|_], Cs) ->
+ {ok, _} = file:position(Fd, {cur,-(N-1)}),
+ reverse([$\n|Cs]);
+collect_line(Fd, _, [], Cs) ->
+ collect_line(Fd, Cs);
+collect_line(Fd, N, [X|Xs], Cs) ->
+ collect_line(Fd, N-1, Xs, [X|Cs]).
+
+
+%% split Port/Proto -> {Port, Proto}
+port_proto([X|Xs], N) when X >= $0, X =< $9 ->
+ port_proto(Xs, N*10 + (X - $0));
+port_proto([$/ | Proto], Port) when Port =/= 0 ->
+ {list_to_atom(Proto), Port}.
+
+%%
+%% Check if a String is a string with visible characters #21..#7E
+%% visible_string(String) -> Bool
+%%
+visible_string([H|T]) ->
+ is_vis1([H|T]);
+visible_string(_) ->
+ false.
+
+is_vis1([C | Cs]) when C >= 16#21, C =< 16#7e -> is_vis1(Cs);
+is_vis1([]) -> true;
+is_vis1(_) -> false.
+
+%%
+%% Check if a String is a domain name according to RFC XXX.
+%% domain(String) -> Bool
+%%
+domain([H|T]) ->
+ is_dom1([H|T]);
+domain(_) ->
+ false.
+
+is_dom1([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs);
+is_dom1([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs);
+is_dom1([C | Cs]) when C >= $0, C =< $9 ->
+ case is_dom_ldh(Cs) of
+ true -> is_dom2(string:tokens([C | Cs],"."));
+ false -> false
+ end;
+is_dom1(_) -> false.
+
+is_dom_ldh([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs);
+is_dom_ldh([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs);
+is_dom_ldh([C | Cs]) when C >= $0, C =< $9 -> is_dom_ldh(Cs);
+is_dom_ldh([$-,$. | _]) -> false;
+is_dom_ldh([$_,$. | _]) -> false;
+is_dom_ldh([$_ | Cs]) -> is_dom_ldh(Cs);
+is_dom_ldh([$- | Cs]) -> is_dom_ldh(Cs);
+is_dom_ldh([$. | Cs]) -> is_dom1(Cs);
+is_dom_ldh([]) -> true;
+is_dom_ldh(_) -> false.
+
+%%% Check that we don't get a IP-address as a domain name.
+
+-define(L2I(L), (catch list_to_integer(L))).
+
+is_dom2([A,B,C,D]) ->
+ case ?L2I(D) of
+ Di when is_integer(Di) ->
+ case {?L2I(A),?L2I(B),?L2I(C)} of
+ {Ai,Bi,Ci} when is_integer(Ai),
+ is_integer(Bi),
+ is_integer(Ci) -> false;
+ _ -> true
+ end;
+ _ -> true
+ end;
+is_dom2(_) ->
+ true.
+
+
+
+%%
+%% Test ipv4 address or ipv6 address
+%% Return {ok, Address} | {error, Reason}
+%%
+address(Cs) when is_list(Cs) ->
+ case ipv4_address(Cs) of
+ {ok,IP} -> {ok,IP};
+ _ ->
+ case ipv6_address(Cs) of
+ {ok, IP} -> {ok, IP};
+ Error -> Error
+ end
+ end;
+address(_) ->
+ {error, einval}.
+
+%%
+%% Parse IPv4 address:
+%% d1.d2.d3.d4
+%% d1.d2.d4
+%% d1.d4
+%% d4
+%%
+%% Return {ok, IP} | {error, einval}
+%%
+ipv4_address(Cs) ->
+ case catch ipv4_addr(Cs) of
+ {'EXIT',_} -> {error,einval};
+ Addr -> {ok,Addr}
+ end.
+
+ipv4_addr(Cs) ->
+ ipv4_addr(d3(Cs), []).
+
+ipv4_addr({Cs0,[]}, A) when length(A) =< 3 ->
+ case [tod(Cs0)|A] of
+ [D4,D3,D2,D1] ->
+ {D1,D2,D3,D4};
+ [D4,D2,D1] ->
+ {D1,D2,0,D4};
+ [D4,D1] ->
+ {D1,0,0,D4};
+ [D4] ->
+ {0,0,0,D4}
+ end;
+ipv4_addr({Cs0,"."++Cs1}, A) when length(A) =< 2 ->
+ ipv4_addr(d3(Cs1), [tod(Cs0)|A]).
+
+d3(Cs) -> d3(Cs, []).
+
+d3([C|Cs], R) when C >= $0, C =< $9, length(R) =< 2 ->
+ d3(Cs, [C|R]);
+d3(Cs, [_|_]=R) ->
+ {lists:reverse(R),Cs}.
+
+tod(Cs) ->
+ case erlang:list_to_integer(Cs) of
+ D when D >= 0, D =< 255 ->
+ D;
+ _ ->
+ erlang:error(badarg, [Cs])
+ end.
+
+%%
+%% Parse IPv6 address:
+%% x1:x2:x3:x4:x5:x6:x7:x8
+%% x1:x2::x7:x8
+%% ::x7:x8
+%% x1:x2::
+%% ::
+%% x1:x2:x3:x4:x5:x6:d7a.d7b.d8a.d8b
+%% x1:x2::x5:x6:d7a.d7b.d8a.d8b
+%% ::x5:x6:d7a.d7b.d8a.d8b
+%% x1:x2::d7a.d7b.d8a.d8b
+%% ::d7a.d7b.d8a.d8b
+%%
+%% Return {ok, IP} | {error, einval}
+%%
+ipv6_address(Cs) ->
+ case catch ipv6_addr(Cs) of
+ {'EXIT',_} -> {error,einval};
+ Addr -> {ok,Addr}
+ end.
+
+ipv6_addr("::") ->
+ ipv6_addr_done([], []);
+ipv6_addr("::"++Cs) ->
+ ipv6_addr(x4(Cs), [], []);
+ipv6_addr(Cs) ->
+ ipv6_addr(x4(Cs), []).
+
+%% Before "::"
+ipv6_addr({Cs0,[]}, A) when length(A) =:= 7 ->
+ ipv6_addr_done([tox(Cs0)|A]);
+ipv6_addr({Cs0,"::"}, A) when length(A) =< 6 ->
+ ipv6_addr_done([tox(Cs0)|A], []);
+ipv6_addr({Cs0,"::"++Cs1}, A) when length(A) =< 5 ->
+ ipv6_addr(x4(Cs1), [tox(Cs0)|A], []);
+ipv6_addr({Cs0,":"++Cs1}, A) when length(A) =< 6 ->
+ ipv6_addr(x4(Cs1), [tox(Cs0)|A]);
+ipv6_addr({Cs0,"."++Cs1}, A) when length(A) =:= 6 ->
+ ipv6_addr(d3(Cs1), A, [], [tod(Cs0)]).
+
+%% After "::"
+ipv6_addr({Cs0,[]}, A, B) when length(A)+length(B) =< 6 ->
+ ipv6_addr_done(A, [tox(Cs0)|B]);
+ipv6_addr({Cs0,":"++Cs1}, A, B) when length(A)+length(B) =< 5 ->
+ ipv6_addr(x4(Cs1), A, [tox(Cs0)|B]);
+ipv6_addr({Cs0,"."++Cs1}, A, B) when length(A)+length(B) =< 5 ->
+ ipv6_addr(x4(Cs1), A, B, [tod(Cs0)]).
+
+%% After "."
+ipv6_addr({Cs0,[]}, A, B, C) when length(C) =:= 3 ->
+ ipv6_addr_done(A, B, [tod(Cs0)|C]);
+ipv6_addr({Cs0,"."++Cs1}, A, B, C) when length(C) =< 2 ->
+ ipv6_addr(d3(Cs1), A, B, [tod(Cs0)|C]).
+
+ipv6_addr_done(Ar, Br, [D4,D3,D2,D1]) ->
+ ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br]).
+
+ipv6_addr_done(Ar, Br) ->
+ ipv6_addr_done(Br++dup(8-length(Ar)-length(Br), 0, Ar)).
+
+ipv6_addr_done(Ar) ->
+ list_to_tuple(lists:reverse(Ar)).
+
+x4(Cs) -> x4(Cs, []).
+
+x4([C|Cs], R) when C >= $0, C =< $9, length(R) =< 3 ->
+ x4(Cs, [C|R]);
+x4([C|Cs], R) when C >= $a, C =< $f, length(R) =< 3 ->
+ x4(Cs, [C|R]);
+x4([C|Cs], R) when C >= $A, C =< $F, length(R) =< 3 ->
+ x4(Cs, [C|R]);
+x4(Cs, [_|_]=R) ->
+ {lists:reverse(R),Cs}.
+
+tox(Cs) ->
+ erlang:list_to_integer(Cs, 16).
+
+dup(0, _, L) ->
+ L;
+dup(N, E, L) when is_integer(N), N >= 1 ->
+ dup(N-1, E, [E|L]);
+dup(N, E, L) ->
+ erlang:error(badarg, [N,E,L]).
+
+%% Convert IPv4 adress to ascii
+%% Convert IPv6 / IPV4 adress to ascii (plain format)
+ntoa({A,B,C,D}) ->
+ integer_to_list(A) ++ "." ++ integer_to_list(B) ++ "." ++
+ integer_to_list(C) ++ "." ++ integer_to_list(D);
+%% ANY
+ntoa({0,0,0,0,0,0,0,0}) -> "::";
+%% LOOPBACK
+ntoa({0,0,0,0,0,0,0,1}) -> "::1";
+%% IPV4 ipv6 host address
+ntoa({0,0,0,0,0,0,A,B}) -> "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
+%% IPV4 non ipv6 host address
+ntoa({0,0,0,0,0,16#ffff,A,B}) ->
+ "::FFFF:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
+ntoa({_,_,_,_,_,_,_,_}=T) ->
+ %% Find longest sequence of zeros, at least 2, to replace with "::"
+ ntoa(tuple_to_list(T), []).
+
+%% Find first double zero
+ntoa([], R) ->
+ ntoa_done(R);
+ntoa([0,0|T], R) ->
+ ntoa(T, R, 2);
+ntoa([D|T], R) ->
+ ntoa(T, [D|R]).
+
+%% Count consecutive zeros
+ntoa([], R, _) ->
+ ntoa_done(R, []);
+ntoa([0|T], R, N) ->
+ ntoa(T, R, N+1);
+ntoa([D|T], R, N) ->
+ ntoa(T, R, N, [D]).
+
+%% Find alternate double zero
+ntoa([], R1, _N1, R2) ->
+ ntoa_done(R1, R2);
+ntoa([0,0|T], R1, N1, R2) ->
+ ntoa(T, R1, N1, R2, 2);
+ntoa([D|T], R1, N1, R2) ->
+ ntoa(T, R1, N1, [D|R2]).
+
+%% Count consecutive alternate zeros
+ntoa(T, R1, N1, R2, N2) when N2 > N1 ->
+ %% Alternate zero sequence is longer - use it instead
+ ntoa(T, R2++dup(N1, 0, R1), N2);
+ntoa([], R1, _N1, R2, N2) ->
+ ntoa_done(R1, dup(N2, 0, R2));
+ntoa([0|T], R1, N1, R2, N2) ->
+ ntoa(T, R1, N1, R2, N2+1);
+ntoa([D|T], R1, N1, R2, N2) ->
+ ntoa(T, R1, N1, [D|dup(N2, 0, R2)]).
+
+ntoa_done(R1, R2) ->
+ lists:append(
+ separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R1)))++
+ ["::"|separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R2)))]).
+
+ntoa_done(R) ->
+ lists:append(separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R)))).
+
+separate(_E, []) ->
+ [];
+separate(E, [_|_]=L) ->
+ separate(E, L, []).
+
+separate(E, [H|[_|_]=T], R) ->
+ separate(E, T, [E,H|R]);
+separate(_E, [H], R) ->
+ lists:reverse(R, [H]).
+
+%% convert to A.B decimal form
+dig_to_dec(0) -> [$0,$.,$0];
+dig_to_dec(X) ->
+ integer_to_list((X bsr 8) band 16#ff) ++ "." ++
+ integer_to_list(X band 16#ff).
+
+%% Convert a integer to hex string
+dig_to_hex(X) ->
+ erlang:integer_to_list(X, 16).
+
+%%
+%% Count number of '.' in a name
+%% return {Number of non-terminating dots, has-terminating dot?}
+%% {integer, bool}
+%%
+dots(Name) -> dots(Name, 0).
+
+dots([$.], N) -> {N, true};
+dots([$. | T], N) -> dots(T, N+1);
+dots([_C | T], N) -> dots(T, N);
+dots([], N) -> {N, false}.
+
+
+split_line(Line) ->
+ split_line(Line, []).
+
+split_line([$# | _], Tokens) -> reverse(Tokens);
+split_line([$\s| L], Tokens) -> split_line(L, Tokens);
+split_line([$\t | L], Tokens) -> split_line(L, Tokens);
+split_line([$\n | L], Tokens) -> split_line(L, Tokens);
+split_line([], Tokens) -> reverse(Tokens);
+split_line([C|Cs], Tokens) -> split_mid(Cs, [C], Tokens).
+
+split_mid([$# | _Cs], Acc, Tokens) -> split_end(Acc, Tokens);
+split_mid([$\s | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]);
+split_mid([$\t | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]);
+split_mid([$\r, $\n | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]);
+split_mid([$\n | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]);
+split_mid([], Acc, Tokens) -> split_end(Acc, Tokens);
+split_mid([C|Cs], Acc, Tokens) -> split_mid(Cs, [C|Acc], Tokens).
+
+split_end(Acc, Tokens) -> reverse([reverse(Acc) | Tokens]).
+
+
+%% Split a comma separated tokens. Because we already have split on
+%% spaces we may have the cases
+%%
+%% ",foo"
+%% "foo,"
+%% "foo,bar..."
+
+split_comma([]) ->
+ [];
+split_comma([Token | Tokens]) ->
+ split_comma(Token, []) ++ split_comma(Tokens).
+
+split_comma([], Tokens) -> reverse(Tokens);
+split_comma([$, | L], Tokens) -> split_comma(L, Tokens);
+split_comma([C|Cs], Tokens) -> split_mid_comma(Cs, [C], Tokens).
+
+split_mid_comma([$, | Cs], Acc, Tokens) ->
+ split_comma(Cs, [reverse(Acc) | Tokens]);
+split_mid_comma([], Acc, Tokens) ->
+ split_end(Acc, Tokens);
+split_mid_comma([C|Cs], Acc, Tokens) ->
+ split_mid_comma(Cs, [C|Acc], Tokens).
+
+%%
+
+warning(Fmt, Args) ->
+ case application:get_env(kernel,inet_warnings) of
+ {ok,on} ->
+ error_logger:info_msg("inet_parse:" ++ Fmt, Args);
+ _ ->
+ ok
+ end.
+
+error(Fmt, Args) ->
+ error_logger:info_msg("inet_parse:" ++ Fmt, Args).
+