1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(io_lib_pretty).
21
22%%% Pretty printing Erlang terms
23%%%
24%%% In this module "print" means the formatted printing while "write"
25%%% means just writing out onto one line.
26
27-export([print/1,print/2,print/3,print/4,print/5,print/6]).
28
29%% To be used by io_lib only.
30-export([intermediate/6, write/1]).
31
32%%%
33%%% Exported functions
34%%%
35
36%% print(Term) -> [Chars]
37%% print(Term, Column, LineLength, Depth) -> [Chars]
38%% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms.
39
40-spec print(term()) -> io_lib:chars().
41
42print(Term) ->
43    print(Term, 1, 80, -1).
44
45%% print(Term, RecDefFun) -> [Chars]
46%% print(Term, Depth, RecDefFun) -> [Chars]
47%% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no
48%% Used by the shell for printing records and for Unicode.
49
50-type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) ->
51                                  'no' | [FieldName :: atom()]).
52-type column() :: integer().
53-type encoding() :: epp:source_encoding() | 'unicode'.
54-type line_length() :: pos_integer().
55-type depth() :: integer().
56-type line_max_chars() :: integer().
57-type chars_limit() :: integer().
58
59-type chars() :: io_lib:chars().
60-type option() :: {'chars_limit', chars_limit()}
61                | {'column', column()}
62                | {'depth', depth()}
63                | {'encoding', encoding()}
64                | {'line_length', line_length()}
65                | {'line_max_chars', line_max_chars()}
66                | {'record_print_fun', rec_print_fun()}
67                | {'strings', boolean()}.
68-type options() :: [option()].
69
70-spec print(term(), rec_print_fun()) -> chars();
71           (term(), options()) -> chars().
72
73print(Term, Options) when is_list(Options) ->
74    Col = get_option(column, Options, 1),
75    Ll = get_option(line_length, Options, 80),
76    D = get_option(depth, Options, -1),
77    M = get_option(line_max_chars, Options, -1),
78    T = get_option(chars_limit, Options, -1),
79    RecDefFun = get_option(record_print_fun, Options, no_fun),
80    Encoding = get_option(encoding, Options, epp:default_encoding()),
81    Strings = get_option(strings, Options, true),
82    print(Term, Col, Ll, D, M, T, RecDefFun, Encoding, Strings);
83print(Term, RecDefFun) ->
84    print(Term, -1, RecDefFun).
85
86-spec print(term(), depth(), rec_print_fun()) -> chars().
87
88print(Term, Depth, RecDefFun) ->
89    print(Term, 1, 80, Depth, RecDefFun).
90
91-spec print(term(), column(), line_length(), depth()) -> chars().
92
93print(Term, Col, Ll, D) ->
94    print(Term, Col, Ll, D, _M=-1, _T=-1, no_fun, latin1, true).
95
96-spec print(term(), column(), line_length(), depth(), rec_print_fun()) ->
97                   chars().
98print(Term, Col, Ll, D, RecDefFun) ->
99    print(Term, Col, Ll, D, _M=-1, RecDefFun).
100
101-spec print(term(), column(), line_length(), depth(), line_max_chars(),
102            rec_print_fun()) -> chars().
103
104print(Term, Col, Ll, D, M, RecDefFun) ->
105    print(Term, Col, Ll, D, M, _T=-1, RecDefFun, latin1, true).
106
107%% D = Depth, default -1 (infinite), or LINEMAX=30 when printing from shell
108%% T = chars_limit, that is, maximal number of characters, default -1
109%%   Used together with D to limit the output. It is possible that
110%%   more than T characters are returned.
111%% Col = current column, default 1
112%% Ll = line length/~p field width, default 80
113%% M = CHAR_MAX (-1 if no max, 60 when printing from shell)
114print(_, _, _, 0, _M, _T, _RF, _Enc, _Str) -> "...";
115print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "...";
116print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 ->
117    %% ensure Col is at least 1
118    print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str);
119print(Atom, _Col, _Ll, _D, _M, _T, _RF, Enc, _Str) when is_atom(Atom) ->
120    write_atom(Atom, Enc);
121print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term);
122                                                         is_list(Term);
123                                                         is_map(Term);
124                                                         is_bitstring(Term) ->
125    %% preprocess and compute total number of chars
126    {_, Len, _Dots, _} = If =
127        case T < 0 of
128            true -> print_length(Term, D, T, RecDefFun, Enc, Str);
129            false -> intermediate(Term, D, T, RecDefFun, Enc, Str)
130        end,
131    %% use Len as CHAR_MAX if M0 = -1
132    M = max_cs(M0, Len),
133    if
134        Ll =:= 0 ->
135            write(If);
136        Len < Ll - Col, Len =< M ->
137            %% write the whole thing on a single line when there is room
138            write(If);
139        true ->
140            %% compute the indentation TInd for tagged tuples and records
141            TInd = while_fail([-1, 4],
142                              fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end,
143                              1),
144            pp(If, Col, Ll, M, TInd, indent(Col), 0, 0)
145    end;
146print(Term, _Col, _Ll, _D, _M, _T, _RF, _Enc, _Str) ->
147    %% atomic data types (bignums, atoms, ...) are never truncated
148    io_lib:write(Term).
149
150%%%
151%%% Local functions
152%%%
153
154%% use M only if nonnegative, otherwise use Len as default value
155max_cs(M, Len) when M < 0 ->
156    Len;
157max_cs(M, _Len) ->
158    M.
159
160-define(ATM(T), is_list(element(1, T))).
161-define(ATM_PAIR(Pair),
162        ?ATM(element(2, element(1, Pair))) % Key
163        andalso
164        ?ATM(element(3, element(1, Pair)))). % Value
165-define(ATM_FLD(Field), ?ATM(element(4, element(1, Field)))).
166
167pp({_S,Len,_,_} = If, Col, Ll, M, _TInd, _Ind, LD, W)
168                      when Len < Ll - Col - LD, Len + W + LD =< M ->
169    write(If);
170pp({{list,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) ->
171    [$[, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $|, W + 1), $]];
172pp({{tuple,true,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) ->
173    [${, pp_tag_tuple(L, Col, Ll, M, TInd, Ind, LD, W + 1), $}];
174pp({{tuple,false,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) ->
175    [${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}];
176pp({{map,Pairs}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) ->
177    [$#, ${, pp_map(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, W + 1),
178     $}];
179pp({{record,[{Name,NLen} | L]}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) ->
180    [Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen+1), $}];
181pp({{bin,S}, _Len, _, _}, Col, Ll, M, _TInd, Ind, LD, W) ->
182    pp_binary(S, Col + 2, Ll, M, indent(2, Ind), LD, W);
183pp({S,_Len,_,_}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
184    S.
185
186%%  Print a tagged tuple by indenting the rest of the elements
187%%  differently to the tag. Tuple has size >= 2.
188pp_tag_tuple({dots, _, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
189    "...";
190pp_tag_tuple([{Tag,Tlen,_,_} | L], Col, Ll, M, TInd, Ind, LD, W) ->
191    %% this uses TInd
192    TagInd = Tlen + 2,
193    Tcol = Col + TagInd,
194    S = $,,
195    if
196        TInd > 0, TagInd > TInd ->
197            Col1 = Col + TInd,
198            Indent = indent(TInd, Ind),
199            [Tag|pp_tail(L, Col1, Tcol, Ll, M, TInd, Indent, LD, S, W+Tlen)];
200        true ->
201            Indent = indent(TagInd, Ind),
202            [Tag, S | pp_list(L, Tcol, Ll, M, TInd, Indent, LD, S, W+Tlen+1)]
203    end.
204
205pp_map([], _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
206    "";                                         % cannot happen
207pp_map({dots, _, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
208    "...";                                      % cannot happen
209pp_map([P | Ps], Col, Ll, M, TInd, Ind, LD, W) ->
210    {PS, PW} = pp_pair(P, Col, Ll, M, TInd, Ind, last_depth(Ps, LD), W),
211    [PS | pp_pairs_tail(Ps, Col, Col + PW, Ll, M, TInd, Ind, LD, PW)].
212
213pp_pairs_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
214    "";
215pp_pairs_tail({dots, _, _, _}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) ->
216    ",...";
217pp_pairs_tail([{_, Len, _, _}=P | Ps], Col0, Col, Ll, M, TInd, Ind, LD, W) ->
218    LD1 = last_depth(Ps, LD),
219    ELen = 1 + Len,
220    if
221        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P);
222        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) ->
223            [$,, write_pair(P) |
224             pp_pairs_tail(Ps, Col0, Col+ELen, Ll, M, TInd, Ind, LD, W+ELen)];
225        true ->
226            {PS, PW} = pp_pair(P, Col0, Ll, M, TInd, Ind, LD1, 0),
227            [$,, $\n, Ind, PS |
228             pp_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, TInd, Ind, LD, PW)]
229    end.
230
231pp_pair({_, Len, _, _}=Pair, Col, Ll, M, _TInd, _Ind, LD, W)
232         when Len < Ll - Col - LD, Len + W + LD =< M ->
233    {write_pair(Pair), if
234                          ?ATM_PAIR(Pair) ->
235                              Len;
236                          true ->
237                              Ll % force nl
238                      end};
239pp_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, TInd, Ind0, LD, W) ->
240    I = map_value_indent(TInd),
241    Ind = indent(I, Ind0),
242    {[pp(K, Col0, Ll, M, TInd, Ind0, LD, W), " =>\n",
243      Ind | pp(V, Col0 + I, Ll, M, TInd, Ind, LD, 0)], Ll}. % force nl
244
245pp_record([], _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
246    "";
247pp_record({dots, _, _, _}, _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
248    "...";
249pp_record([F | Fs], Nlen, Col0, Ll, M, TInd, Ind0, LD, W0) ->
250    Nind = Nlen + 1,
251    {Col, Ind, S, W} = rec_indent(Nind, TInd, Col0, Ind0, W0),
252    {FS, FW} = pp_field(F, Col, Ll, M, TInd, Ind, last_depth(Fs, LD), W),
253    [S, FS | pp_fields_tail(Fs, Col, Col + FW, Ll, M, TInd, Ind, LD, W + FW)].
254
255pp_fields_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) ->
256    "";
257pp_fields_tail({dots, _, _ ,_}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) ->
258    ",...";
259pp_fields_tail([{_, Len, _, _}=F | Fs], Col0, Col, Ll, M, TInd, Ind, LD, W) ->
260    LD1 = last_depth(Fs, LD),
261    ELen = 1 + Len,
262    if
263        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F);
264        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) ->
265            [$,, write_field(F) |
266             pp_fields_tail(Fs, Col0, Col+ELen, Ll, M, TInd, Ind, LD, W+ELen)];
267        true ->
268            {FS, FW} = pp_field(F, Col0, Ll, M, TInd, Ind, LD1, 0),
269            [$,, $\n, Ind, FS |
270             pp_fields_tail(Fs, Col0, Col0 + FW, Ll, M, TInd, Ind, LD, FW)]
271    end.
272
273pp_field({_, Len, _, _}=Fl, Col, Ll, M, _TInd, _Ind, LD, W)
274         when Len < Ll - Col - LD, Len + W + LD =< M ->
275    {write_field(Fl), if
276                          ?ATM_FLD(Fl) ->
277                              Len;
278                          true ->
279                              Ll % force nl
280                      end};
281pp_field({{field, Name, NameL, F},_,_, _}, Col0, Ll, M, TInd, Ind0, LD, W0) ->
282    {Col, Ind, S, W} = rec_indent(NameL, TInd, Col0, Ind0, W0 + NameL),
283    Sep = case S of
284              [$\n | _] -> " =";
285              _ -> " = "
286          end,
287    {[Name, Sep, S | pp(F, Col, Ll, M, TInd, Ind, LD, W)], Ll}. % force nl
288
289rec_indent(RInd, TInd, Col0, Ind0, W0) ->
290    %% this uses TInd
291    Nl = (TInd > 0) and (RInd > TInd),
292    DCol = case Nl of
293               true -> TInd;
294               false -> RInd
295           end,
296    Col = Col0 + DCol,
297    Ind = indent(DCol, Ind0),
298    S = case Nl of
299            true -> [$\n | Ind];
300            false -> ""
301        end,
302    W = case Nl of
303            true -> 0;
304            false -> W0
305        end,
306    {Col, Ind, S, W}.
307
308pp_list({dots, _, _, _}, _Col0, _Ll, _M, _TInd, _Ind, _LD, _S, _W) ->
309    "...";
310pp_list([E | Es], Col0, Ll, M, TInd, Ind, LD, S, W) ->
311    {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, last_depth(Es, LD), W),
312    [ES | pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, W + WE)].
313
314pp_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _S, _W) ->
315    [];
316pp_tail([{_, Len, _, _}=E | Es], Col0, Col, Ll, M, TInd, Ind, LD, S, W) ->
317    LD1 = last_depth(Es, LD),
318    ELen = 1 + Len,
319    if
320        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E);
321        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) ->
322            [$,, write(E) |
323             pp_tail(Es, Col0, Col + ELen, Ll, M, TInd, Ind, LD, S, W+ELen)];
324        true ->
325            {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, LD1, 0),
326            [$,, $\n, Ind, ES |
327             pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, WE)]
328    end;
329pp_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, S, _W) ->
330    [S | "..."];
331pp_tail({_, Len, _, _}=E, _Col0, Col, Ll, M, _TInd, _Ind, LD, S, W)
332                  when Len + 1 < Ll - Col - (LD + 1),
333                       Len + 1 + W + (LD + 1) =< M,
334                       ?ATM(E) ->
335    [S | write(E)];
336pp_tail(E, Col0, _Col, Ll, M, TInd, Ind, LD, S, _W) ->
337    [S, $\n, Ind | pp(E, Col0, Ll, M, TInd, Ind, LD + 1, 0)].
338
339pp_element({_, Len, _, _}=E, Col, Ll, M, _TInd, _Ind, LD, W)
340           when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) ->
341    {write(E), Len};
342pp_element(E, Col, Ll, M, TInd, Ind, LD, W) ->
343    {pp(E, Col, Ll, M, TInd, Ind, LD, W), Ll}. % force nl
344
345%% Reuse the list created by io_lib:write_binary()...
346pp_binary([LT,LT,S,GT,GT], Col, Ll, M, Ind, LD, W) ->
347    N = erlang:max(8, erlang:min(Ll - Col, M - 4 - W) - LD),
348    [LT,LT,pp_binary(S, N, N, Ind),GT,GT].
349
350pp_binary([BS, $, | S], N, N0, Ind) ->
351    Len = length(BS) + 1,
352    case N - Len of
353        N1 when N1 < 0 ->
354            [$\n, Ind, BS, $, | pp_binary(S, N0 - Len, N0, Ind)];
355        N1 ->
356            [BS, $, | pp_binary(S, N1, N0, Ind)]
357    end;
358pp_binary([BS1, $:, BS2]=S, N, _N0, Ind)
359         when length(BS1) + length(BS2) + 1 > N ->
360    [$\n, Ind, S];
361pp_binary(S, N, _N0, Ind) ->
362    case iolist_size(S) > N of
363        true ->
364            [$\n, Ind, S];
365        false ->
366            S
367    end.
368
369%% write the whole thing on a single line
370write({{tuple, _IsTagged, L}, _, _, _}) ->
371    [${, write_list(L, $,), $}];
372write({{list, L}, _, _, _}) ->
373    [$[, write_list(L, $|), $]];
374write({{map, Pairs}, _, _, _}) ->
375    [$#,${, write_list(Pairs, $,), $}];
376write({{map_pair, _K, _V}, _, _, _}=Pair) ->
377    write_pair(Pair);
378write({{record, [{Name,_} | L]}, _, _, _}) ->
379    [Name, ${, write_fields(L), $}];
380write({{bin, S}, _, _, _}) ->
381    S;
382write({S, _, _, _}) ->
383    S.
384
385write_pair({{map_pair, K, V}, _, _, _}) ->
386    [write(K), " => ", write(V)].
387
388write_fields([]) ->
389    "";
390write_fields({dots, _, _, _}) ->
391    "...";
392write_fields([F | Fs]) ->
393    [write_field(F) | write_fields_tail(Fs)].
394
395write_fields_tail([]) ->
396    "";
397write_fields_tail({dots, _, _, _}) ->
398    ",...";
399write_fields_tail([F | Fs]) ->
400    [$,, write_field(F) | write_fields_tail(Fs)].
401
402write_field({{field, Name, _NameL, F}, _, _, _}) ->
403    [Name, " = " | write(F)].
404
405write_list({dots, _, _, _}, _S) ->
406    "...";
407write_list([E | Es], S) ->
408    [write(E) | write_tail(Es, S)].
409
410write_tail([], _S) ->
411    [];
412write_tail([E | Es], S) ->
413    [$,, write(E) | write_tail(Es, S)];
414write_tail({dots, _, _, _}, S) ->
415    [S | "..."];
416write_tail(E, S) ->
417    [S | write(E)].
418
419-type more() :: fun((chars_limit(), DeltaDepth :: non_neg_integer()) ->
420                            intermediate_format()).
421
422-type if_list() :: maybe_improper_list(intermediate_format(),
423                                       {'dots', non_neg_integer(),
424                                        non_neg_integer(), more()}).
425
426-type intermediate_format() ::
427        {chars()
428         | {'bin', chars()}
429         | 'dots'
430         | {'field', Name :: chars(), NameLen :: non_neg_integer(),
431                     intermediate_format()}
432         | {'list', if_list()}
433         | {'map', if_list()}
434         | {'map_pair', K :: intermediate_format(),
435                        V :: intermediate_format()}
436         | {'record', [{Name :: chars(), NameLen :: non_neg_integer()}
437                       | if_list()]}
438         | {'tuple', IsTagged :: boolean(), if_list()},
439         Len :: non_neg_integer(),
440         NumOfDots :: non_neg_integer(),
441         More :: more() | 'no_more'
442        }.
443
444-spec intermediate(term(), depth(), pos_integer(), rec_print_fun(),
445                   encoding(), boolean()) -> intermediate_format().
446
447intermediate(Term, D, T, RF, Enc, Str) when T > 0 ->
448    D0 = 1,
449    If = print_length(Term, D0, T, RF, Enc, Str),
450    case If of
451        {_, Len, Dots, _} when Dots =:= 0; Len > T; D =:= 1 ->
452            If;
453        {_, Len, _, _} ->
454            find_upper(If, Term, T, D0, 2, D, RF, Enc, Str, Len)
455    end.
456
457find_upper(Lower, Term, T, Dl, Dd, D, RF, Enc, Str, LastLen) ->
458    Dd2 = Dd * 2,
459    D1 = case D < 0 of
460             true -> Dl + Dd2;
461             false -> min(Dl + Dd2, D)
462         end,
463    If = expand(Lower, T, D1 - Dl),
464    case If of
465        {_, _, _Dots=0, _} -> % even if Len > T
466            If;
467        {_, LastLen, _, _} ->
468            %% Cannot happen if print_length() is free of bugs.
469            If;
470        {_, Len, _, _} when Len =< T, D1 < D orelse D < 0 ->
471	    find_upper(If, Term, T, D1, Dd2, D, RF, Enc, Str, Len);
472        _ ->
473	    search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str)
474    end.
475
476%% Lower has NumOfDots > 0 and Len =< T.
477%% Upper has NumOfDots > 0 and Len > T.
478search_depth(Lower, Upper, _Term, T, Dl, Du, _RF, _Enc, _Str)
479        when Du - Dl =:= 1 ->
480    %% The returned intermediate format has Len >= T.
481    case Lower of
482        {_, T, _, _} ->
483            Lower;
484        _ ->
485            Upper
486    end;
487search_depth(Lower, Upper, Term, T, Dl, Du, RF, Enc, Str) ->
488    D1 = (Dl  + Du) div 2,
489    If = expand(Lower, T, D1 - Dl),
490    case If of
491	{_, Len, _, _} when Len > T ->
492            %% Len can be greater than Upper's length.
493            %% This is a bit expensive since the work to
494            %% crate Upper is wasted. It is the price
495            %% to pay to get a more balanced output.
496            search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str);
497        _ ->
498            search_depth(If, Upper, Term, T, D1, Du, RF, Enc, Str)
499    end.
500
501%% The depth (D) is used for extracting and counting the characters to
502%% print. The structure is kept so that the returned intermediate
503%% format can be formatted. The separators (list, tuple, record, map) are
504%% counted but need to be added later.
505
506%% D =/= 0
507print_length([], _D, _T, _RF, _Enc, _Str) ->
508    {"[]", 2, 0, no_more};
509print_length({}, _D, _T, _RF, _Enc, _Str) ->
510    {"{}", 2, 0, no_more};
511print_length(#{}=M, _D, _T, _RF, _Enc, _Str) when map_size(M) =:= 0 ->
512    {"#{}", 3, 0, no_more};
513print_length(Atom, _D, _T, _RF, Enc, _Str) when is_atom(Atom) ->
514    S = write_atom(Atom, Enc),
515    {S, io_lib:chars_length(S), 0, no_more};
516print_length(List, D, T, RF, Enc, Str) when is_list(List) ->
517    %% only flat lists are "printable"
518    case Str andalso printable_list(List, D, T, Enc) of
519        true ->
520            %% print as string, escaping double-quotes in the list
521            S = write_string(List, Enc),
522            {S, io_lib:chars_length(S), 0, no_more};
523        {true, Prefix} ->
524            %% Truncated lists when T < 0 could break some existing code.
525            S = write_string(Prefix, Enc),
526            %% NumOfDots = 0 to avoid looping--increasing the depth
527            %% does not make Prefix longer.
528            {[S | "..."], 3 + io_lib:chars_length(S), 0, no_more};
529        false ->
530            case print_length_list(List, D, T, RF, Enc, Str) of
531                {What, Len, Dots, _More} when Dots > 0 ->
532                    More = fun(T1, Dd) ->
533                                   ?FUNCTION_NAME(List, D+Dd, T1, RF, Enc, Str)
534                           end,
535                    {What, Len, Dots, More};
536                If ->
537                    If
538            end
539    end;
540print_length(Fun, _D, _T, _RF, _Enc, _Str) when is_function(Fun) ->
541    S = io_lib:write(Fun),
542    {S, iolist_size(S), 0, no_more};
543print_length(R, D, T, RF, Enc, Str) when is_atom(element(1, R)),
544                                         is_function(RF) ->
545    case RF(element(1, R), tuple_size(R) - 1) of
546        no ->
547            print_length_tuple(R, D, T, RF, Enc, Str);
548        RDefs ->
549            print_length_record(R, D, T, RF, RDefs, Enc, Str)
550    end;
551print_length(Tuple, D, T, RF, Enc, Str) when is_tuple(Tuple) ->
552    print_length_tuple(Tuple, D, T, RF, Enc, Str);
553print_length(Map, D, T, RF, Enc, Str) when is_map(Map) ->
554    print_length_map(Map, D, T, RF, Enc, Str);
555print_length(<<>>, _D, _T, _RF, _Enc, _Str) ->
556    {"<<>>", 4, 0, no_more};
557print_length(<<_/bitstring>> = Bin, 1, _T, RF, Enc, Str) ->
558    More = fun(T1, Dd) -> ?FUNCTION_NAME(Bin, 1+Dd, T1, RF, Enc, Str) end,
559    {"<<...>>", 7, 3, More};
560print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) ->
561    D1 = D - 1,
562    case
563        Str andalso
564        (bit_size(Bin) rem 8) =:= 0 andalso
565        printable_bin0(Bin, D1, tsub(T, 6), Enc)
566    of
567        {true, List} when is_list(List) ->
568            S = io_lib:write_string(List, $"), %"
569            {[$<,$<,S,$>,$>], 4 + length(S), 0, no_more};
570        {false, List} when is_list(List) ->
571            S = io_lib:write_string(List, $"), %"
572            {[$<,$<,S,"/utf8>>"], 9 + io_lib:chars_length(S), 0, no_more};
573        {true, true, Prefix} ->
574            S = io_lib:write_string(Prefix, $"), %"
575            More = fun(T1, Dd) ->
576                           ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str)
577                   end,
578            {[$<,$<,S|"...>>"], 7 + length(S), 3, More};
579        {false, true, Prefix} ->
580            S = io_lib:write_string(Prefix, $"), %"
581            More = fun(T1, Dd) ->
582                           ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str)
583                   end,
584            {[$<,$<,S|"/utf8...>>"], 12 + io_lib:chars_length(S), 3, More};
585        false ->
586            case io_lib:write_binary(Bin, D, T) of
587                {S, <<>>} ->
588                    {{bin, S}, iolist_size(S), 0, no_more};
589                {S, _Rest} ->
590                    More = fun(T1, Dd) ->
591                                   ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str)
592                           end,
593                    {{bin, S}, iolist_size(S), 3, More}
594            end
595    end;
596print_length(Term, _D, _T, _RF, _Enc, _Str) ->
597    S = io_lib:write(Term),
598    %% S can contain unicode, so iolist_size(S) cannot be used here
599    {S, io_lib:chars_length(S), 0, no_more}.
600
601print_length_map(Map, 1, _T, RF, Enc, Str) ->
602    More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1+Dd, T1, RF, Enc, Str) end,
603    {"#{...}", 6, 3, More};
604print_length_map(Map, D, T, RF, Enc, Str) when is_map(Map) ->
605    Next = maps:next(maps:iterator(Map)),
606    PairsS = print_length_map_pairs(Next, D, D - 1, tsub(T, 3), RF, Enc, Str),
607    {Len, Dots} = list_length(PairsS, 3, 0),
608    {{map, PairsS}, Len, Dots, no_more}.
609
610print_length_map_pairs(none, _D, _D0, _T, _RF, _Enc, _Str) ->
611    [];
612print_length_map_pairs(Term, D, D0, T, RF, Enc, Str) when D =:= 1; T =:= 0->
613    More = fun(T1, Dd) ->
614                   ?FUNCTION_NAME(Term, D+Dd, D0, T1, RF, Enc, Str)
615           end,
616    {dots, 3, 3, More};
617print_length_map_pairs({K, V, Iter}, D, D0, T, RF, Enc, Str) ->
618    Next = maps:next(Iter),
619    T1 = case Next =:= none of
620             false -> tsub(T, 1);
621             true -> T
622         end,
623    Pair1 = print_length_map_pair(K, V, D0, T1, RF, Enc, Str),
624    {_, Len1, _, _} = Pair1,
625    [Pair1 |
626     print_length_map_pairs(Next, D - 1, D0, tsub(T1, Len1), RF, Enc, Str)].
627
628print_length_map_pair(K, V, D, T, RF, Enc, Str) ->
629    {_, KL, KD, _} = P1 = print_length(K, D, T, RF, Enc, Str),
630    KL1 = KL + 4,
631    {_, VL, VD, _} = P2 = print_length(V, D, tsub(T, KL1), RF, Enc, Str),
632    {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}.
633
634print_length_tuple(Tuple, 1, _T, RF, Enc, Str) ->
635    More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, 1+Dd, T1, RF, Enc, Str) end,
636    {"{...}", 5, 3, More};
637print_length_tuple(Tuple, D, T, RF, Enc, Str) ->
638    L = print_length_tuple1(Tuple, 1, D, tsub(T, 2), RF, Enc, Str),
639    IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1),
640    {Len, Dots} = list_length(L, 2, 0),
641    {{tuple,IsTagged,L}, Len, Dots, no_more}.
642
643print_length_tuple1(Tuple, I, _D, _T, _RF, _Enc, _Str)
644             when I > tuple_size(Tuple) ->
645    [];
646print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) when D =:= 1; T =:= 0->
647    More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, I, D+Dd, T1, RF, Enc, Str) end,
648    {dots, 3, 3, More};
649print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) ->
650    E = element(I, Tuple),
651    T1 = case I =:= tuple_size(Tuple) of
652             false -> tsub(T, 1);
653             true -> T
654         end,
655    {_, Len1, _, _} = Elem1 = print_length(E, D - 1, T1, RF, Enc, Str),
656    T2 = tsub(T1, Len1),
657    [Elem1 | print_length_tuple1(Tuple, I + 1, D - 1, T2, RF, Enc, Str)].
658
659print_length_record(Tuple, 1, _T, RF, RDefs, Enc, Str) ->
660    More = fun(T1, Dd) ->
661                   ?FUNCTION_NAME(Tuple, 1+Dd, T1, RF, RDefs, Enc, Str)
662           end,
663    {"{...}", 5, 3, More};
664print_length_record(Tuple, D, T, RF, RDefs, Enc, Str) ->
665    Name = [$# | write_atom(element(1, Tuple), Enc)],
666    NameL = io_lib:chars_length(Name),
667    T1 = tsub(T, NameL+2),
668    L = print_length_fields(RDefs, D - 1, T1, Tuple, 2, RF, Enc, Str),
669    {Len, Dots} = list_length(L, NameL + 2, 0),
670    {{record, [{Name,NameL} | L]}, Len, Dots, no_more}.
671
672print_length_fields([], _D, _T, Tuple, I, _RF, _Enc, _Str)
673                when I > tuple_size(Tuple) ->
674    [];
675print_length_fields(Term, D, T, Tuple, I, RF, Enc, Str)
676                when D =:= 1; T =:= 0 ->
677    More = fun(T1, Dd) ->
678                   ?FUNCTION_NAME(Term, D+Dd, T1, Tuple, I, RF, Enc, Str)
679           end,
680    {dots, 3, 3, More};
681print_length_fields([Def | Defs], D, T, Tuple, I, RF, Enc, Str) ->
682    E = element(I, Tuple),
683    T1 = case I =:= tuple_size(Tuple) of
684             false -> tsub(T, 1);
685             true -> T
686         end,
687    Field1 = print_length_field(Def, D - 1, T1, E, RF, Enc, Str),
688    {_, Len1, _, _} = Field1,
689    T2 = tsub(T1, Len1),
690    [Field1 |
691     print_length_fields(Defs, D - 1, T2, Tuple, I + 1, RF, Enc, Str)].
692
693print_length_field(Def, D, T, E, RF, Enc, Str) ->
694    Name = write_atom(Def, Enc),
695    NameL = io_lib:chars_length(Name) + 3,
696    {_, Len, Dots, _} =
697        Field = print_length(E, D, tsub(T, NameL), RF, Enc, Str),
698    {{field, Name, NameL, Field}, NameL + Len, Dots, no_more}.
699
700print_length_list(List, D, T, RF, Enc, Str) ->
701    L = print_length_list1(List, D, tsub(T, 2), RF, Enc, Str),
702    {Len, Dots} = list_length(L, 2, 0),
703    {{list, L}, Len, Dots, no_more}.
704
705print_length_list1([], _D, _T, _RF, _Enc, _Str) ->
706    [];
707print_length_list1(Term, D, T, RF, Enc, Str) when D =:= 1; T =:= 0->
708    More = fun(T1, Dd) -> ?FUNCTION_NAME(Term, D+Dd, T1, RF, Enc, Str) end,
709    {dots, 3, 3, More};
710print_length_list1([E | Es], D, T, RF, Enc, Str) ->
711    %% If E is the last element in list, don't account length for a comma.
712    T1 = case Es =:= [] of
713             false -> tsub(T, 1);
714             true -> T
715         end,
716    {_, Len1, _, _} = Elem1 = print_length(E, D - 1, T1, RF, Enc, Str),
717    [Elem1 | print_length_list1(Es, D - 1, tsub(T1, Len1), RF, Enc, Str)];
718print_length_list1(E, D, T, RF, Enc, Str) ->
719    print_length(E, D - 1, T, RF, Enc, Str).
720
721list_length([], Acc, DotsAcc) ->
722    {Acc, DotsAcc};
723list_length([{_, Len, Dots, _} | Es], Acc, DotsAcc) ->
724    list_length_tail(Es, Acc + Len, DotsAcc + Dots);
725list_length({_, Len, Dots, _}, Acc, DotsAcc) ->
726    {Acc + Len, DotsAcc + Dots}.
727
728list_length_tail([], Acc, DotsAcc) ->
729    {Acc, DotsAcc};
730list_length_tail([{_, Len, Dots, _} | Es], Acc, DotsAcc) ->
731    list_length_tail(Es, Acc + 1 + Len, DotsAcc + Dots);
732list_length_tail({_, Len, Dots, _}, Acc, DotsAcc) ->
733    {Acc + 1 + Len, DotsAcc + Dots}.
734
735%% ?CHARS printable characters has depth 1.
736-define(CHARS, 4).
737
738%% only flat lists are "printable"
739printable_list(_L, 1, _T, _Enc) ->
740    false;
741printable_list(L, _D, T, latin1) when T < 0 ->
742    io_lib:printable_latin1_list(L);
743printable_list(L, _D, T, latin1) when T >= 0 ->
744    N = tsub(T, 2),
745    case printable_latin1_list(L, N) of
746        all ->
747            true;
748        0 ->
749            {L1, _} = lists:split(N, L),
750            {true, L1};
751        _NC ->
752            false
753    end;
754printable_list(L, _D, T, _Unicode) when T >= 0 ->
755    N = tsub(T, 2),
756    %% Be careful not to traverse more of L than necessary.
757    try string:slice(L, 0, N) of
758        "" ->
759            false;
760        Prefix ->
761            case is_flat(L, lists:flatlength(Prefix)) of
762                true ->
763                    case string:equal(Prefix, L) of
764                        true ->
765                            io_lib:printable_list(L);
766                        false ->
767                            io_lib:printable_list(Prefix)
768                            andalso {true, Prefix}
769                    end;
770                false ->
771                    false
772            end
773    catch _:_ -> false
774    end;
775printable_list(L, _D, T, _Uni) when T < 0->
776    io_lib:printable_list(L).
777
778is_flat(_L, 0) ->
779    true;
780is_flat([C|Cs], N) when is_integer(C) ->
781    is_flat(Cs, N - 1);
782is_flat(_, _N) ->
783    false.
784
785printable_bin0(Bin, D, T, Enc) ->
786    Len = case D >= 0 of
787              true ->
788                  %% Use byte_size() also if Enc =/= latin1.
789                  DChars = erlang:min(?CHARS * D, byte_size(Bin)),
790                  case T >= 0 of
791                      true ->
792                          erlang:min(T, DChars);
793                      false ->
794                          DChars
795                  end;
796              false when T < 0 ->
797                  byte_size(Bin);
798              false when T >= 0 -> % cannot happen
799                  T
800          end,
801    printable_bin(Bin, Len, D, Enc).
802
803printable_bin(_Bin, 0, _D, _Enc) ->
804    false;
805printable_bin(Bin, Len, D, latin1) ->
806    N = erlang:min(20, Len),
807    L = binary_to_list(Bin, 1, N),
808    case printable_latin1_list(L, N) of
809        all when N =:= byte_size(Bin)  ->
810            {true, L};
811        all when N =:= Len -> % N < byte_size(Bin)
812            {true, true, L};
813        all ->
814            case printable_bin1(Bin, 1 + N, Len - N) of
815                0 when byte_size(Bin) =:= Len ->
816                    {true, binary_to_list(Bin)};
817                NC when D > 0, Len - NC >= D ->
818                    {true, true, binary_to_list(Bin, 1, Len - NC)};
819                NC when is_integer(NC) ->
820                    false
821            end;
822        NC when is_integer(NC), D > 0, N - NC >= D ->
823            {true, true, binary_to_list(Bin, 1, N - NC)};
824        NC when is_integer(NC) ->
825            false
826    end;
827printable_bin(Bin, Len, D, _Uni) ->
828    case valid_utf8(Bin,Len) of
829	true ->
830	    case printable_unicode(Bin, Len, [], io:printable_range()) of
831		{_, <<>>, L} ->
832		    {byte_size(Bin) =:= length(L), L};
833		{NC, Bin1, L} when D > 0, Len - NC >= D ->
834		    {byte_size(Bin)-byte_size(Bin1) =:= length(L), true, L};
835		{_NC, _Bin, _L} ->
836		    false
837	    end;
838	false ->
839	    printable_bin(Bin, Len, D, latin1)
840    end.
841
842printable_bin1(_Bin, _Start, 0) ->
843    0;
844printable_bin1(Bin, Start, Len) ->
845    N = erlang:min(10000, Len),
846    L = binary_to_list(Bin, Start, Start + N - 1),
847    case printable_latin1_list(L, N) of
848        all ->
849            printable_bin1(Bin, Start + N, Len - N);
850        NC when is_integer(NC) ->
851            Len - (N - NC)
852    end.
853
854%% -> all | integer() >=0. Adopted from io_lib.erl.
855printable_latin1_list([_ | _], 0) -> 0;
856printable_latin1_list([C | Cs], N) when is_integer(C), C >= $\s, C =< $~ ->
857    printable_latin1_list(Cs, N - 1);
858printable_latin1_list([C | Cs], N) when is_integer(C), C >= $\240, C =< $\377 ->
859    printable_latin1_list(Cs, N - 1);
860printable_latin1_list([$\n | Cs], N) -> printable_latin1_list(Cs, N - 1);
861printable_latin1_list([$\r | Cs], N) -> printable_latin1_list(Cs, N - 1);
862printable_latin1_list([$\t | Cs], N) -> printable_latin1_list(Cs, N - 1);
863printable_latin1_list([$\v | Cs], N) -> printable_latin1_list(Cs, N - 1);
864printable_latin1_list([$\b | Cs], N) -> printable_latin1_list(Cs, N - 1);
865printable_latin1_list([$\f | Cs], N) -> printable_latin1_list(Cs, N - 1);
866printable_latin1_list([$\e | Cs], N) -> printable_latin1_list(Cs, N - 1);
867printable_latin1_list([], _) -> all;
868printable_latin1_list(_, N) -> N.
869
870valid_utf8(<<>>,_) ->
871    true;
872valid_utf8(_,0) ->
873    true;
874valid_utf8(<<_/utf8, R/binary>>,N) ->
875    valid_utf8(R,N-1);
876valid_utf8(_,_) ->
877    false.
878
879printable_unicode(<<C/utf8, R/binary>>=Bin, I, L, Range) when I > 0 ->
880    case printable_char(C,Range) of
881        true ->
882            printable_unicode(R, I - 1, [C | L],Range);
883        false ->
884            {I, Bin, lists:reverse(L)}
885    end;
886printable_unicode(Bin, I, L,_) ->
887    {I, Bin, lists:reverse(L)}.
888
889printable_char($\n,_) -> true;
890printable_char($\r,_) -> true;
891printable_char($\t,_) -> true;
892printable_char($\v,_) -> true;
893printable_char($\b,_) -> true;
894printable_char($\f,_) -> true;
895printable_char($\e,_) -> true;
896printable_char(C,latin1) ->
897    C >= $\s andalso C =< $~ orelse
898    C >= 16#A0 andalso C =< 16#FF;
899printable_char(C,unicode) ->
900    C >= $\s andalso C =< $~ orelse
901    C >= 16#A0 andalso C < 16#D800 orelse
902    C > 16#DFFF andalso C < 16#FFFE orelse
903    C > 16#FFFF andalso C =< 16#10FFFF.
904
905write_atom(A, latin1) ->
906    io_lib:write_atom_as_latin1(A);
907write_atom(A, _Uni) ->
908    io_lib:write_atom(A).
909
910write_string(S, latin1) ->
911    io_lib:write_latin1_string(S, $"); %"
912write_string(S, _Uni) ->
913    io_lib:write_string(S, $"). %"
914
915expand({_, _, _Dots=0, no_more} = If, _T, _Dd) -> If;
916expand({{tuple,IsTagged,L}, _Len, _, no_more}, T, Dd) ->
917    {NL, NLen, NDots} = expand_list(L, T, Dd, 2),
918    {{tuple,IsTagged,NL}, NLen, NDots, no_more};
919expand({{map, Pairs}, _Len, _, no_more}, T, Dd) ->
920    {NPairs, NLen, NDots} = expand_list(Pairs, T, Dd, 3),
921    {{map, NPairs}, NLen, NDots, no_more};
922expand({{map_pair, K, V}, _Len, _, no_more}, T, Dd) ->
923    {_, KL, KD, _} = P1 = expand(K, tsub(T, 1), Dd),
924    KL1 = KL + 4,
925    {_, VL, VD, _} = P2 = expand(V, tsub(T, KL1), Dd),
926    {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more};
927expand({{record, [{Name,NameL} | L]}, _Len, _, no_more}, T, Dd) ->
928    {NL, NLen, NDots} = expand_list(L, T, Dd, NameL + 2),
929    {{record, [{Name,NameL} | NL]}, NLen, NDots, no_more};
930expand({{field, Name, NameL, Field}, _Len, _, no_more}, T, Dd) ->
931    F = {_S, L, Dots, _} = expand(Field, tsub(T, NameL), Dd),
932    {{field, Name, NameL, F}, NameL + L, Dots, no_more};
933expand({_, _, _, More}, T, Dd) ->
934    More(T, Dd).
935
936expand_list(Ifs, T, Dd, L0) ->
937    L = expand_list(Ifs, tsub(T, L0), Dd),
938    {Len, Dots} = list_length(L, L0, 0),
939    {L, Len, Dots}.
940
941expand_list([], _T, _Dd) ->
942    [];
943expand_list([If | Ifs], T, Dd) ->
944    T1 = case Ifs =:= [] of
945             false -> tsub(T, 1);
946             true -> T
947         end,
948    {_, Len1, _, _} = Elem1 = expand(If, T1, Dd),
949    [Elem1 | expand_list(Ifs, tsub(T1, Len1), Dd)];
950expand_list({_, _, _, More}, T, Dd) ->
951    More(T, Dd).
952
953%% Make sure T does not change sign.
954tsub(T, _) when T < 0 -> T;
955tsub(T, E) when T >= E -> T - E;
956tsub(_, _) -> 0.
957
958%% Throw 'no_good' if the indentation exceeds half the line length
959%% unless there is room for M characters on the line.
960
961cind({_S, Len, _, _}, Col, Ll, M, Ind, LD, W) when Len < Ll - Col - LD,
962                                                   Len + W + LD =< M ->
963    Ind;
964cind({{list,L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) ->
965    cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1);
966cind({{tuple,true,L}, _Len, _ ,_}, Col, Ll, M, Ind, LD, W) ->
967    cind_tag_tuple(L, Col, Ll, M, Ind, LD, W + 1);
968cind({{tuple,false,L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) ->
969    cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1);
970cind({{map,Pairs}, _Len, _, _}, Col, Ll, M, Ind, LD, W) ->
971    cind_map(Pairs, Col + 2, Ll, M, Ind, LD, W + 2);
972cind({{record,[{_Name,NLen} | L]}, _Len, _, _}, Col, Ll, M, Ind, LD, W) ->
973    cind_record(L, NLen, Col, Ll, M, Ind, LD, W + NLen + 1);
974cind({{bin,_S}, _Len, _, _}, _Col, _Ll, _M, Ind, _LD, _W) ->
975    Ind;
976cind({_S,_Len,_,_}, _Col, _Ll, _M, Ind, _LD, _W) ->
977    Ind.
978
979cind_tag_tuple([{_Tag,Tlen,_,_} | L], Col, Ll, M, Ind, LD, W) ->
980    TagInd = Tlen + 2,
981    Tcol = Col + TagInd,
982    if
983        Ind > 0, TagInd > Ind ->
984            Col1 = Col + Ind,
985            if
986                M + Col1 =< Ll; Col1 =< Ll div 2 ->
987                    cind_tail(L, Col1, Tcol, Ll, M, Ind, LD, W + Tlen);
988                true ->
989                    throw(no_good)
990            end;
991        M + Tcol < Ll; Tcol < Ll div 2 ->
992            cind_list(L, Tcol, Ll, M, Ind, LD, W + Tlen + 1);
993        true ->
994            throw(no_good)
995    end;
996cind_tag_tuple(_, _Col, _Ll, _M, Ind, _LD, _W) ->
997    Ind.
998
999cind_map([P | Ps], Col, Ll, M, Ind, LD, W) ->
1000    PW = cind_pair(P, Col, Ll, M, Ind, last_depth(Ps, LD), W),
1001    cind_pairs_tail(Ps, Col, Col + PW, Ll, M, Ind, LD, W + PW);
1002cind_map(_, _Col, _Ll, _M, Ind, _LD, _W) ->
1003    Ind.                                        % cannot happen
1004
1005cind_pairs_tail([{_, Len, _, _} = P | Ps], Col0, Col, Ll, M, Ind, LD, W) ->
1006    LD1 = last_depth(Ps, LD),
1007    ELen = 1 + Len,
1008    if
1009        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P);
1010        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) ->
1011            cind_pairs_tail(Ps, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen);
1012        true ->
1013            PW = cind_pair(P, Col0, Ll, M, Ind, LD1, 0),
1014            cind_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, Ind, LD, PW)
1015    end;
1016cind_pairs_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) ->
1017    Ind.
1018
1019cind_pair({{map_pair, _Key, _Value}, Len, _, _}=Pair, Col, Ll, M, _Ind, LD, W)
1020         when Len < Ll - Col - LD, Len + W + LD =< M ->
1021    if
1022        ?ATM_PAIR(Pair) ->
1023            Len;
1024        true ->
1025            Ll
1026    end;
1027cind_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, Ind, LD, W0) ->
1028    cind(K, Col0, Ll, M, Ind, LD, W0),
1029    I = map_value_indent(Ind),
1030    cind(V, Col0 + I, Ll, M, Ind, LD, 0),
1031    Ll.
1032
1033map_value_indent(TInd) ->
1034    case TInd > 0 of
1035        true ->
1036            TInd;
1037        false ->
1038            4
1039    end.
1040
1041cind_record([F | Fs], Nlen, Col0, Ll, M, Ind, LD, W0) ->
1042    Nind = Nlen + 1,
1043    {Col, W} = cind_rec(Nind, Col0, Ll, M, Ind, W0),
1044    FW = cind_field(F, Col, Ll, M, Ind, last_depth(Fs, LD), W),
1045    cind_fields_tail(Fs, Col, Col + FW, Ll, M, Ind, LD, W + FW);
1046cind_record(_, _Nlen, _Col, _Ll, _M, Ind, _LD, _W) ->
1047    Ind.
1048
1049cind_fields_tail([{_, Len, _, _} = F | Fs], Col0, Col, Ll, M, Ind, LD, W) ->
1050    LD1 = last_depth(Fs, LD),
1051    ELen = 1 + Len,
1052    if
1053        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F);
1054        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) ->
1055            cind_fields_tail(Fs, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen);
1056        true ->
1057            FW = cind_field(F, Col0, Ll, M, Ind, LD1, 0),
1058            cind_fields_tail(Fs, Col0, Col + FW, Ll, M, Ind, LD, FW)
1059    end;
1060cind_fields_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) ->
1061    Ind.
1062
1063cind_field({{field, _N, _NL, _F}, Len, _, _}=Fl, Col, Ll, M, _Ind, LD, W)
1064         when Len < Ll - Col - LD, Len + W + LD =< M ->
1065    if
1066        ?ATM_FLD(Fl) ->
1067            Len;
1068        true ->
1069            Ll
1070    end;
1071cind_field({{field, _Name, NameL, F},_Len,_,_}, Col0, Ll, M, Ind, LD, W0) ->
1072    {Col, W} = cind_rec(NameL, Col0, Ll, M, Ind, W0 + NameL),
1073    cind(F, Col, Ll, M, Ind, LD, W),
1074    Ll.
1075
1076cind_rec(RInd, Col0, Ll, M, Ind, W0) ->
1077    Nl = (Ind > 0) and (RInd > Ind),
1078    DCol = case Nl of
1079               true -> Ind;
1080               false -> RInd
1081           end,
1082    Col = Col0 + DCol,
1083    if
1084        M + Col =< Ll; Col =< Ll div 2 ->
1085            W = case Nl of
1086                    true -> 0;
1087                    false -> W0
1088                end,
1089            {Col, W};
1090        true ->
1091            throw(no_good)
1092    end.
1093
1094cind_list({dots, _, _, _}, _Col0, _Ll, _M, Ind, _LD, _W) ->
1095    Ind;
1096cind_list([E | Es], Col0, Ll, M, Ind, LD, W) ->
1097    WE = cind_element(E, Col0, Ll, M, Ind, last_depth(Es, LD), W),
1098    cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, W + WE).
1099
1100cind_tail([], _Col0, _Col, _Ll, _M, Ind, _LD, _W) ->
1101    Ind;
1102cind_tail([{_, Len, _, _} = E | Es], Col0, Col, Ll, M, Ind, LD, W) ->
1103    LD1 = last_depth(Es, LD),
1104    ELen = 1 + Len,
1105    if
1106        LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E);
1107        LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) ->
1108            cind_tail(Es, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen);
1109        true ->
1110            WE = cind_element(E, Col0, Ll, M, Ind, LD1, 0),
1111            cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, WE)
1112    end;
1113cind_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, Ind, _LD, _W) ->
1114    Ind;
1115cind_tail({_, Len, _, _}=E, _Col0, Col, Ll, M, Ind, LD, W)
1116                  when Len + 1 < Ll - Col - (LD + 1),
1117                       Len + 1 + W + (LD + 1) =< M,
1118                       ?ATM(E) ->
1119    Ind;
1120cind_tail(E, _Col0, Col, Ll, M, Ind, LD, _W) ->
1121    cind(E, Col, Ll, M, Ind, LD + 1, 0).
1122
1123cind_element({_, Len, _, _}=E, Col, Ll, M, _Ind, LD, W)
1124           when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) ->
1125    Len;
1126cind_element(E, Col, Ll, M, Ind, LD, W) ->
1127    cind(E, Col, Ll, M, Ind, LD, W),
1128    Ll.
1129
1130last_depth([_ | _], _LD) ->
1131    0;
1132last_depth(_, LD) ->
1133    LD + 1.
1134
1135while_fail([], _F, V) ->
1136    V;
1137while_fail([A | As], F, V) ->
1138    try F(A) catch _ -> while_fail(As, F, V) end.
1139
1140%% make a string of N spaces
1141indent(N) when is_integer(N), N > 0 ->
1142    chars($\s, N-1).
1143
1144%% prepend N spaces onto Ind
1145indent(1, Ind) -> % Optimization of common case
1146    [$\s | Ind];
1147indent(4, Ind) -> % Optimization of common case
1148    S2 = [$\s, $\s],
1149    [S2, S2 | Ind];
1150indent(N, Ind) when is_integer(N), N > 0 ->
1151    [chars($\s, N) | Ind].
1152
1153%% A deep version of string:chars/2
1154chars(_C, 0) ->
1155    [];
1156chars(C, 2) ->
1157    [C, C];
1158chars(C, 3) ->
1159    [C, C, C];
1160chars(C, N) when (N band 1) =:= 0 ->
1161    S = chars(C, N bsr 1),
1162    [S | S];
1163chars(C, N) ->
1164    S = chars(C, N bsr 1),
1165    [C, S | S].
1166
1167get_option(Key, TupleList, Default) ->
1168    case lists:keyfind(Key, 1, TupleList) of
1169	false -> Default;
1170	{Key, Value} -> Value;
1171	_ -> Default
1172    end.
1173