Skip to content

Commit 5d1bd8f

Browse files
committed
stdlib: Apply chars_limit to field width for all format controls
Previously, chars_limit only capped the field width for ~s via limit_field/2. Other controls (~c, ~n, ~~, ~w, ~W, ~e, ~f, etc.) could allocate unbounded padding that bypassed chars_limit entirely. Fix by passing chars_limit to build_small/build_small_bin and applying limit_field to the field width before calling control_small. Also apply limit_field in control_limited for ~w/~W. Without chars_limit set, behavior is unchanged.
1 parent 088e58c commit 5d1bd8f

2 files changed

Lines changed: 33 additions & 22 deletions

File tree

lib/stdlib/src/io_lib_format.erl

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ build(Cs) ->
103103

104104
build(Cs, Options) ->
105105
CharsLimit = get_option(chars_limit, Options, -1),
106-
Res1 = build_small(Cs),
106+
Res1 = build_small(Cs, CharsLimit),
107107
{P, S, W, Other} = count_small(Res1),
108108
case P + S + W of
109109
0 ->
@@ -128,7 +128,7 @@ build_bin(Cs) ->
128128

129129
build_bin(Cs, Options) ->
130130
CharsLimit = get_option(chars_limit, Options, -1),
131-
Res1 = build_small_bin(Cs),
131+
Res1 = build_small_bin(Cs, CharsLimit),
132132
{P, S, W, Other} = count_small(Res1),
133133
case P + S + W of
134134
0 ->
@@ -349,13 +349,13 @@ count_small([], #{p := P, s := S, w := W, other := Other}) ->
349349
%% be smart when calculating indentation for characters in format.
350350

351351
build_small([#{control_char := C, args := As, width := F, adjust := Ad,
352-
precision := P, pad_char := Pad, encoding := Enc}=CC | Cs]) ->
353-
case control_small(C, As, F, Ad, P, Pad, Enc) of
354-
not_small -> [CC | build_small(Cs)];
355-
S -> lists:flatten(S) ++ build_small(Cs)
352+
precision := P, pad_char := Pad, encoding := Enc}=CC | Cs], CL) ->
353+
case control_small(C, As, limit_field(F, CL), Ad, P, Pad, Enc) of
354+
not_small -> [CC | build_small(Cs, CL)];
355+
S -> lists:flatten(S) ++ build_small(Cs, CL)
356356
end;
357-
build_small([C|Cs]) -> [C|build_small(Cs)];
358-
build_small([]) -> [].
357+
build_small([C|Cs], CL) -> [C|build_small(Cs, CL)];
358+
build_small([], _CL) -> [].
359359

360360
build_limited([#{control_char := C, args := As, width := F, adjust := Ad,
361361
precision := P, pad_char := Pad, encoding := Enc,
@@ -395,22 +395,22 @@ decr_pc($P, Pc) -> Pc - 1;
395395
decr_pc(_, Pc) -> Pc.
396396

397397
build_small_bin([#{control_char := C, args := As, width := F, adjust := Ad,
398-
precision := P, pad_char := Pad, encoding := Enc}=CC | Cs]) ->
399-
case control_small(C, As, F, Ad, P, Pad, Enc) of
398+
precision := P, pad_char := Pad, encoding := Enc}=CC | Cs], CL) ->
399+
case control_small(C, As, limit_field(F, CL), Ad, P, Pad, Enc) of
400400
not_small ->
401-
[CC | build_small_bin(Cs)];
401+
[CC | build_small_bin(Cs, CL)];
402402
[$\n|_] = NL ->
403-
[NL | build_small_bin(Cs)];
403+
[NL | build_small_bin(Cs, CL)];
404404
S ->
405405
SBin = unicode:characters_to_binary(S, Enc, unicode),
406406
true = is_binary(SBin),
407-
[SBin | build_small_bin(Cs)]
407+
[SBin | build_small_bin(Cs, CL)]
408408
end;
409-
build_small_bin([$\t|Cs]) ->
410-
[$\t | build_small_bin(Cs)];
411-
build_small_bin([C|Cs]) ->
412-
[C | build_small_bin(Cs)];
413-
build_small_bin([]) ->
409+
build_small_bin([$\t|Cs], CL) ->
410+
[$\t | build_small_bin(Cs, CL)];
411+
build_small_bin([C|Cs], CL) ->
412+
[C | build_small_bin(Cs, CL)];
413+
build_small_bin([], _CL) ->
414414
[].
415415

416416
build_limited_bin([#{control_char := C, args := As, width := F, adjust := Ad,
@@ -544,13 +544,13 @@ control_limited($s, [L0], F, Adj, P, Pad, Enc, _Str, _Ord, CL, _I) ->
544544
end;
545545
control_limited($w, [A], F, Adj, P, Pad, Enc, _Str, Ord, CL, _I) ->
546546
Chars = io_lib:write(A, -1, Enc, Ord, CL),
547-
term(Chars, F, Adj, P, Pad, Enc);
547+
term(Chars, limit_field(F, CL), Adj, P, Pad, Enc);
548548
control_limited($p, [A], F, Adj, P, Pad, Enc, Str, Ord, CL, I) ->
549549
print(A, -1, F, Adj, P, Pad, Enc, list, Str, Ord, CL, I);
550550
control_limited($W, [A,Depth], F, Adj, P, Pad, Enc, _Str, Ord, CL, _I)
551551
when is_integer(Depth) ->
552552
Chars = io_lib:write(A, Depth, Enc, Ord, CL),
553-
term(Chars, F, Adj, P, Pad, Enc);
553+
term(Chars, limit_field(F, CL), Adj, P, Pad, Enc);
554554
control_limited($P, [A,Depth], F, Adj, P, Pad, Enc, Str, Ord, CL, I)
555555
when is_integer(Depth) ->
556556
print(A, Depth, F, Adj, P, Pad, Enc, list, Str, Ord, CL, I).
@@ -560,13 +560,13 @@ control_limited_bin($s, [L0], F, Adj, P, Pad, Enc, _Str, _Ord, CL, _I) ->
560560
string_bin(B, Sz, limit_field(F, CL), Adj, P, Pad, Enc);
561561
control_limited_bin($w, [A], F, Adj, P, Pad, Enc, _Str, Ord, CL, I) ->
562562
{Chars, Sz} = io_lib:write_bin(A, -1, Enc, Ord, CL),
563-
term_bin(Chars, F, Adj, P, Pad, Enc, Sz, I);
563+
term_bin(Chars, limit_field(F, CL), Adj, P, Pad, Enc, Sz, I);
564564
control_limited_bin($p, [A], F, Adj, P, Pad, Enc, Str, Ord, CL, I) ->
565565
print(A, -1, F, Adj, P, Pad, Enc, binary, Str, Ord, CL, I);
566566
control_limited_bin($W, [A,Depth], F, Adj, P, Pad, Enc, _Str, Ord, CL, I)
567567
when is_integer(Depth) ->
568568
{Chars, Sz} = io_lib:write_bin(A, Depth, Enc, Ord, CL),
569-
term_bin(Chars, F, Adj, P, Pad, Enc, Sz, I);
569+
term_bin(Chars, limit_field(F, CL), Adj, P, Pad, Enc, Sz, I);
570570
control_limited_bin($P, [A,Depth], F, Adj, P, Pad, Enc, Str, Ord, CL, I)
571571
when is_integer(Depth) ->
572572
print(A, Depth, F, Adj, P, Pad, Enc, binary, Str, Ord, CL, I).

lib/stdlib/test/io_SUITE.erl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,6 +3178,17 @@ chars_limit(_Config) ->
31783178
CL <- lists:seq(N, N*3),
31793179
What <- [List, Tuple, Map, Record]
31803180
],
3181+
3182+
%% chars_limit caps field width for all controls, not just ~s.
3183+
10 = iolist_size(io_lib:format("~100n", [], [{chars_limit, 10}])),
3184+
10 = iolist_size(io_lib:format("~100c", [$x], [{chars_limit, 10}])),
3185+
10 = iolist_size(io_lib:format("~100c", [$x], [{chars_limit, 10}])),
3186+
10 = iolist_size(io_lib:format("~*c", [100, $x], [{chars_limit, 10}])),
3187+
10 = iolist_size(io_lib:format("~*c", [-100, $x], [{chars_limit, 10}])),
3188+
10 = iolist_size(io_lib:format("~100w", [foo], [{chars_limit, 10}])),
3189+
%% Without chars_limit, field width is not capped.
3190+
100 = iolist_size(io_lib:format("~100n", [])),
3191+
100 = iolist_size(io_lib:format("~100c", [$x])),
31813192
ok.
31823193

31833194
error_info(Config) ->

0 commit comments

Comments
 (0)