Skip to content

Commit

Permalink
io: Add types io:standard_io/standard_error and document them
Browse files Browse the repository at this point in the history
  • Loading branch information
garazdawi committed Jul 10, 2023
1 parent 27a322b commit cc27cba
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 107 deletions.
19 changes: 17 additions & 2 deletions erts/doc/src/erlang.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2915,8 +2915,23 @@ uncompiled code with the same arity are mapped to the same list by
groups have a <em>group leader</em>. All I/O from the group
is channeled to the group leader. When a new process is
spawned, it gets the same group leader as the spawning
process. Initially, at system startup, <c>init</c> is both
its own group leader and the group leader of all processes.</p>
process.</p>
<p>Initially, at system startup, <c>init</c> is both
its own group leader and the group leader of all processes.
During the boot of a system the group leader for processes
will be changed depending on the need of the system. Some examples
where this is done are:</p>
<list type="bulleted">
<item>When an application is started, the top supervisor of that
application will have its group leader set to the application
master. See <seemfa marker="kernel:application#start/2"><c>
application:start/2</c></seemfa> for more details.</item>
<item>When running tests, both <seeapp marker="common_test:index"><c>common_test</c></seeapp> and
<seeerl marker="eunit:eunit"><c>eunit</c></seeerl> set the
group leader in order to capture any I/O from the testcase.</item>
<item>The <seeerl marker="stdlib:shell">interactive shell</seeerl>
sets the group leader to intercept I/O.</item>
</list>
</desc>
</func>

Expand Down
6 changes: 2 additions & 4 deletions erts/doc/src/escript_cmd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,8 @@ $ <input>escript factorial 5</input></pre>
I/O-server, however, must be set explicitly as follows:</p>
<code>
io:setopts([{encoding, latin1}])</code>
<p>The default encoding of the I/O-server for <c>standard_io</c>
is <c>unicode</c> if its supported, as the script runs in a
non-interactive terminal.
(see section
<p>The default encoding of the I/O-server for <seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>
is <c>unicode</c> if its supported. (see section
<seeguide marker="stdlib:unicode_usage#unicode_options_summary">
Summary of Options</seeguide>) in the STDLIB User's Guide.</p>
</note>
Expand Down
2 changes: 1 addition & 1 deletion lib/common_test/doc/src/run_test_chapter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@
<item>A confirmation when the test run is complete.</item>
<item>Some special information, such as error reports, progress
reports, and printouts written with <c>erlang:display/1</c>, or <c>io:format/3</c>
specifically addressed to a receiver other than <c>standard_io</c>
specifically addressed to a receiver other than <seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>
(for example, the default group leader process <c>user</c>).</item>
</list>

Expand Down
15 changes: 10 additions & 5 deletions lib/erl_docgen/priv/bin/validate_links.escript
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,18 @@ validate_link(Filename, "seemfa", Line, Link, CachedFiles) ->
end;
validate_link(Filename, LinkType = "seetype", Line, Link, CachedFiles) ->
{App,Mod,Type} = ParsedLink = parse_link(Filename, maps:get(m2a,CachedFiles), Link),
Types = maps:get(datatypes,maps:get({App,Mod},CachedFiles)),
case lists:member(Type, Types) of
false ->
case maps:find({App,Mod},CachedFiles) of
error ->
fail(Line, "Could not find documentation for ~s when "
"resolving link",[App ++ ":" ++ Mod ++ "#" ++ Type]);
_ ->
validate_type(Line,LinkType,read_link(Line, ParsedLink, CachedFiles))
{ok, AppData} ->
case lists:member(Type, maps:get(datatypes,AppData)) of
false ->
fail(Line, "Could not find documentation for ~s when "
"resolving link",[App ++ ":" ++ Mod ++ "#" ++ Type]);
_ ->
validate_type(Line,LinkType,read_link(Line, ParsedLink, CachedFiles))
end
end;
validate_link({"jinterface","jinterface_users_guide"},"seefile",_, _, _) ->
%% Skip links to java documentation
Expand Down
11 changes: 7 additions & 4 deletions lib/kernel/doc/src/file.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1374,8 +1374,8 @@ f.txt: {person, "kalle", 25}.
read from the file, while the position in the file can be moved much more than
this number when reading a Unicode file.</p>
<p>Also, if <c>encoding</c> is set to something else than <c>latin1</c>,
the <c>read/3</c> call fails if the data contains characters larger than 255,
which is why module <seeerl marker="stdlib:io"><c>io(3)</c></seeerl>
the <c>read/2</c> call fails if the data contains characters larger than 255,
which is why <seemfa marker="stdlib:io#get_chars/3"><c>io:get_chars/3</c></seemfa>
is to be preferred when reading such a file.</p>
<p>The function returns:</p>
<taglist>
Expand Down Expand Up @@ -1628,7 +1628,7 @@ f.txt: {person, "kalle", 25}.
raw line-oriented reading.</p>
<p>If <c>encoding</c> is set to something else than <c>latin1</c>, the
<c>read_line/1</c> call fails if the data contains characters larger than 255,
why module <seeerl marker="stdlib:io"><c>io(3)</c></seeerl> is to be
why <seemfa marker="stdlib:io#get_line/2"><c>io:get_line/2</c></seemfa> is to be
preferred when reading such a file.</p>
<p>The function returns:</p>
<taglist>
Expand Down Expand Up @@ -1978,7 +1978,10 @@ f.txt: {person, "kalle", 25}.
<p>If the file is opened with <c>encoding</c> set to something else than
<c>latin1</c>, each byte written can result in many bytes being written to
the file, as the byte range 0..255 can represent anything between one and
four bytes depending on value and UTF encoding type.</p>
four bytes depending on value and UTF encoding type. If you want to write
<seetype marker="stdlib:unicode#chardata"><c>unicode:chardata()</c></seetype>
to the <c><anno>IoDevice</anno></c> you should use <seemfa marker="stdlib:io#put_chars/2">
<c>io:put_chars/2</c></seemfa> instead.</p>
<p>Typical error reasons:</p>
<taglist>
<tag><c>ebadf</c></tag>
Expand Down
2 changes: 1 addition & 1 deletion lib/kernel/doc/src/logger_chapter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ logger:debug(#{got => connection_request, id => Id, state => State},
configuration file that configures Logger according to the
description.</p>
<p>Modify the default handler to print to a file instead of
<c>standard_io</c>:</p>
<seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>:</p>
<code>
[{kernel,
[{logger,
Expand Down
12 changes: 7 additions & 5 deletions lib/kernel/doc/src/logger_std_h.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
<description>
<p>This is the standard handler for Logger.
Multiple instances of this handler can be added to
Logger, and each instance prints logs to <c>standard_io</c>,
<c>standard_error</c>, or to file.</p>
Logger, and each instance prints logs to <seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>,
<seetype marker="stdlib:io#standard_error"><c>standard_error</c></seetype>, or to file.</p>
<p>The handler has an overload protection mechanism that keeps the handler
process and the Kernel application alive during high loads of log
events. How overload protection works, and how to configure it, is
Expand All @@ -55,12 +55,14 @@
is stored in a sub map with the key <c>config</c>, and can contain the
following parameters:</p>
<taglist>
<tag><marker id="type"/><c>type = standard_io | standard_error | file | {device, io:device()}</c></tag>
<tag><marker id="type"/><c>type = </c><seetype marker="stdlib:io#standard_io"><c>io:standard_io()</c></seetype><c>
| </c><seetype marker="stdlib:io#standard_error"><c>io:standard_error()</c></seetype><c> | file
| {device, </c><seetype marker="stdlib:io#device"><c>io:device()</c></seetype><c>}</c></tag>
<item>
<p>Specifies the log destination.</p>
<p>The value is set when the handler is added, and it cannot
be changed in runtime.</p>
<p>Defaults to <c>standard_io</c>, unless
<p>Defaults to <seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>, unless
parameter <seeerl marker="#file"><c>file</c></seeerl> is
given, in which case it defaults to <c>file</c>.</p>
</item>
Expand Down Expand Up @@ -182,7 +184,7 @@ logger:add_handler(my_standard_h, logger_std_h,
filesync_repeat_interval => 1000}}).
</code>
<p>To set the default handler, that starts initially with
the Kernel application, to log to file instead of <c>standard_io</c>,
the Kernel application, to log to file instead of <seetype marker="stdlib:io#standard_io"><c>standard_io</c></seetype>,
change the Kernel default logger configuration. Example:</p>
<code type="none">
erl -kernel logger '[{handler,default,logger_std_h,
Expand Down
6 changes: 3 additions & 3 deletions lib/kernel/src/file.erl
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ allocate(#file_descriptor{module = Module} = Handle, Offset, Length) ->
Module:allocate(Handle, Offset, Length).

-spec read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} when
IoDevice :: io_device() | atom(),
IoDevice :: io_device() | io:device(),
Number :: non_neg_integer(),
Data :: string() | binary(),
Reason :: posix()
Expand All @@ -604,7 +604,7 @@ read(_, _) ->
{error, badarg}.

-spec read_line(IoDevice) -> {ok, Data} | eof | {error, Reason} when
IoDevice :: io_device() | atom(),
IoDevice :: io_device() | io:device(),
Data :: string() | binary(),
Reason :: posix()
| badarg
Expand Down Expand Up @@ -668,7 +668,7 @@ pread(_, _, _) ->
{error, badarg}.

-spec write(IoDevice, Bytes) -> ok | {error, Reason} when
IoDevice :: io_device() | atom(),
IoDevice :: io_device() | io:device(),
Bytes :: iodata(),
Reason :: posix() | badarg | terminated.

Expand Down
155 changes: 95 additions & 60 deletions lib/stdlib/doc/src/io.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
protocols. Normally, it is a <c>IoDevice</c> returned by
<seemfa marker="kernel:file#open/2"><c>file:open/2</c></seemfa>.
If no <seetype marker="#device"><c>IoDevice</c></seetype> is given,
<c>standard_io</c> is used.</p>
<seetype marker="#standard_io"><c>standard_io</c></seetype> is used.</p>

<p>For a description of the I/O protocols, see section
<seeguide marker="io_protocol">The Erlang I/O Protocol</seeguide>
Expand Down Expand Up @@ -71,12 +71,74 @@
<datatype>
<name name="device"/>
<desc>
<p>An I/O device, either <c>standard_io</c>, <c>standard_error</c>, a
registered name, or a pid handling I/O protocols (returned from
<p>An I/O device, either <c>standard_io</c>, <c>standard_error</c>, <c>user</c>,
a registered name, or a pid handling I/O protocols (returned from
<seemfa marker="kernel:file#open/2"><c>file:open/2</c></seemfa>).</p>
<p>For more information about the built-in devices see
<seeerl marker="#standard-input-output">Standard Input/Output</seeerl>
and <seeerl marker="#standard-error">Standard Error</seeerl>.
</desc>
</datatype>
<datatype>
<name name="standard_io"/>
<desc>
<p>All Erlang processes have a default standard I/O device. This
device is used when no <c>IoDevice</c> argument is specified in
the function calls in this module. However, it is sometimes desirable to
use an explicit <c>IoDevice</c> argument that refers to the
default I/O device. This is the case with functions that can
access either a file or the default I/O device. The atom
<c>standard_io</c> has this special meaning. The following example
illustrates this:
</p>

<pre>
27> <input>io:read('enter>').</input>
enter><input>foo.</input>
{ok,foo}
28> <input>io:read(standard_io, 'enter>').</input>
enter><input>bar.</input>
{ok,bar}</pre>

<p>By default all I/O sent to <c>standard_io</c> will en up in the
<seetype marker="#user"><c>user</c></seetype> I/O device of the node
that spawned the calling process.
</p>

<p><c>standard_io</c> is an alias for <seemfa marker="erts:erlang#group_leader/0"><c>
group_leader/0</c></seemfa>, so in order to change where the default input/output
requests are sent you can change the group leader for the current process using
<seemfa marker="erts:erlang#group_leader/2"><c>
group_leader(NewGroupLeader, self())</c></seemfa>.
</p>

</desc>
</datatype>
<datatype>
<name name="standard_error"/>
<desc>
<p>The I/O device <c>standard_error</c> can be used to direct
output to whatever the current operating system considers a suitable
I/O device for error output. This can be useful when standard output is
redirected. Example on a Unix-like operating system:
</p>

<pre>
$ <input>erl -noinput -eval 'io:format(standard_error,"Error: ~s~n",["error 11"]),'\</input>
<input>'init:stop().' > /dev/null</input>
Error: error 11</pre>
</desc>
</datatype>
<datatype>
<name name="user"/>
<desc>
<p>An I/O device that can be used to interact with the node local
<c>stdout</c> and <c>stdin</c>. This can be either a terminal,
a pipe, a file, or a combination. You can use <seemfa marker="#getopts/0">
<c>getopts/0</c></seemfa> to get more information about the
I/O device.
</p>
<p>See <seeguide marker="unicode_usage#the-interactive-shell">The Interactive Shell</seeguide>
and <seeguide marker="unicode_usage#escripts-and-non-interactive-i-o">Escripts and non-interactive I/O</seeguide>
in the Using Unicode In Erlang User's Guide for details on how Unicode
is handled by <c>user</c>.
</p>
</desc>
</datatype>
Expand Down Expand Up @@ -743,7 +805,10 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
the data can represent codepoints &gt; 255 (the
<c>latin1</c> range). If the I/O server is set to deliver
binaries, they are encoded in UTF-8 (regardless of whether
the I/O device supports Unicode).</p>
the I/O device supports Unicode). If you want the data to
be returned as a latin1 encoded binary you should use
<seemfa marker="kernel:file#read/2"><c>file:read/2</c></seemfa>
instead.</p>
</item>
<tag><c>eof</c></tag>
<item>
Expand Down Expand Up @@ -775,7 +840,10 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
the data can represent codepoints &gt; 255 (the
<c>latin1</c> range). If the I/O server is set to deliver
binaries, they are encoded in UTF-8 (regardless of if
the I/O device supports Unicode).</p>
the I/O device supports Unicode). If you want the data to
be returned as a latin1 encoded binary you should use
<seemfa marker="kernel:file#read_line/1"><c>file:read_line/1</c></seemfa>
instead.</p>
</item>
<tag><c>eof</c></tag>
<item>
Expand Down Expand Up @@ -816,8 +884,11 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
<p>This example is, as can be seen, run in an environment where the
terminal supports Unicode input and output.</p>
<p>The <c>terminal</c> option is read only and indicates whether
the output stream is a terminal or not.
See <seemfa marker="#setopts/1"><c>setopts/1</c></seemfa> for a description
the output stream is a terminal or not. When it is a terminal,
most systems that Erlang runs on allows the use of
<url href="https://en.wikipedia.org/wiki/ANSI_escape_code">ANSI escape codes</url>
to control what the terminal outputs.</p>
<p>See <seemfa marker="#setopts/1"><c>setopts/1</c></seemfa> for a description
of the other options.</p>
</desc>
</func>
Expand Down Expand Up @@ -965,7 +1036,10 @@ enter><input>abc("hey".</input>
<fsummary>Write a list of characters.</fsummary>
<desc>
<p>Writes the characters of <c><anno>CharData</anno></c> to the I/O
server (<c><anno>IoDevice</anno></c>).</p>
server (<c><anno>IoDevice</anno></c>). If you want to write latin1 encoded
bytes to the I/O server you should use
<seemfa marker="kernel:file#write/2"><c>file:write/2</c></seemfa>
instead.</p>
</desc>
</func>

Expand Down Expand Up @@ -1214,15 +1288,19 @@ fun("") -> {yes, "quit", []};
<note><p>
Prior to OTP 26.0, when Erlang was started with the
<c>-oldshell</c> or <c>-noshell</c> flags (for example, in an
<c>escript</c>), the default encoding for <c>standard_io</c> was
set to <c>latin1</c>, meaning that any characters &gt; codepoint
255 were escaped and that input was expected to be plain 8-bit
ISO Latin-1. As of OTP 26.0, <c>standard_io</c> always defaults
<c>escript</c>), the default encoding for <seetype marker="#standard_io">
<c>standard_io</c></seetype> was set to <c>latin1</c>, meaning
that any characters &gt; codepoint 255 were escaped and that input
was expected to be plain 8-bit ISO Latin-1. As of OTP 26.0,
<seetype marker="#standard_io"><c>standard_io</c></seetype> always defaults
to <c>unicode</c> if its supported, otherwise <c>latin1</c>.
</p><p>
If you want to send raw bytes on <c>standard_io</c>, you now
always need to explicitly set the encoding to <c>latin1</c>;
If you want to send raw bytes on <seetype marker="#standard_io"><c>standard_io</c></seetype>,
you now always need to explicitly set the encoding to <c>latin1</c>;
otherwise, code points 128-255 will be converted to UTF-8.
This is best done by setting the kernel configuration parameter
<seeapp marker="kernel:kernel_app#standard_io_encoding">standard_io_encoding</seeapp>
to <c>latin1</c>.
</p></note>
<p>Files can also be set in <c>{encoding, unicode}</c>, meaning
that data is written and read as UTF-8. More encodings are
Expand Down Expand Up @@ -1261,49 +1339,6 @@ fun("") -> {yes, "quit", []};
</func>
</funcs>

<section>
<title>Standard Input/Output</title>
<p>All Erlang processes have a default standard I/O device. This
device is used when no <c>IoDevice</c> argument is specified in
the function calls in this module. However, it is sometimes desirable to
use an explicit <c>IoDevice</c> argument that refers to the
default I/O device. This is the case with functions that can
access either a file or the default I/O device. The atom
<c>standard_io</c> has this special meaning. The following example
illustrates this:</p>

<pre>
27> <input>io:read('enter>').</input>
enter><input>foo.</input>
{ok,foo}
28> <input>io:read(standard_io, 'enter>').</input>
enter><input>bar.</input>
{ok,bar}</pre>

<p><c>standard_io</c> is an alias for <seemfa marker="erts:erlang#group_leader/0"><c>
group_leader/0</c></seemfa>, so in order to change where the default input/output
requests are sent you can change the group leader for the current process using
<seemfa marker="erts:erlang#group_leader/2"><c>
group_leader(NewGroupLeader, self()).</c></seemfa></p>

<p>There is always a process registered under the name of
<c>user</c>. This can be used for sending output to the user.</p>
</section>

<section>
<title>Standard Error</title>
<p>In certain situations, especially when the standard output is
redirected, access to an I/O server specific for error messages can be
convenient. The I/O device <c>standard_error</c> can be used to direct
output to whatever the current operating system considers a suitable
I/O device for error output. Example on a Unix-like operating system:</p>

<pre>
$ <input>erl -noshell -noinput -eval 'io:format(standard_error,"Error: ~s~n",["error 11"]),'\</input>
<input>'init:stop().' > /dev/null</input>
Error: error 11</pre>
</section>

<section>
<title>Error Information</title>
<p>The <c>ErrorInfo</c> mentioned in this module is the standard
Expand Down
Loading

0 comments on commit cc27cba

Please sign in to comment.