You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
18 KiB

преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 12 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1996-2011-2012. All Rights Reserved.
  5. %%
  6. %% The contents of this file are subject to the Erlang Public License,
  7. %% Version 1.1, (the "License"); you may not use this file except in
  8. %% compliance with the License. You should have received a copy of the
  9. %% Erlang Public License along with this software. If not, it can be
  10. %% retrieved online at http://www.erlang.org/.
  11. %%
  12. %% Software distributed under the License is distributed on an "AS IS"
  13. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  14. %% the License for the specific language governing rights and limitations
  15. %% under the License.
  16. %%
  17. %% %CopyrightEnd%
  18. %%
  19. -module(lager_format).
  20. %% fork of io_lib_format that uses trunc_io to protect against large terms
  21. -export([format/3, format/4]).
  22. -record(options, {
  23. chomp = false :: boolean()
  24. }).
  25. format(FmtStr, Args, MaxLen) ->
  26. format(FmtStr, Args, MaxLen, []).
  27. format([], [], _, _) ->
  28. "";
  29. format(FmtStr, Args, MaxLen, Opts) when is_atom(FmtStr) ->
  30. format(atom_to_list(FmtStr), Args, MaxLen, Opts);
  31. format(FmtStr, Args, MaxLen, Opts) when is_binary(FmtStr) ->
  32. format(binary_to_list(FmtStr), Args, MaxLen, Opts);
  33. format(FmtStr, Args, MaxLen, Opts) when is_list(FmtStr) ->
  34. case lager_stdlib:string_p(FmtStr) of
  35. true ->
  36. Options = make_options(Opts, #options{}),
  37. Cs = collect(FmtStr, Args),
  38. {Cs2, MaxLen2} = build(Cs, [], MaxLen, Options),
  39. %% count how many terms remain
  40. {Count, StrLen} = lists:foldl(
  41. fun({_C, _As, _F, _Adj, _P, _Pad, _Enc}, {Terms, Chars}) ->
  42. {Terms + 1, Chars};
  43. (_, {Terms, Chars}) ->
  44. {Terms, Chars + 1}
  45. end, {0, 0}, Cs2),
  46. build2(Cs2, Count, MaxLen2 - StrLen);
  47. false ->
  48. erlang:error(badarg)
  49. end;
  50. format(_FmtStr, _Args, _MaxLen, _Opts) ->
  51. erlang:error(badarg).
  52. collect([$~|Fmt0], Args0) ->
  53. {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
  54. [C|collect(Fmt1, Args1)];
  55. collect([C|Fmt], Args) ->
  56. [C|collect(Fmt, Args)];
  57. collect([], []) -> [].
  58. collect_cseq(Fmt0, Args0) ->
  59. {F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
  60. {P,Fmt2,Args2} = precision(Fmt1, Args1),
  61. {Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
  62. {Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
  63. {C,As,Fmt5,Args5} = collect_cc(Fmt4, Args4),
  64. {{C,As,F,Ad,P,Pad,Encoding},Fmt5,Args5}.
  65. encoding([$t|Fmt],Args) ->
  66. {unicode,Fmt,Args};
  67. encoding(Fmt,Args) ->
  68. {latin1,Fmt,Args}.
  69. field_width([$-|Fmt0], Args0) ->
  70. {F,Fmt,Args} = field_value(Fmt0, Args0),
  71. field_width(-F, Fmt, Args);
  72. field_width(Fmt0, Args0) ->
  73. {F,Fmt,Args} = field_value(Fmt0, Args0),
  74. field_width(F, Fmt, Args).
  75. field_width(F, Fmt, Args) when F < 0 ->
  76. {-F,left,Fmt,Args};
  77. field_width(F, Fmt, Args) when F >= 0 ->
  78. {F,right,Fmt,Args}.
  79. precision([$.|Fmt], Args) ->
  80. field_value(Fmt, Args);
  81. precision(Fmt, Args) ->
  82. {none,Fmt,Args}.
  83. field_value([$*|Fmt], [A|Args]) when is_integer(A) ->
  84. {A,Fmt,Args};
  85. field_value([C|Fmt], Args) when is_integer(C), C >= $0, C =< $9 ->
  86. field_value([C|Fmt], Args, 0);
  87. field_value(Fmt, Args) ->
  88. {none,Fmt,Args}.
  89. field_value([C|Fmt], Args, F) when is_integer(C), C >= $0, C =< $9 ->
  90. field_value(Fmt, Args, 10*F + (C - $0));
  91. field_value(Fmt, Args, F) -> %Default case
  92. {F,Fmt,Args}.
  93. pad_char([$.,$*|Fmt], [Pad|Args]) -> {Pad,Fmt,Args};
  94. pad_char([$.,Pad|Fmt], Args) -> {Pad,Fmt,Args};
  95. pad_char(Fmt, Args) -> {$\s,Fmt,Args}.
  96. %% collect_cc([FormatChar], [Argument]) ->
  97. %% {Control,[ControlArg],[FormatChar],[Arg]}.
  98. %% Here we collect the argments for each control character.
  99. %% Be explicit to cause failure early.
  100. collect_cc([$w|Fmt], [A|Args]) -> {$w,[A],Fmt,Args};
  101. collect_cc([$p|Fmt], [A|Args]) -> {$p,[A],Fmt,Args};
  102. collect_cc([$W|Fmt], [A,Depth|Args]) -> {$W,[A,Depth],Fmt,Args};
  103. collect_cc([$P|Fmt], [A,Depth|Args]) -> {$P,[A,Depth],Fmt,Args};
  104. collect_cc([$s|Fmt], [A|Args]) -> {$s,[A],Fmt,Args};
  105. collect_cc([$e|Fmt], [A|Args]) -> {$e,[A],Fmt,Args};
  106. collect_cc([$f|Fmt], [A|Args]) -> {$f,[A],Fmt,Args};
  107. collect_cc([$g|Fmt], [A|Args]) -> {$g,[A],Fmt,Args};
  108. collect_cc([$b|Fmt], [A|Args]) -> {$b,[A],Fmt,Args};
  109. collect_cc([$B|Fmt], [A|Args]) -> {$B,[A],Fmt,Args};
  110. collect_cc([$x|Fmt], [A,Prefix|Args]) -> {$x,[A,Prefix],Fmt,Args};
  111. collect_cc([$X|Fmt], [A,Prefix|Args]) -> {$X,[A,Prefix],Fmt,Args};
  112. collect_cc([$+|Fmt], [A|Args]) -> {$+,[A],Fmt,Args};
  113. collect_cc([$#|Fmt], [A|Args]) -> {$#,[A],Fmt,Args};
  114. collect_cc([$c|Fmt], [A|Args]) -> {$c,[A],Fmt,Args};
  115. collect_cc([$~|Fmt], Args) when is_list(Args) -> {$~,[],Fmt,Args};
  116. collect_cc([$n|Fmt], Args) when is_list(Args) -> {$n,[],Fmt,Args};
  117. collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}.
  118. %% build([Control], Pc, Indentation) -> [Char].
  119. %% Interpret the control structures. Count the number of print
  120. %% remaining and only calculate indentation when necessary. Must also
  121. %% be smart when calculating indentation for characters in format.
  122. build([{$n, _, _, _, _, _, _}], Acc, MaxLen, #options{chomp=true}) ->
  123. %% trailing ~n, ignore
  124. {lists:reverse(Acc), MaxLen};
  125. build([{C,As,F,Ad,P,Pad,Enc}|Cs], Acc, MaxLen, O) ->
  126. {S, MaxLen2} = control(C, As, F, Ad, P, Pad, Enc, MaxLen),
  127. build(Cs, [S|Acc], MaxLen2, O);
  128. build([$\n], Acc, MaxLen, #options{chomp=true}) ->
  129. %% trailing \n, ignore
  130. {lists:reverse(Acc), MaxLen};
  131. build([$\n|Cs], Acc, MaxLen, O) ->
  132. build(Cs, [$\n|Acc], MaxLen - 1, O);
  133. build([$\t|Cs], Acc, MaxLen, O) ->
  134. build(Cs, [$\t|Acc], MaxLen - 1, O);
  135. build([C|Cs], Acc, MaxLen, O) ->
  136. build(Cs, [C|Acc], MaxLen - 1, O);
  137. build([], Acc, MaxLen, _O) ->
  138. {lists:reverse(Acc), MaxLen}.
  139. build2([{C,As,F,Ad,P,Pad,Enc}|Cs], Count, MaxLen) ->
  140. {S, Len} = control2(C, As, F, Ad, P, Pad, Enc, MaxLen div Count),
  141. [S|build2(Cs, Count - 1, MaxLen - Len)];
  142. build2([C|Cs], Count, MaxLen) ->
  143. [C|build2(Cs, Count, MaxLen)];
  144. build2([], _, _) -> [].
  145. %% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar,
  146. %% Indentation) -> [Char]
  147. %% This is the main dispatch function for the various formatting commands.
  148. %% Field widths and precisions have already been calculated.
  149. control($e, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
  150. Res = fwrite_e(A, F, Adj, P, Pad),
  151. {Res, L - lists:flatlength(Res)};
  152. control($f, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
  153. Res = fwrite_f(A, F, Adj, P, Pad),
  154. {Res, L - lists:flatlength(Res)};
  155. control($g, [A], F, Adj, P, Pad, _Enc, L) when is_float(A) ->
  156. Res = fwrite_g(A, F, Adj, P, Pad),
  157. {Res, L - lists:flatlength(Res)};
  158. control($b, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  159. Res = unprefixed_integer(A, F, Adj, base(P), Pad, true),
  160. {Res, L - lists:flatlength(Res)};
  161. control($B, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  162. Res = unprefixed_integer(A, F, Adj, base(P), Pad, false),
  163. {Res, L - lists:flatlength(Res)};
  164. control($x, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
  165. is_atom(Prefix) ->
  166. Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true),
  167. {Res, L - lists:flatlength(Res)};
  168. control($x, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  169. true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
  170. Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true),
  171. {Res, L - lists:flatlength(Res)};
  172. control($X, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A),
  173. is_atom(Prefix) ->
  174. Res = prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false),
  175. {Res, L - lists:flatlength(Res)};
  176. control($X, [A,Prefix], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  177. true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list
  178. Res = prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false),
  179. {Res, L - lists:flatlength(Res)};
  180. control($+, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  181. Base = base(P),
  182. Prefix = [integer_to_list(Base), $#],
  183. Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, true),
  184. {Res, L - lists:flatlength(Res)};
  185. control($#, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  186. Base = base(P),
  187. Prefix = [integer_to_list(Base), $#],
  188. Res = prefixed_integer(A, F, Adj, Base, Pad, Prefix, false),
  189. {Res, L - lists:flatlength(Res)};
  190. control($c, [A], F, Adj, P, Pad, unicode, L) when is_integer(A) ->
  191. Res = char(A, F, Adj, P, Pad),
  192. {Res, L - lists:flatlength(Res)};
  193. control($c, [A], F, Adj, P, Pad, _Enc, L) when is_integer(A) ->
  194. Res = char(A band 255, F, Adj, P, Pad),
  195. {Res, L - lists:flatlength(Res)};
  196. control($~, [], F, Adj, P, Pad, _Enc, L) ->
  197. Res = char($~, F, Adj, P, Pad),
  198. {Res, L - lists:flatlength(Res)};
  199. control($n, [], F, Adj, P, Pad, _Enc, L) ->
  200. Res = newline(F, Adj, P, Pad),
  201. {Res, L - lists:flatlength(Res)};
  202. control($i, [_A], _F, _Adj, _P, _Pad, _Enc, L) ->
  203. {[], L};
  204. control($s, [A], F, Adj, P, Pad, _Enc, L) when is_atom(A) ->
  205. Res = string(atom_to_list(A), F, Adj, P, Pad),
  206. {Res, L - lists:flatlength(Res)};
  207. control(C, A, F, Adj, P, Pad, Enc, L) ->
  208. %% save this for later - these are all the 'large' terms
  209. {{C, A, F, Adj, P, Pad, Enc}, L}.
  210. control2($w, [A], F, Adj, P, Pad, _Enc, L) ->
  211. Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, false}]),
  212. Res = term(Term, F, Adj, P, Pad),
  213. {Res, lists:flatlength(Res)};
  214. control2($p, [A], _F, _Adj, _P, _Pad, _Enc, L) ->
  215. Term = lager_trunc_io:fprint(A, L, [{lists_as_strings, true}]),
  216. {Term, lists:flatlength(Term)};
  217. control2($W, [A,Depth], F, Adj, P, Pad, _Enc, L) when is_integer(Depth) ->
  218. Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, false}]),
  219. Res = term(Term, F, Adj, P, Pad),
  220. {Res, lists:flatlength(Res)};
  221. control2($P, [A,Depth], _F, _Adj, _P, _Pad, _Enc, L) when is_integer(Depth) ->
  222. Term = lager_trunc_io:fprint(A, L, [{depth, Depth}, {lists_as_strings, true}]),
  223. {Term, lists:flatlength(Term)};
  224. control2($s, [L0], F, Adj, P, Pad, latin1, L) ->
  225. List = lager_trunc_io:fprint(maybe_flatten(L0), L, [{force_strings, true}]),
  226. Res = string(List, F, Adj, P, Pad),
  227. {Res, lists:flatlength(Res)};
  228. control2($s, [L0], F, Adj, P, Pad, unicode, L) ->
  229. List = lager_trunc_io:fprint(unicode:characters_to_list(L0), L, [{force_strings, true}]),
  230. Res = uniconv(string(List, F, Adj, P, Pad)),
  231. {Res, lists:flatlength(Res)}.
  232. maybe_flatten(X) when is_list(X) ->
  233. lists:flatten(X);
  234. maybe_flatten(X) ->
  235. X.
  236. make_options([], Options) ->
  237. Options;
  238. make_options([{chomp, Bool}|T], Options) when is_boolean(Bool) ->
  239. make_options(T, Options#options{chomp=Bool}).
  240. -ifdef(UNICODE_AS_BINARIES).
  241. uniconv(C) ->
  242. unicode:characters_to_binary(C,unicode).
  243. -else.
  244. uniconv(C) ->
  245. C.
  246. -endif.
  247. %% Default integer base
  248. base(none) ->
  249. 10;
  250. base(B) when is_integer(B) ->
  251. B.
  252. %% term(TermList, Field, Adjust, Precision, PadChar)
  253. %% Output the characters in a term.
  254. %% Adjust the characters within the field if length less than Max padding
  255. %% with PadChar.
  256. term(T, none, _Adj, none, _Pad) -> T;
  257. term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
  258. term(T, F, Adj, P0, Pad) ->
  259. L = lists:flatlength(T),
  260. P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
  261. if
  262. L > P ->
  263. adjust(chars($*, P), chars(Pad, F-P), Adj);
  264. F >= P ->
  265. adjust(T, chars(Pad, F-L), Adj)
  266. end.
  267. %% fwrite_e(Float, Field, Adjust, Precision, PadChar)
  268. fwrite_e(Fl, none, Adj, none, Pad) -> %Default values
  269. fwrite_e(Fl, none, Adj, 6, Pad);
  270. fwrite_e(Fl, none, _Adj, P, _Pad) when P >= 2 ->
  271. float_e(Fl, float_data(Fl), P);
  272. fwrite_e(Fl, F, Adj, none, Pad) ->
  273. fwrite_e(Fl, F, Adj, 6, Pad);
  274. fwrite_e(Fl, F, Adj, P, Pad) when P >= 2 ->
  275. term(float_e(Fl, float_data(Fl), P), F, Adj, F, Pad).
  276. float_e(Fl, Fd, P) when Fl < 0.0 -> %Negative numbers
  277. [$-|float_e(-Fl, Fd, P)];
  278. float_e(_Fl, {Ds,E}, P) ->
  279. case float_man(Ds, 1, P-1) of
  280. {[$0|Fs],true} -> [[$1|Fs]|float_exp(E)];
  281. {Fs,false} -> [Fs|float_exp(E-1)]
  282. end.
  283. %% float_man([Digit], Icount, Dcount) -> {[Chars],CarryFlag}.
  284. %% Generate the characters in the mantissa from the digits with Icount
  285. %% characters before the '.' and Dcount decimals. Handle carry and let
  286. %% caller decide what to do at top.
  287. float_man(Ds, 0, Dc) ->
  288. {Cs,C} = float_man(Ds, Dc),
  289. {[$.|Cs],C};
  290. float_man([D|Ds], I, Dc) ->
  291. case float_man(Ds, I-1, Dc) of
  292. {Cs,true} when D =:= $9 -> {[$0|Cs],true};
  293. {Cs,true} -> {[D+1|Cs],false};
  294. {Cs,false} -> {[D|Cs],false}
  295. end;
  296. float_man([], I, Dc) -> %Pad with 0's
  297. {string:chars($0, I, [$.|string:chars($0, Dc)]),false}.
  298. float_man([D|_], 0) when D >= $5 -> {[],true};
  299. float_man([_|_], 0) -> {[],false};
  300. float_man([D|Ds], Dc) ->
  301. case float_man(Ds, Dc-1) of
  302. {Cs,true} when D =:= $9 -> {[$0|Cs],true};
  303. {Cs,true} -> {[D+1|Cs],false};
  304. {Cs,false} -> {[D|Cs],false}
  305. end;
  306. float_man([], Dc) -> {string:chars($0, Dc),false}. %Pad with 0's
  307. %% float_exp(Exponent) -> [Char].
  308. %% Generate the exponent of a floating point number. Always include sign.
  309. float_exp(E) when E >= 0 ->
  310. [$e,$+|integer_to_list(E)];
  311. float_exp(E) ->
  312. [$e|integer_to_list(E)].
  313. %% fwrite_f(FloatData, Field, Adjust, Precision, PadChar)
  314. fwrite_f(Fl, none, Adj, none, Pad) -> %Default values
  315. fwrite_f(Fl, none, Adj, 6, Pad);
  316. fwrite_f(Fl, none, _Adj, P, _Pad) when P >= 1 ->
  317. float_f(Fl, float_data(Fl), P);
  318. fwrite_f(Fl, F, Adj, none, Pad) ->
  319. fwrite_f(Fl, F, Adj, 6, Pad);
  320. fwrite_f(Fl, F, Adj, P, Pad) when P >= 1 ->
  321. term(float_f(Fl, float_data(Fl), P), F, Adj, F, Pad).
  322. float_f(Fl, Fd, P) when Fl < 0.0 ->
  323. [$-|float_f(-Fl, Fd, P)];
  324. float_f(Fl, {Ds,E}, P) when E =< 0 ->
  325. float_f(Fl, {string:chars($0, -E+1, Ds),1}, P); %Prepend enough 0's
  326. float_f(_Fl, {Ds,E}, P) ->
  327. case float_man(Ds, E, P) of
  328. {Fs,true} -> "1" ++ Fs; %Handle carry
  329. {Fs,false} -> Fs
  330. end.
  331. %% float_data([FloatChar]) -> {[Digit],Exponent}
  332. float_data(Fl) ->
  333. float_data(float_to_list(Fl), []).
  334. float_data([$e|E], Ds) ->
  335. {lists:reverse(Ds),list_to_integer(E)+1};
  336. float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
  337. float_data(Cs, [D|Ds]);
  338. float_data([_|Cs], Ds) ->
  339. float_data(Cs, Ds).
  340. %% fwrite_g(Float, Field, Adjust, Precision, PadChar)
  341. %% Use the f form if Float is >= 0.1 and < 1.0e4,
  342. %% and the prints correctly in the f form, else the e form.
  343. %% Precision always means the # of significant digits.
  344. fwrite_g(Fl, F, Adj, none, Pad) ->
  345. fwrite_g(Fl, F, Adj, 6, Pad);
  346. fwrite_g(Fl, F, Adj, P, Pad) when P >= 1 ->
  347. A = abs(Fl),
  348. E = if A < 1.0e-1 -> -2;
  349. A < 1.0e0 -> -1;
  350. A < 1.0e1 -> 0;
  351. A < 1.0e2 -> 1;
  352. A < 1.0e3 -> 2;
  353. A < 1.0e4 -> 3;
  354. true -> fwrite_f
  355. end,
  356. if P =< 1, E =:= -1;
  357. P-1 > E, E >= -1 ->
  358. fwrite_f(Fl, F, Adj, P-1-E, Pad);
  359. P =< 1 ->
  360. fwrite_e(Fl, F, Adj, 2, Pad);
  361. true ->
  362. fwrite_e(Fl, F, Adj, P, Pad)
  363. end.
  364. %% string(String, Field, Adjust, Precision, PadChar)
  365. string(S, none, _Adj, none, _Pad) -> S;
  366. string(S, F, Adj, none, Pad) ->
  367. string_field(S, F, Adj, lists:flatlength(S), Pad);
  368. string(S, none, _Adj, P, Pad) ->
  369. string_field(S, P, left, lists:flatlength(S), Pad);
  370. string(S, F, Adj, P, Pad) when F >= P ->
  371. N = lists:flatlength(S),
  372. if F > P ->
  373. if N > P ->
  374. adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
  375. N < P ->
  376. adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
  377. true -> % N == P
  378. adjust(S, chars(Pad, F-P), Adj)
  379. end;
  380. true -> % F == P
  381. string_field(S, F, Adj, N, Pad)
  382. end.
  383. string_field(S, F, _Adj, N, _Pad) when N > F ->
  384. flat_trunc(S, F);
  385. string_field(S, F, Adj, N, Pad) when N < F ->
  386. adjust(S, chars(Pad, F-N), Adj);
  387. string_field(S, _, _, _, _) -> % N == F
  388. S.
  389. %% unprefixed_integer(Int, Field, Adjust, Base, PadChar, Lowercase)
  390. %% -> [Char].
  391. unprefixed_integer(Int, F, Adj, Base, Pad, Lowercase)
  392. when Base >= 2, Base =< 1+$Z-$A+10 ->
  393. if Int < 0 ->
  394. S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
  395. term([$-|S], F, Adj, none, Pad);
  396. true ->
  397. S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
  398. term(S, F, Adj, none, Pad)
  399. end.
  400. %% prefixed_integer(Int, Field, Adjust, Base, PadChar, Prefix, Lowercase)
  401. %% -> [Char].
  402. prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
  403. when Base >= 2, Base =< 1+$Z-$A+10 ->
  404. if Int < 0 ->
  405. S = cond_lowercase(erlang:integer_to_list(-Int, Base), Lowercase),
  406. term([$-,Prefix|S], F, Adj, none, Pad);
  407. true ->
  408. S = cond_lowercase(erlang:integer_to_list(Int, Base), Lowercase),
  409. term([Prefix|S], F, Adj, none, Pad)
  410. end.
  411. %% char(Char, Field, Adjust, Precision, PadChar) -> [Char].
  412. char(C, none, _Adj, none, _Pad) -> [C];
  413. char(C, F, _Adj, none, _Pad) -> chars(C, F);
  414. char(C, none, _Adj, P, _Pad) -> chars(C, P);
  415. char(C, F, Adj, P, Pad) when F >= P ->
  416. adjust(chars(C, P), chars(Pad, F - P), Adj).
  417. %% newline(Field, Adjust, Precision, PadChar) -> [Char].
  418. newline(none, _Adj, _P, _Pad) -> "\n";
  419. newline(F, right, _P, _Pad) -> chars($\n, F).
  420. %%
  421. %% Utilities
  422. %%
  423. adjust(Data, [], _) -> Data;
  424. adjust(Data, Pad, left) -> [Data|Pad];
  425. adjust(Data, Pad, right) -> [Pad|Data].
  426. %% Flatten and truncate a deep list to at most N elements.
  427. flat_trunc(List, N) when is_integer(N), N >= 0 ->
  428. flat_trunc(List, N, []).
  429. flat_trunc(L, 0, R) when is_list(L) ->
  430. lists:reverse(R);
  431. flat_trunc([H|T], N, R) ->
  432. flat_trunc(T, N-1, [H|R]);
  433. flat_trunc([], _, R) ->
  434. lists:reverse(R).
  435. %% A deep version of string:chars/2,3
  436. chars(_C, 0) ->
  437. [];
  438. chars(C, 1) ->
  439. [C];
  440. chars(C, 2) ->
  441. [C,C];
  442. chars(C, 3) ->
  443. [C,C,C];
  444. chars(C, N) when is_integer(N), (N band 1) =:= 0 ->
  445. S = chars(C, N bsr 1),
  446. [S|S];
  447. chars(C, N) when is_integer(N) ->
  448. S = chars(C, N bsr 1),
  449. [C,S|S].
  450. %chars(C, N, Tail) ->
  451. % [chars(C, N)|Tail].
  452. %% Lowercase conversion
  453. cond_lowercase(String, true) ->
  454. lowercase(String);
  455. cond_lowercase(String,false) ->
  456. String.
  457. lowercase([H|T]) when is_integer(H), H >= $A, H =< $Z ->
  458. [(H-$A+$a)|lowercase(T)];
  459. lowercase([H|T]) ->
  460. [H|lowercase(T)];
  461. lowercase([]) ->
  462. [].