%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.0, (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.erlang.org/EPL1_0.txt
%% 
%% 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.
%% 
%% The Original Code is Erlang-4.7.3, December, 1998.
%% 
%% The Initial Developer of the Original Code is Ericsson Telecom
%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%% Telecom AB. All Rights Reserved.
%% 
%% Contributor(s): ______________________________________.''
%%
%% Copyright (C) 1990, Ellemtel Telecommunications Systems Laboratories
%% File    : io_lib.erl
%% Author  : Robert Virding
%% Purpose : Library of useful i/o functions.
%% Revision: $Id: io_lib.erl,v 4.3.1.2 1996/04/23 12:11:39 rv Exp $

%% This module is a library of useful i/o functions. It is hoped that the
%% functions defined in it are basic enough to be used without modification
%% as components of more complex utilities.
%%
%% It is completely self-contained and uses no other modules. Its own
%% utilities are exported.
%%
%% Most of the code here is derived from the original prolog versions and
%% from similar code written by Joe Armstrong and myself.
%%
%% This module has been split into seperate modules:
%% io_lib        - basic write and utilities
%% io_lib_format - formatted output
%% io_lib_fread  - formatted input
%% io_lib_pretty - term prettyprinter

-module(io_lib).
-copyright('Copyright (c) 1991-97 Ericsson Telecom AB').
-vsn('$Revision: /main/release/free/1').

-export([fwrite/2,fread/2,fread/3,format/2]).
-export([print/1,print/4,indentation/2]).

-export([write/1,write/2,write/3,nl/0]).
-export([write_atom/1,write_string/1,write_string/2,write_char/1]).

-export([quote_atom/2,char_list/1,deep_char_list/1,printable_list/1]).

%% Utilities for collecting characters.
-export([collect_chars/3,collect_line/2]).

-export([scan/1,scan/2,scan/3,reserved_word/1]).

%% Backward compatibility functions.

scan(Cont, Chars, Pos) -> erl_scan:tokens(Cont, Chars, Pos).

scan(Chars) -> erl_scan:string(Chars).

scan(Chars, StartPos) -> erl_scan:string(Chars, StartPos).

reserved_word(Atom) -> erl_scan:reserved_word(Atom).

%% Interface calls to sub-modules.

fwrite(Format, Args) ->
    io_lib_format:fwrite(Format, Args).

fread(Chars, Format) ->
    io_lib_fread:fread(Chars, Format).

fread(Cont, Chars, Format) ->
    io_lib_fread:fread(Cont, Chars, Format).

format(Format, Args) ->
    io_lib_format:fwrite(Format, Args).

print(Term) ->
    io_lib_pretty:print(Term).

print(Term, Column, LineLength, Depth) ->
    io_lib_pretty:print(Term, Column, LineLength, Depth).

indentation(Chars, Current) ->
    io_lib_format:indentation(Chars, Current).

%% write(Term)
%% write(Term, Depth)
%% write(Term, Depth, Pretty)
%%  Return a (non-flattened) list of characters giving a printed
%%  representation of the term. write/3 is for backward compatibility.

write(Term) -> write(Term, -1).

write(Term, D, true) ->
    io_lib_pretty:print(Term, 1, 80, D);
write(Term, D, false) ->
    write(Term, D).

write(Term, 0) -> "...";
write(Term, D) when integer(Term) -> integer_to_list(Term);
write(Term, D) when float(Term) -> io_lib_format:fwrite_g(Term);
write(Atom, D) when atom(Atom) -> write_atom(Atom);
write(Term, D) when port(Term) -> "#Port";
write(Term, D) when pid(Term) -> pid_to_list(Term);
write(Term, D) when reference(Term) -> "#Ref";
write(Term, D) when binary(Term) -> "#Bin";
write([], D) -> "[]";
write({}, D) -> "{}";
write([H|T], D) ->
    if
	D == 1 -> "[...]";
	true ->
	    [$[,[write(H, D-1)|write_tail(T, D-1)],$]]
    end;
write(F, D) when function(F) ->
    {'fun',M,I,U,Free} = F,
    ["#Fun<",atom_to_list(M),">"];
write(T, D) when tuple(T) ->
    if
	D == 1 -> "{...}";
	true ->
	    [${,
	     [write(element(1, T), D-1)|write_tail(tl(tuple_to_list(T)), D-1)],
	     $}]
    end.

%% write_tail(List, Depth)
%%  Test the terminating case first as this looks better with depth.

write_tail([], D) -> "";
write_tail(List, 1) -> "|...";
write_tail([H|T], D) ->
    [$,,write(H, D-1)|write_tail(T, D-1)];
write_tail(Other, D) ->
    [$|,write(Other, D-1)].

%% write_atom(Atom) -> [Char]
%%  Generate the list of characters needed to print an atom.

write_atom(Atom) ->
    Chars = atom_to_list(Atom),
    case quote_atom(Atom, Chars) of
	true ->
	    write_string(Chars, $');
	false ->
	    Chars
    end.

%% write_string([Char]) -> [Char]
%%  Generate the list of characters needed to print a string.

write_string(S) ->
    write_string(S, $").

write_string(S, Q) ->
    [Q|write_string1(S, Q)].

write_string1([], Q) ->
    [Q];
write_string1([C|Cs], Q) ->
    write_char(C, Q, write_string1(Cs, Q)).

%% PrintList = write_char(Char)
%%  Generate the list of characters needed to print a character constant.

write_char(C) when C >= 0, C =< 255 ->
    [$$|write_char(C, -1, [])].

write_char(Q, Q, Tail) ->			%Must check this first
    [$\\,Q|Tail];
write_char($\\, _, Tail) ->			%In printable character range
    [$\\,$\\|Tail];
write_char(C, _, Tail) when C >= $ , C =< $~ ->
    [C|Tail];
write_char(C, _, Tail) when C >= 128+$ , C =< 255 ->
    [C|Tail];
write_char($\n, Q, Tail) ->			%\n = LF
    [$\\,$n|Tail];
write_char($\r, _, Tail) ->			%\r = CR
    [$\\,$r|Tail];
write_char($\t, _, Tail) ->			%\t = TAB
    [$\\,$t|Tail];
write_char($\v, _, Tail) ->			%\v = VT
    [$\\,$v|Tail];
write_char($\b, _, Tail) ->			%\b = BS
    [$\\,$b|Tail];
write_char($\f, _, Tail) ->			%\f = FF
    [$\\,$f|Tail];
write_char($\e, _, Tail) ->			%\e = ESC
    [$\\,$e|Tail];
write_char($\d, _, Tail) ->			%\d = DEL
    [$\\,$d|Tail];
write_char(C, _, Tail) when C < $  ->
    C1 = (C bsr 3) + $0,
    C2 = (C band 7) + $0,
    [$\\,$0,C1,C2|Tail];
write_char(C, _, Tail) when C > $~ ->
    C1 = (C bsr 6) + $0,
    C2 = ((C bsr 3) band 7) + $0,
    C3 = (C band 7) + $0,
    [$\\,C1,C2,C3|Tail].

%% quote_atom(Atom, CharList)
%%  Return 'true' if atom with chars in CharList needs to be quoted, else
%%  return 'false'.

quote_atom(Atom, Cs0) ->
    case erl_scan:reserved_word(Atom) of
	true -> true;
	false ->
	    case Cs0 of
		[C|Cs] when C >= $a, C =< $z ->
		    quote_atom(Cs);
		_ -> true
	    end
    end.

quote_atom([C|Cs]) when C >= $a, C =< $z ->
    quote_atom(Cs);
quote_atom([C|Cs]) when C >= $A, C =< $Z ->
    quote_atom(Cs);
quote_atom([C|Cs]) when C >= $0, C =< $9 ->
    quote_atom(Cs);
quote_atom([$_|Cs]) ->
    quote_atom(Cs);
quote_atom([$@|Cs]) ->
    quote_atom(Cs);
quote_atom([_|_]) ->
    true;
quote_atom([]) ->
    false.

%% char_list(CharList)
%% deep_char_list(CharList)
%%  Return true if CharList is a (possibly deep) list of characters, else
%%  false.

char_list([C|Cs]) when integer(C), C >= 0, C =< 255 ->
    char_list(Cs);
char_list([]) -> true;
char_list(Other) -> false.			%Everything else is false

deep_char_list(Cs) ->
    deep_char_list(Cs, []).

deep_char_list([C|Cs], More) when list(C) ->
    deep_char_list(C, [Cs|More]);
deep_char_list([C|Cs], More) when integer(C), C >= 0, C =< 255 ->
    deep_char_list(Cs, More);
deep_char_list([], [Cs|More]) ->
    deep_char_list(Cs, More);
deep_char_list([], []) -> true;
deep_char_list(Other, More) ->			%Everything else is false
    false.

%% printable_list([Char]) -> bool()
%%  Return true if CharList is a list of printable characters, else
%%  false.

printable_list([C|Cs]) when integer(C), C >= $ , C =< 255 ->
    printable_list(Cs);
printable_list([$\n|Cs]) ->
    printable_list(Cs);
printable_list([$\r|Cs]) ->
    printable_list(Cs);
printable_list([$\t|Cs]) ->
    printable_list(Cs);
printable_list([$\v|Cs]) ->
    printable_list(Cs);
printable_list([$\b|Cs]) ->
    printable_list(Cs);
printable_list([$\f|Cs]) ->
    printable_list(Cs);
printable_list([$\e|Cs]) ->
    printable_list(Cs);
printable_list([]) -> true;
printable_list(Other) -> false.			%Everything else is false

%% List = nl()
%%  Return a list of characters to generate a newline.

nl() ->
    "\n".

%%
%% Utilities for collecting characters in input files
%%

%% collect_chars(Continuation, MoreChars, Count)
%%  Returns:
%%	{done,Result,RestChars}
%%	{more,Continuation}

collect_chars([], Chars, N) ->
    collect_chars1(N, Chars, []);
collect_chars({Left,Sofar}, Chars, N) ->
    collect_chars1(Left, Chars, Sofar).

collect_chars1(N, Chars, Stack) when N =< 0 ->
    {done,lists:reverse(Stack, []),Chars};
collect_chars1(N, [C|Rest], Stack) ->
    collect_chars1(N-1, Rest, [C|Stack]);
collect_chars1(N, eof, []) ->
    {done,eof,[]};
collect_chars1(N, eof, Stack) ->
    {done,lists:reverse(Stack, []),[]};
collect_chars1(N, [], Stack) ->
    {more,{N,Stack}}.

%% collect_line(Continutation, MoreChars)
%%  Returns:
%%	{done,Result,RestChars}
%%	{more,Continuation}

collect_line([], Chars) ->
    collect_line1(Chars, []);
collect_line({SoFar}, More) ->
    collect_line1(More, SoFar).

collect_line1([$\r, $\n|Rest], Stack) ->
    collect_line1([$\n|Rest], Stack);
collect_line1([$\n|Rest], Stack) ->
    {done,lists:reverse([$\n|Stack], []),Rest};
collect_line1([C|Rest], Stack) ->
    collect_line1(Rest, [C|Stack]);
collect_line1(eof, []) ->
    {done,eof,[]};
collect_line1(eof, Stack) ->
    {done,lists:reverse(Stack, []),[]};
collect_line1([], Stack) ->
    {more,{Stack}}.
