Sfoglia il codice sorgente

ft: 修改为基于otp23.2.4编写

master
SisMaker 4 anni fa
parent
commit
59a32fd54a
3 ha cambiato i file con 919 aggiunte e 619 eliminazioni
  1. +2
    -0
      README.md
  2. +640
    -474
      src/eFmt.erl
  3. +277
    -145
      src/eFmtFormat.erl

+ 2
- 0
README.md Vedi File

@ -2,6 +2,8 @@ eFmt
=====
An OTP library to format term, for efficient
base on otp-23.2.4
Build
-----

+ 640
- 474
src/eFmt.erl
File diff soppresso perché troppo grande
Vedi File


src/eFmt_format.erl → src/eFmtFormat.erl Vedi File

@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -17,11 +17,12 @@
%%
%% %CopyrightEnd%
%%
-module(eFmt_format).
-module(eFmtFormat).
%% Formatting functions of io library.
-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
-export([fwrite/2,fwrite/3,fwrite_g/1,indentation/2,scan/2,unscan/1,
build/1, build/2]).
%% Format the arguments in Args after string Format. Just generate
%% an error if there is an error in the arguments.
@ -37,29 +38,55 @@
%% and it also splits the handling of the control characters into two
%% parts.
-spec fwrite(Format, Data) -> FormatList when
-spec fwrite(Format, Data) -> io_lib:chars() when
Format :: io:format(),
Data :: [term()],
FormatList :: [char() | eFmt:format_spec()].
Data :: [term()].
fwrite(Format, Args) ->
build(scan(Format, Args)).
-spec fwrite(Format, Data, Options) -> io_lib:chars() when
Format :: io:format(),
Data :: [term()],
Options :: [Option],
Option :: {'chars_limit', CharsLimit},
CharsLimit :: io_lib:chars_limit().
fwrite(Format, Args, Options) ->
build(scan(Format, Args), Options).
%% Build the output text for a pre-parsed format list.
-spec build(FormatList) -> eFmt:chars() when
FormatList :: [char() | eFmt:format_spec()].
-spec build(FormatList) -> io_lib:chars() when
FormatList :: [char() | io_lib:format_spec()].
build(Cs) ->
Pc = pcount(Cs),
build(Cs, Pc, 0).
build(Cs, []).
-spec build(FormatList, Options) -> io_lib:chars() when
FormatList :: [char() | io_lib:format_spec()],
Options :: [Option],
Option :: {'chars_limit', CharsLimit},
CharsLimit :: io_lib:chars_limit().
build(Cs, Options) ->
CharsLimit = get_option(chars_limit, Options, -1),
Res1 = build_small(Cs),
{P, S, W, Other} = count_small(Res1),
case P + S + W of
0 ->
Res1;
NumOfLimited ->
RemainingChars = sub(CharsLimit, Other),
build_limited(Res1, P, NumOfLimited, RemainingChars, 0)
end.
%% Parse all control sequences in the format string.
-spec scan(Format, Data) -> FormatList when
Format :: io:format(),
Data :: [term()],
FormatList :: [char() | eFmt:format_spec()].
FormatList :: [char() | io_lib:format_spec()].
scan(Format, Args) when is_atom(Format) ->
scan(atom_to_list(Format), Args);
@ -72,7 +99,7 @@ scan(Format, Args) ->
%% list of arguments.
-spec unscan(FormatList) -> {Format, Data} when
FormatList :: [char() | eFmt:format_spec()],
FormatList :: [char() | io_lib:format_spec()],
Format :: io:format(),
Data :: [term()].
@ -95,7 +122,7 @@ print([]) ->
[].
print(C, F, Ad, P, Pad, Encoding, Strings) ->
[$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
[$~] ++ print_field_width(F, Ad) ++ print_precision(P, Pad) ++
print_pad_char(Pad) ++ print_encoding(Encoding) ++
print_strings(Strings) ++ [C].
@ -103,8 +130,9 @@ print_field_width(none, _Ad) -> "";
print_field_width(F, left) -> integer_to_list(-F);
print_field_width(F, right) -> integer_to_list(F).
print_precision(none) -> "";
print_precision(P) -> [$. | integer_to_list(P)].
print_precision(none, $\s) -> "";
print_precision(none, _Pad) -> "."; % pad must be second dot
print_precision(P, _Pad) -> [$. | integer_to_list(P)].
print_pad_char($\s) -> ""; % default, no need to make explicit
print_pad_char(Pad) -> [$., Pad].
@ -126,25 +154,23 @@ collect_cseq(Fmt0, Args0) ->
{F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
{Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
{Strings,Fmt5,Args5} = strings(Fmt4, Args4),
{C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
FormatSpec = #{control_char => C, args => As, width => F, adjust => Ad,
precision => P, pad_char => Pad, encoding => Encoding,
strings => Strings},
{FormatSpec,Fmt6,Args6}.
encoding([$t|Fmt],Args) ->
true = hd(Fmt) =/= $l,
{unicode,Fmt,Args};
encoding(Fmt,Args) ->
{latin1,Fmt,Args}.
strings([$l|Fmt],Args) ->
true = hd(Fmt) =/= $t,
{false,Fmt,Args};
strings(Fmt,Args) ->
{true,Fmt,Args}.
Spec0 = #{width => F,
adjust => Ad,
precision => P,
pad_char => Pad,
encoding => latin1,
strings => true},
{Spec1,Fmt4} = modifiers(Fmt3, Spec0),
{C,As,Fmt5,Args4} = collect_cc(Fmt4, Args3),
Spec2 = Spec1#{control_char => C, args => As},
{Spec2,Fmt5,Args4}.
modifiers([$t|Fmt], Spec) ->
modifiers(Fmt, Spec#{encoding => unicode});
modifiers([$l|Fmt], Spec) ->
modifiers(Fmt, Spec#{strings => false});
modifiers(Fmt, Spec) ->
{Spec, Fmt}.
field_width([$-|Fmt0], Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
@ -203,45 +229,88 @@ collect_cc([$~|Fmt], Args) when is_list(Args) -> {$~,[],Fmt,Args};
collect_cc([$n|Fmt], Args) when is_list(Args) -> {$n,[],Fmt,Args};
collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
%% pcount([ControlC]) -> Count.
%% Count the number of print requests.
pcount(Cs) -> pcount(Cs, 0).
pcount([#{control_char := $p}|Cs], Acc) -> pcount(Cs, Acc+1);
pcount([#{control_char := $P}|Cs], Acc) -> pcount(Cs, Acc+1);
pcount([_|Cs], Acc) -> pcount(Cs, Acc);
pcount([], Acc) -> Acc.
%% build([Control], Pc, Indentation) -> eFmt:chars().
%% count_small([ControlC]) -> Count.
%% Count the number of big (pPwWsS) print requests and
%% number of characters of other print (small) requests.
count_small(Cs) ->
count_small(Cs, #{p => 0, s => 0, w => 0, other => 0}).
count_small([#{control_char := $p}|Cs], #{p := P} = Cnts) ->
count_small(Cs, Cnts#{p := P + 1});
count_small([#{control_char := $P}|Cs], #{p := P} = Cnts) ->
count_small(Cs, Cnts#{p := P + 1});
count_small([#{control_char := $w}|Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([#{control_char := $W}|Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([#{control_char := $s}|Cs], #{w := W} = Cnts) ->
count_small(Cs, Cnts#{w := W + 1});
count_small([S|Cs], #{other := Other} = Cnts) when is_list(S);
is_binary(S) ->
count_small(Cs, Cnts#{other := Other + io_lib:chars_length(S)});
count_small([C|Cs], #{other := Other} = Cnts) when is_integer(C) ->
count_small(Cs, Cnts#{other := Other + 1});
count_small([], #{p := P, s := S, w := W, other := Other}) ->
{P, S, W, Other}.
%% build_small([Control]) -> io_lib:chars().
%% Interpret the control structures, but only the small ones.
%% The big ones are saved for later.
%% build_limited([Control], NumberOfPps, NumberOfLimited,
%% CharsLimit, Indentation)
%% Interpret the control structures. Count the number of print
%% remaining and only calculate indentation when necessary. Must also
%% be smart when calculating indentation for characters in format.
build([#{control_char := C, args := As, width := F, adjust := Ad,
precision := P, pad_char := Pad, encoding := Enc,
strings := Str} | Cs], Pc0, I) ->
S = control(C, As, F, Ad, P, Pad, Enc, Str, I),
Pc1 = decr_pc(C, Pc0),
build_small([#{control_char := C, args := As, width := F, adjust := Ad,
precision := P, pad_char := Pad, encoding := Enc}=CC | Cs]) ->
case control_small(C, As, F, Ad, P, Pad, Enc) of
not_small -> [CC | build_small(Cs)];
S -> lists:flatten(S) ++ build_small(Cs)
end;
build_small([C|Cs]) -> [C|build_small(Cs)];
build_small([]) -> [].
build_limited([#{control_char := C, args := As, width := F, adjust := Ad,
precision := P, pad_char := Pad, encoding := Enc,
strings := Str} | Cs], NumOfPs0, Count0, MaxLen0, I) ->
MaxChars = if
MaxLen0 < 0 -> MaxLen0;
true -> MaxLen0 div Count0
end,
S = control_limited(C, As, F, Ad, P, Pad, Enc, Str, MaxChars, I),
NumOfPs = decr_pc(C, NumOfPs0),
Count = Count0 - 1,
MaxLen = if
MaxLen0 < 0 -> % optimization
MaxLen0;
true ->
Len = io_lib:chars_length(S),
sub(MaxLen0, Len)
end,
if
Pc1 > 0 -> [S|build(Cs, Pc1, indentation(S, I))];
true -> [S|build(Cs, Pc1, I)]
NumOfPs > 0 -> [S|build_limited(Cs, NumOfPs, Count,
MaxLen, indentation(S, I))];
true -> [S|build_limited(Cs, NumOfPs, Count, MaxLen, I)]
end;
build([$\n|Cs], Pc, _I) -> [$\n|build(Cs, Pc, 0)];
build([$\t|Cs], Pc, I) -> [$\t|build(Cs, Pc, ((I + 8) div 8) * 8)];
build([C|Cs], Pc, I) -> [C|build(Cs, Pc, I+1)];
build([], _Pc, _I) -> [].
build_limited([$\n|Cs], NumOfPs, Count, MaxLen, _I) ->
[$\n|build_limited(Cs, NumOfPs, Count, MaxLen, 0)];
build_limited([$\t|Cs], NumOfPs, Count, MaxLen, I) ->
[$\t|build_limited(Cs, NumOfPs, Count, MaxLen, ((I + 8) div 8) * 8)];
build_limited([C|Cs], NumOfPs, Count, MaxLen, I) ->
[C|build_limited(Cs, NumOfPs, Count, MaxLen, I+1)];
build_limited([], _, _, _, _) -> [].
decr_pc($p, Pc) -> Pc - 1;
decr_pc($P, Pc) -> Pc - 1;
decr_pc(_, Pc) -> Pc.
%% Calculate the indentation of the end of a string given its start
%% indentation. We assume tabs at 8 cols.
-spec indentation(String, StartIndent) -> integer() when
String :: eFmt:chars(),
String :: io_lib:chars(),
StartIndent :: integer().
indentation([$\n|Cs], _I) -> indentation(Cs, 0);
@ -252,67 +321,74 @@ indentation([C|Cs], I) ->
indentation(Cs, indentation(C, I));
indentation([], I) -> I.
%% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
%% Encoding, Indentation) -> String
%% This is the main dispatch function for the various formatting commands.
%% Field widths and precisions have already been calculated.
control($w, [A], F, Adj, P, Pad, Enc, _Str, _I) ->
term(eFmt:write(A, [{depth,-1}, {encoding, Enc}]), F, Adj, P, Pad);
control($p, [A], F, Adj, P, Pad, Enc, Str, I) ->
print(A, -1, F, Adj, P, Pad, Enc, Str, I);
control($W, [A,Depth], F, Adj, P, Pad, Enc, _Str, _I) when is_integer(Depth) ->
term(eFmt:write(A, [{depth,Depth}, {encoding, Enc}]), F, Adj, P, Pad);
control($P, [A,Depth], F, Adj, P, Pad, Enc, Str, I) when is_integer(Depth) ->
print(A, Depth, F, Adj, P, Pad, Enc, Str, I);
control($s, [A], F, Adj, P, Pad, latin1, _Str, _I) when is_atom(A) ->
%% control_small(FormatChar, [Argument], FieldWidth, Adjust, Precision,
%% PadChar, Encoding) -> String
%% control_limited(FormatChar, [Argument], FieldWidth, Adjust, Precision,
%% PadChar, Encoding, StringP, ChrsLim, Indentation) -> String
%% These are the dispatch functions for the various formatting controls.
control_small($s, [A], F, Adj, P, Pad, latin1=Enc) when is_atom(A) ->
L = iolist_to_chars(atom_to_list(A)),
string(L, F, Adj, P, Pad);
control($s, [A], F, Adj, P, Pad, unicode, _Str, _I) when is_atom(A) ->
string(atom_to_list(A), F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, latin1, _Str, _I) ->
L = iolist_to_chars(L0),
string(L, F, Adj, P, Pad);
control($s, [L0], F, Adj, P, Pad, unicode, _Str, _I) ->
L = cdata_to_chars(L0),
uniconv(string(L, F, Adj, P, Pad));
control($e, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
string(L, F, Adj, P, Pad, Enc);
control_small($s, [A], F, Adj, P, Pad, unicode=Enc) when is_atom(A) ->
string(atom_to_list(A), F, Adj, P, Pad, Enc);
control_small($e, [A], F, Adj, P, Pad, _Enc) when is_float(A) ->
fwrite_e(A, F, Adj, P, Pad);
control($f, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
control_small($f, [A], F, Adj, P, Pad, _Enc) when is_float(A) ->
fwrite_f(A, F, Adj, P, Pad);
control($g, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) ->
control_small($g, [A], F, Adj, P, Pad, _Enc) when is_float(A) ->
fwrite_g(A, F, Adj, P, Pad);
control($b, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
control_small($b, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
unprefixed_integer(A, F, Adj, base(P), Pad, true);
control($B, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
control_small($B, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
unprefixed_integer(A, F, Adj, base(P), Pad, false);
control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A),
is_atom(Prefix) ->
control_small($x, [A,Prefix], F, Adj, P, Pad, _Enc) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true);
control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
true = eFmt:deep_char_list(Prefix), %Check if Prefix a character list
control_small($x, [A,Prefix], F, Adj, P, Pad, _Enc) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true);
control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A),
is_atom(Prefix) ->
control_small($X, [A,Prefix], F, Adj, P, Pad, _Enc) when is_integer(A),
is_atom(Prefix) ->
prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false);
control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
true = eFmt:deep_char_list(Prefix), %Check if Prefix a character list
control_small($X, [A,Prefix], F, Adj, P, Pad, _Enc) when is_integer(A) ->
true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false);
control($+, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
control_small($+, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
prefixed_integer(A, F, Adj, Base, Pad, Prefix, true);
control($#, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
control_small($#, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
Base = base(P),
Prefix = [integer_to_list(Base), $#],
prefixed_integer(A, F, Adj, Base, Pad, Prefix, false);
control($c, [A], F, Adj, P, Pad, unicode, _Str, _I) when is_integer(A) ->
control_small($c, [A], F, Adj, P, Pad, unicode) when is_integer(A) ->
char(A, F, Adj, P, Pad);
control($c, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) ->
control_small($c, [A], F, Adj, P, Pad, _Enc) when is_integer(A) ->
char(A band 255, F, Adj, P, Pad);
control($~, [], F, Adj, P, Pad, _Enc, _Str, _I) -> char($~, F, Adj, P, Pad);
control($n, [], F, Adj, P, Pad, _Enc, _Str, _I) -> newline(F, Adj, P, Pad);
control($i, [_A], _F, _Adj, _P, _Pad, _Enc, _Str, _I) -> [].
control_small($~, [], F, Adj, P, Pad, _Enc) -> char($~, F, Adj, P, Pad);
control_small($n, [], F, Adj, P, Pad, _Enc) -> newline(F, Adj, P, Pad);
control_small($i, [_A], _F, _Adj, _P, _Pad, _Enc) -> [];
control_small(_C, _As, _F, _Adj, _P, _Pad, _Enc) -> not_small.
control_limited($s, [L0], F, Adj, P, Pad, latin1=Enc, _Str, CL, _I) ->
L = iolist_to_chars(L0, F, CL),
string(L, limit_field(F, CL), Adj, P, Pad, Enc);
control_limited($s, [L0], F, Adj, P, Pad, unicode=Enc, _Str, CL, _I) ->
L = cdata_to_chars(L0, F, CL),
uniconv(string(L, limit_field(F, CL), Adj, P, Pad, Enc));
control_limited($w, [A], F, Adj, P, Pad, Enc, _Str, CL, _I) ->
Chars = io_lib:write(A, [{depth, -1}, {encoding, Enc}, {chars_limit, CL}]),
term(Chars, F, Adj, P, Pad);
control_limited($p, [A], F, Adj, P, Pad, Enc, Str, CL, I) ->
print(A, -1, F, Adj, P, Pad, Enc, Str, CL, I);
control_limited($W, [A,Depth], F, Adj, P, Pad, Enc, _Str, CL, _I)
when is_integer(Depth) ->
Chars = io_lib:write(A, [{depth, Depth}, {encoding, Enc}, {chars_limit, CL}]),
term(Chars, F, Adj, P, Pad);
control_limited($P, [A,Depth], F, Adj, P, Pad, Enc, Str, CL, I)
when is_integer(Depth) ->
print(A, Depth, F, Adj, P, Pad, Enc, Str, CL, I).
-ifdef(UNICODE_AS_BINARIES).
uniconv(C) ->
@ -335,7 +411,7 @@ base(B) when is_integer(B) ->
term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
L = lists:flatlength(T),
L = io_lib:chars_length(T),
P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),
if
L > P ->
@ -349,12 +425,13 @@ term(T, F, Adj, P0, Pad) ->
%% Print a term. Field width sets maximum line length, Precision sets
%% initial indentation.
print(T, D, none, Adj, P, Pad, E, Str, I) ->
print(T, D, 80, Adj, P, Pad, E, Str, I);
print(T, D, F, Adj, none, Pad, E, Str, I) ->
print(T, D, F, Adj, I+1, Pad, E, Str, I);
print(T, D, F, right, P, _Pad, Enc, Str, _I) ->
Options = [{column, P},
print(T, D, none, Adj, P, Pad, E, Str, ChLim, I) ->
print(T, D, 80, Adj, P, Pad, E, Str, ChLim, I);
print(T, D, F, Adj, none, Pad, E, Str, ChLim, I) ->
print(T, D, F, Adj, I+1, Pad, E, Str, ChLim, I);
print(T, D, F, right, P, _Pad, Enc, Str, ChLim, _I) ->
Options = [{chars_limit, ChLim},
{column, P},
{line_length, F},
{depth, D},
{encoding, Enc},
@ -380,7 +457,7 @@ float_e(_Fl, {Ds,E}, P) ->
{Fs,false} -> [Fs|float_exp(E-1)]
end.
%% float_man([Digit], Icount, Dcount) -> {[Chars],CarryFlag}.
%% float_man([Digit], Icount, Dcount) -> {[Char],CarryFlag}.
%% Generate the characters in the mantissa from the digits with Icount
%% characters before the '.' and Dcount decimals. Handle carry and let
%% caller decide what to do at top.
@ -395,7 +472,7 @@ float_man([D|Ds], I, Dc) ->
{Cs,false} -> {[D|Cs],false}
end;
float_man([], I, Dc) -> %Pad with 0's
{string:chars($0, I, [$.|string:chars($0, Dc)]),false}.
{lists:duplicate(I, $0) ++ [$.|lists:duplicate(Dc, $0)],false}.
float_man([D|_], 0) when D >= $5 -> {[],true};
float_man([_|_], 0) -> {[],false};
@ -405,7 +482,7 @@ float_man([D|Ds], Dc) ->
{Cs,true} -> {[D+1|Cs],false};
{Cs,false} -> {[D|Cs],false}
end;
float_man([], Dc) -> {string:chars($0, Dc),false}. %Pad with 0's
float_man([], Dc) -> {lists:duplicate(Dc, $0),false}. %Pad with 0's
%% float_exp(Exponent) -> [Char].
%% Generate the exponent of a floating point number. Always include sign.
@ -429,7 +506,7 @@ fwrite_f(Fl, F, Adj, P, Pad) when P >= 1 ->
float_f(Fl, Fd, P) when Fl < 0.0 ->
[$-|float_f(-Fl, Fd, P)];
float_f(Fl, {Ds,E}, P) when E =< 0 ->
float_f(Fl, {string:chars($0, -E+1, Ds),1}, P); %Prepend enough 0's
float_f(Fl, {lists:duplicate(-E+1, $0)++Ds,1}, P); %Prepend enough 0's
float_f(_Fl, {Ds,E}, P) ->
case float_man(Ds, E, P) of
{Fs,true} -> "1" ++ Fs; %Handle carry
@ -641,7 +718,10 @@ fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 ->
end.
%% iolist_to_chars(iolist()) -> deep_char_list()
iolist_to_chars(Cs, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F ->
iolist_to_chars(Cs);
iolist_to_chars(Cs, _, CharsLimit) ->
limit_iolist_to_chars(Cs, sub(CharsLimit, 3), [], normal). % three dots
iolist_to_chars([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 ->
[C | iolist_to_chars(Cs)];
@ -652,12 +732,34 @@ iolist_to_chars([]) ->
iolist_to_chars(B) when is_binary(B) ->
binary_to_list(B).
%% cdata() :: clist() | cbinary()
%% clist() :: maybe_improper_list(char() | cbinary() | clist(),
%% cbinary() | nil())
%% cbinary() :: unicode:unicode_binary() | unicode:latin1_binary()
limit_iolist_to_chars(Cs, 0, S, normal) ->
L = limit_iolist_to_chars(Cs, 4, S, final),
case iolist_size(L) of
N when N < 4 -> L;
4 -> "..."
end;
limit_iolist_to_chars(_Cs, 0, _S, final) -> [];
limit_iolist_to_chars([C|Cs], Limit, S, Mode) when C >= $\000, C =< $\377 ->
[C | limit_iolist_to_chars(Cs, Limit - 1, S, Mode)];
limit_iolist_to_chars([I|Cs], Limit, S, Mode) ->
limit_iolist_to_chars(I, Limit, [Cs|S], Mode);
limit_iolist_to_chars([], _Limit, [], _Mode) ->
[];
limit_iolist_to_chars([], Limit, [Cs|S], Mode) ->
limit_iolist_to_chars(Cs, Limit, S, Mode);
limit_iolist_to_chars(B, Limit, S, Mode) when is_binary(B) ->
case byte_size(B) of
Sz when Sz > Limit ->
{B1, B2} = split_binary(B, Limit),
[binary_to_list(B1) | limit_iolist_to_chars(B2, 0, S, Mode)];
Sz ->
[binary_to_list(B) | limit_iolist_to_chars([], Limit-Sz, S, Mode)]
end.
%% cdata_to_chars(cdata()) -> eFmt:deep_char_list()
cdata_to_chars(Cs, F, CharsLimit) when CharsLimit < 0; CharsLimit >= F ->
cdata_to_chars(Cs);
cdata_to_chars(Cs, _, CharsLimit) ->
limit_cdata_to_chars(Cs, sub(CharsLimit, 3), normal). % three dots
cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 ->
[C | cdata_to_chars(Cs)];
@ -671,32 +773,58 @@ cdata_to_chars(B) when is_binary(B) ->
_ -> binary_to_list(B)
end.
limit_cdata_to_chars(Cs, 0, normal) ->
L = limit_cdata_to_chars(Cs, 4, final),
case string:length(L) of
N when N < 4 -> L;
4 -> "..."
end;
limit_cdata_to_chars(_Cs, 0, final) -> [];
limit_cdata_to_chars(Cs, Limit, Mode) ->
case string:next_grapheme(Cs) of
{error, <<C,Cs1/binary>>} ->
%% This is how ~ts handles Latin1 binaries with option
%% chars_limit.
[C | limit_cdata_to_chars(Cs1, Limit - 1, Mode)];
{error, [C|Cs1]} -> % not all versions of module string return this
[C | limit_cdata_to_chars(Cs1, Limit - 1, Mode)];
[] ->
[];
[GC|Cs1] ->
[GC | limit_cdata_to_chars(Cs1, Limit - 1, Mode)]
end.
limit_field(F, CharsLimit) when CharsLimit < 0; F =:= none ->
F;
limit_field(F, CharsLimit) ->
max(3, min(F, CharsLimit)).
%% string(String, Field, Adjust, Precision, PadChar)
string(S, none, _Adj, none, _Pad) -> S;
string(S, F, Adj, none, Pad) ->
string_field(S, F, Adj, lists:flatlength(S), Pad);
string(S, none, _Adj, P, Pad) ->
string_field(S, P, left, lists:flatlength(S), Pad);
string(S, F, Adj, P, Pad) when F >= P ->
N = lists:flatlength(S),
string(S, none, _Adj, none, _Pad, _Enc) -> S;
string(S, F, Adj, none, Pad, Enc) ->
string_field(S, F, Adj, io_lib:chars_length(S), Pad, Enc);
string(S, none, _Adj, P, Pad, Enc) ->
string_field(S, P, left, io_lib:chars_length(S), Pad, Enc);
string(S, F, Adj, P, Pad, Enc) when F >= P ->
N = io_lib:chars_length(S),
if F > P ->
if N > P ->
adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
adjust(flat_trunc(S, P, Enc), chars(Pad, F-P), Adj);
N < P ->
adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
true -> % N == P
adjust(S, chars(Pad, F-P), Adj)
end;
true -> % F == P
string_field(S, F, Adj, N, Pad)
string_field(S, F, Adj, N, Pad, Enc)
end.
string_field(S, F, _Adj, N, _Pad) when N > F ->
flat_trunc(S, F);
string_field(S, F, Adj, N, Pad) when N < F ->
string_field(S, F, _Adj, N, _Pad, Enc) when N > F ->
flat_trunc(S, F, Enc);
string_field(S, F, Adj, N, Pad, _Enc) when N < F ->
adjust(S, chars(Pad, F-N), Adj);
string_field(S, _, _, _, _) -> % N == F
string_field(S, _, _, _, _, _) -> % N == F
S.
%% unprefixed_integer(Int, Field, Adjust, Base, PadChar, Lowercase)
@ -748,21 +876,13 @@ adjust(Data, Pad, right) -> [Pad|Data].
%% Flatten and truncate a deep list to at most N elements.
flat_trunc(List, N) when is_integer(N), N >= 0 ->
flat_trunc(List, N, [], []).
flat_trunc(L, 0, _, R) when is_list(L) ->
lists:reverse(R);
flat_trunc([H|T], N, S, R) when is_list(H) ->
flat_trunc(H, N, [T|S], R);
flat_trunc([H|T], N, S, R) ->
flat_trunc(T, N-1, S, [H|R]);
flat_trunc([], N, [H|S], R) ->
flat_trunc(H, N, S, R);
flat_trunc([], _, [], R) ->
lists:reverse(R).
flat_trunc(List, N, latin1) when is_integer(N), N >= 0 ->
{S, _} = lists:split(N, lists:flatten(List)),
S;
flat_trunc(List, N, unicode) when is_integer(N), N >= 0 ->
string:slice(List, 0, N).
%% A deep version of string:chars/2,3
%% A deep version of lists:duplicate/2
chars(_C, 0) ->
[];
@ -795,3 +915,15 @@ lowercase([H|T]) ->
[H|lowercase(T)];
lowercase([]) ->
[].
%% Make sure T does change sign.
sub(T, _) when T < 0 -> T;
sub(T, E) when T >= E -> T - E;
sub(_, _) -> 0.
get_option(Key, TupleList, Default) ->
case lists:keyfind(Key, 1, TupleList) of
false -> Default;
{Key, Value} -> Value;
_ -> Default
end.

Caricamento…
Annulla
Salva