File 5571-ftp-generate-documentation-from-source-code.patch of Package erlang
From ce8323bb96c3a3d267cd9acbb6dd6e8dfa99c3b0 Mon Sep 17 00:00:00 2001
From: Kiko Fernandez-Reyes <kiko@erlang.org>
Date: Fri, 17 Feb 2023 15:09:41 +0100
Subject: [PATCH 1/2] ftp: generate documentation from source code
Generates the types in the documentation of `ftp` from the source code.
Before this change, the documentation was hard-coded in `ftp.xml`.
---
lib/ftp/doc/specs/.gitignore | 1 +
lib/ftp/doc/src/Makefile | 4 +
lib/ftp/doc/src/ftp.xml | 736 +++++++++++++----------------------
lib/ftp/doc/src/specs.xml | 4 +
lib/ftp/test/ftp_SUITE.erl | 2 +-
5 files changed, 290 insertions(+), 457 deletions(-)
create mode 100644 lib/ftp/doc/specs/.gitignore
create mode 100644 lib/ftp/doc/src/specs.xml
diff --git a/lib/ftp/doc/specs/.gitignore b/lib/ftp/doc/specs/.gitignore
new file mode 100644
index 0000000000..322eebcb06
--- /dev/null
+++ b/lib/ftp/doc/specs/.gitignore
@@ -0,0 +1 @@
+specs_*.xml
diff --git a/lib/ftp/doc/src/Makefile b/lib/ftp/doc/src/Makefile
index 38ad4f76d0..6223dcc72b 100644
--- a/lib/ftp/doc/src/Makefile
+++ b/lib/ftp/doc/src/Makefile
@@ -54,6 +54,10 @@ XML_FILES = \
$(XML_REF3_FILES) \
$(XML_APPLICATION_FILES)
+SPEC_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml)
+TOP_SPECS_FILE = specs.xml
+
+
# IMAGE_FILES = ftp.gif
include $(ERL_TOP)/make/doc.mk
diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml
index a70369a777..7ea8023b24 100644
--- a/lib/ftp/doc/src/ftp.xml
+++ b/lib/ftp/doc/src/ftp.xml
@@ -81,203 +81,23 @@
<p>The FTP client can be started and stopped dynamically in runtime by
calling the <c>ftp</c> application API
<c>ftp:open(Host, Options)</c> and
- <c>ftp:close(Client)</c>.</p>
-
- <p>The available configuration options are as follows:</p>
-
- <taglist>
- <tag>{host, Host}</tag>
- <item>
- <marker id="host"></marker>
- <p>Host = <c>string() | ip_address()</c></p>
- </item>
-
- <tag>{port, Port}</tag>
- <item>
- <marker id="port"></marker>
- <p>Port = <c>integer() > 0</c></p>
- <p>Default is <c>0</c> which aliases to <c>21</c> or <c>990</c> when used with
- <seeerl marker="#open"><c>{tls_sec_method,ftps}</c></seeerl>).</p>
- </item>
-
- <tag>{mode, Mode}</tag>
- <item>
- <marker id="mode"></marker>
- <p>Mode = <c>active | passive</c></p>
- <p>Default is <c>passive</c>.</p>
- </item>
-
- <tag>{verbose, Verbose}</tag>
- <item>
- <marker id="verbose"></marker>
- <p>Verbose = <c>boolean()</c> </p>
- <p>Determines if the FTP communication is to be
- verbose or not.</p>
- <p>Default is <c>false</c>.</p>
- </item>
-
- <tag>{debug, Debug}</tag>
- <item>
- <marker id="debug"></marker>
- <p>Debug = <c>trace | debug | disable</c> </p>
- <p>Debugging using the dbg toolkit. </p>
- <p>Default is <c>disable</c>.</p>
- </item>
-
- <tag>{ipfamily, IpFamily}</tag>
- <item>
- <marker id="ipfamily"></marker>
- <p>IpFamily = <c>inet | inet6 | inet6fb4</c> </p>
- <p>With <c>inet6fb4</c> the client behaves as before, that is,
- tries to use IPv6, and only if that does not work it
- uses IPv4).</p>
- <p>Default is <c>inet</c> (IPv4).</p>
- </item>
-
- <tag>{timeout, Timeout}</tag>
- <item>
- <marker id="timeout"></marker>
- <p>Timeout = <c>non_neg_integer()</c></p>
- <p>Connection time-out.</p>
- <p>Default is <c>60000</c> (milliseconds).</p>
- </item>
-
- <tag>{dtimeout, DTimeout}</tag>
- <item>
- <marker id="dtimeout"></marker>
- <p>DTimeout = <c>non_neg_integer() | infinity</c> </p>
- <p>Data connect time-out.
- The time the client waits for the server to connect to the
- data socket.</p>
- <p>Default is <c>infinity</c>. </p>
- </item>
-
- <tag>{progress, Progress}</tag>
- <item>
- <marker id="progress"></marker>
- <p>Progress = <c>ignore | {CBModule, CBFunction, InitProgress}</c></p>
- <p><c>CBModule = atom()</c>, <c>CBFunction = atom()</c></p>
- <p><c>InitProgress = term()</c></p>
- <p>Default is <c>ignore</c>.</p>
- </item>
-
- </taglist>
-
- <p>Option <c>progress</c> is intended to be used by applications that
- want to create some type of progress report, such as a progress bar in
- a GUI. Default for the progress option is <c>ignore</c>,
- that is, the option is not used. When the progress option is
- specified, the following happens when <c>ftp:send/[3,4]</c> or
- <c>ftp:recv/[3,4]</c> are called:</p>
-
- <list type="bulleted">
- <item>
- <p>Before a file is transferred, the following call is
- made to indicate the start of the file transfer and how large
- the file is. The return value of the callback function
- is to be a new value for the <c>UserProgressTerm</c> that will
- be used as input the next time the callback function is
- called.</p>
- <p><c>
- CBModule:CBFunction(InitProgress, File, {file_size, FileSize})
- </c></p>
- </item>
-
- <item>
- <p>Every time a chunk of bytes is transferred the
- following call is made:</p>
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, TransferSize})
- </c></p>
- </item>
-
- <item>
- <p>At the end of the file the following call is
- made to indicate the end of the transfer:</p>
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, 0})
- </c></p>
- </item>
- </list>
-
- <p>The callback function is to be defined as follows:</p>
-
- <p><c>
- CBModule:CBFunction(UserProgressTerm, File, Size) -> UserProgressTerm
- </c></p>
-
- <p><c>
- CBModule = CBFunction = atom()
- </c></p>
-
- <p><c>
- UserProgressTerm = term()
- </c></p>
-
- <p><c>
- File = string()
- </c></p>
-
- <p><c>
- Size = {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown}
- </c></p>
-
- <p>For remote files, <c>ftp</c> cannot determine the
- file size in a platform independent way. In this case the size
- becomes <c>unknown</c> and it is left to the application to
- determine the size.</p>
-
- <note>
- <p>The callback is made by a middleman process, hence the
- file transfer is not affected by the code in the progress
- callback function. If the callback crashes, this is
- detected by the FTP connection process, which then prints an
- info-report and goes on as if the progress option was set
- to <c>ignore</c>.</p>
- </note>
-
- <p>The file transfer type is set to the default of the FTP server
- when the session is opened. This is usually ASCII mode.
- </p>
-
- <p>The current local working directory (compare <c>lpwd/1</c>) is set
- to the value reported by <c>file:get_cwd/1</c>, the wanted
- local directory.
- </p>
-
- <p>The return value <c>Pid</c> is used as a reference to the
- newly created FTP client in all other functions, and they are to
- be called by the process that created the connection. The FTP
- client process monitors the process that created it and
- terminates if that process terminates.</p>
+ <c>ftp:close(Client)</c>. </p>
</section>
<section>
- <title>DATA TYPES</title>
+ <title>Data Types</title>
<p>The following type definitions are used by more than one
function in the FTP client API:</p>
<p><c>pid()</c> = identifier of an FTP connection</p>
<p><c>string()</c> = list of ASCII characters</p>
- <p><c>shortage_reason()</c> = <c>etnospc | epnospc</c></p>
- <p><c>restriction_reason()</c> = <c>epath | efnamena | elogin | enotbinary</c>
- - all restrictions are not always relevant to all functions
- </p>
- <p><c>common_reason()</c> = <c>econn | eclosed | term()</c>
- - some explanation of what went wrong</p>
-
<marker id="account"></marker>
</section>
<funcs>
<func>
- <name since="">account(Pid, Account) -> ok | {error, Reason}</name>
+ <name since="" name="account" arity="2" />
<fsummary>Specifies which account to use.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Account = string()</v>
- <v>Reason = eacct | common_reason()</v>
- </type>
<desc>
<p>Sets the account for an operation, if needed.</p>
@@ -288,15 +108,10 @@
</func>
<func>
- <name since="">append(Pid, LocalFile) -> </name>
- <name since="">append(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfers a file to remote server, and appends it to
+ <name since="" name="append" arity="2"/>
+ <name since="" name="append" arity="3"/>
+ <fsummary>Transfers a file to remote server, and appends it to
<c>Remotefile</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>LocalFile = RemoteFile = string()</v>
- <v>Reason = epath | elogin | etnospc | epnospc | efnamena | common_reason</v>
- </type>
<desc>
<p>Transfers the file <c>LocalFile</c> to the remote server. If
<c>RemoteFile</c> is specified, the name of the remote file that the
@@ -309,17 +124,11 @@
</func>
<func>
- <name since="">append_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
+ <name since="" name="append_bin" arity="3"/>
<fsummary>Transfers a binary into a remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason()| shortage_reason() | common_reason()</v>
- </type>
<desc>
<p>Transfers the binary <c>Bin</c> to the remote server and appends
- it to the file <c>RemoteFile</c>. If the file does not exist, it
+ it to the file <c><anno>RemoteFile</anno></c>. If the file does not exist, it
is created.</p>
<marker id="append_chunk"></marker>
@@ -327,15 +136,10 @@
</func>
<func>
- <name since="">append_chunk(Pid, Bin) -> ok | {error, Reason}</name>
+ <name since="" name="append_chunk" arity="2" />
<fsummary>Appends a chunk to the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = echunk | restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Transfers the chunk <c>Bin</c> to the remote server, which
+ <p>Transfers the chunk <c><anno>Bin</anno></c> to the remote server, which
appends it to the file specified in the call to
<c>append_chunk_start/2</c>.</p>
<p>For some errors, for example, file system full, it is
@@ -347,16 +151,11 @@
</func>
<func>
- <name since="">append_chunk_start(Pid, File) -> ok | {error, Reason}</name>
+ <name since="" name="append_chunk_start" arity="2" />
<fsummary>Starts transfer of file chunks for appending to <c>File</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Starts the transfer of chunks for appending to the file
- <c>File</c> at the remote server. If the file does not exist,
+ <c><anno>RemoteFile</anno></c> at the remote server. If the file does not exist,
it is created.</p>
<marker id="append_chunk_end"></marker>
@@ -364,12 +163,8 @@
</func>
<func>
- <name since="">append_chunk_end(Pid) -> ok | {error, Reason}</name>
+ <name since="" name="append_chunk_end" arity="1"/>
<fsummary>Stops transfer of chunks for appending.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = echunk | restriction_reason() | shortage_reason() </v>
- </type>
<desc>
<p>Stops transfer of chunks for appending to the remote server.
The file at the remote server, specified in the call to
@@ -380,27 +175,19 @@
</func>
<func>
- <name since="">cd(Pid, Dir) -> ok | {error, Reason}</name>
+ <name since="" name="cd" arity="2" />
<fsummary>Changes remote working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason() </v>
- </type>
<desc>
<p>Changes the working directory at the remote server to
- <c>Dir</c>.</p>
+ <c><anno>Dir</anno></c>.</p>
<marker id="close"></marker>
</desc>
</func>
<func>
- <name since="">close(Pid) -> ok</name>
+ <name since="" name="close" arity="1" />
<fsummary>Ends the FTP session.</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Ends an FTP session, created using function
<seeerl marker="#open">open</seeerl>.</p>
@@ -410,13 +197,8 @@
</func>
<func>
- <name since="">delete(Pid, File) -> ok | {error, Reason}</name>
+ <name since="" name="delete" arity="2"/>
<fsummary>Deletes a file at the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Deletes the file <c>File</c> at the remote server.</p>
@@ -425,11 +207,8 @@
</func>
<func>
- <name since="">formaterror(Tag) -> string()</name>
+ <name since="" name="formaterror" arity="1"/>
<fsummary>Returns error diagnostics.</fsummary>
- <type>
- <v>Tag = {error, atom()} | atom()</v>
- </type>
<desc>
<p>Given an error return value <c>{error, AtomReason}</c>,
this function returns a readable string describing the error.</p>
@@ -439,26 +218,18 @@
</func>
<func>
- <name since="">lcd(Pid, Dir) -> ok | {error, Reason}</name>
+ <name since="" name="lcd" arity="2" />
<fsummary>Changes local working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason()</v>
- </type>
<desc>
- <p>Changes the working directory to <c>Dir</c> for the local client.</p>
+ <p>Changes the working directory to <c><anno>Dir</anno></c> for the local client.</p>
<marker id="lpwd"></marker>
</desc>
</func>
<func>
- <name since="">lpwd(Pid) -> {ok, Dir}</name>
+ <name since="" name="lpwd" arity="1" />
<fsummary>Gets local current working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- </type>
<desc>
<p>Returns the current working directory at the local client.</p>
@@ -469,21 +240,15 @@
</func>
<func>
- <name since="">ls(Pid) -> </name>
- <name since="">ls(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name>
+ <name since="" name="ls" arity="1" />
+ <name since="" name="ls" arity="2" />
<fsummary>List of files.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Pathname = string()</v>
- <v>Listing = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Returns a list of files in long format.</p>
- <p><c>Pathname</c> can be a directory, a group of files, or
- a file. The <c>Pathname</c> string can contain wildcards.</p>
+ <p><c><anno>Dir</anno></c> can be a directory or
+ a file. The <c><anno>Dir</anno></c> string can contain wildcards.</p>
<p><c>ls/1</c> implies the current remote directory of the user.</p>
- <p>The format of <c>Listing</c> depends on the operating system.
+ <p>The format of <c><anno>Listing</anno></c> depends on the operating system.
On UNIX, it is typically produced from the output of the
<c>ls -l</c> shell command.</p>
@@ -492,15 +257,10 @@
</func>
<func>
- <name since="">mkdir(Pid, Dir) -> ok | {error, Reason}</name>
+ <name since="" name="mkdir" arity="2" />
<fsummary>Creates a remote directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Creates the directory <c>Dir</c> at the remote server.</p>
+ <p>Creates the directory <c><anno>Dir</anno></c> at the remote server.</p>
<marker id="nlist"></marker>
<marker id="nlist1"></marker>
@@ -509,21 +269,15 @@
</func>
<func>
- <name since="">nlist(Pid) -> </name>
- <name since="">nlist(Pid, Pathname) -> {ok, Listing} | {error, Reason}</name>
+ <name since="" name="nlist" arity="1" />
+ <name since="" name="nlist" arity="2" />
<fsummary>List of files.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Pathname = string()</v>
- <v>Listing = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Returns a list of files in short format.</p>
- <p><c>Pathname</c> can be a directory, a group of files, or
- a file. The <c>Pathname</c> string can contain wildcards.</p>
+ <p><c><anno>Pathname</anno></c> can be a directory or
+ a file. The <c><anno>Pathname</anno></c> string can contain wildcards.</p>
<p><c>nlist/1</c> implies the current remote directory of the user.</p>
- <p>The format of <c>Listing</c> is a stream of
+ <p>The format of <c><anno>Listing</anno></c> is a stream of
filenames where each filename is separated by <CRLF> or
<NL>. Contrary to function <c>ls</c>, the purpose of
<c>nlist</c> is to enable a program to
@@ -534,76 +288,229 @@
</func>
<func>
- <name since="">open(Host) -> {ok, Pid} | {error, Reason}</name>
- <name since="">open(Host, Opts) -> {ok, Pid} | {error, Reason}</name>
+ <name since="" name="open" arity="1" />
+ <name since="" name="open" arity="2" />
<fsummary>Starts a FTP client.</fsummary>
- <type>
- <v>Host = string() | ip_address()</v>
- <v>Opts = options()</v>
- <v>options() = [option()]</v>
- <v>option() = start_option() | open_option()</v>
- <v>start_option() = {verbose, verbose()} | {debug, debug()}</v>
- <v>verbose() = boolean() (default is false)</v>
- <v>debug() = disable | debug | trace (default is disable)</v>
- <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {tls_sec_method, tls_sec_method()} | {tls_ctrl_session_reuse, boolean() (default is false)} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()} | {sock_ctrl, sock_opts()} | {sock_data_act, sock_opts()} | {sock_data_pass, sock_opts()}</v>
- <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v>
- <v>port() = non_neg_integer() (default is 0 which aliases to 21 or 990 when used with {tls_sec_method,ftps})</v>
- <v>mode() = active | passive (default is passive)</v>
- <v>tls_options() = [<seetype marker="ssl:ssl#tls_option">ssl:tls_option()</seetype>]</v>
- <v>tls_sec_method() = ftps | ftpes (default is ftpes)</v>
- <v>sock_opts() = [<seetype marker="kernel:gen_tcp#option">gen_tcp:option()</seetype> except for ipv6_v6only, active, packet, mode, packet_size and header</v>
- <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v>
- <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v>
- <v>progress() = ignore | {module(), function(), initial_data()} (default is ignore)</v>
- <v>module() = atom()</v>
- <v>function() = atom()</v>
- <v>initial_data() = term()</v>
- <v>Reason = ehost | term()</v>
- </type>
-
<desc>
<p>Starts a FTP client process and
opens a session with the FTP server at <c>Host</c>. </p>
+ <p>A session opened in this way is closed using function
+ <seeerl marker="#close">close</seeerl>.</p>
+
+ <p>The available configuration options are as follows:</p>
- <p>If option <c>{tls, tls_options()}</c> is present, the FTP session
- is transported over <c>tls</c> (<c>ftps</c>, see
- <url href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</url>).
- The list <c>tls_options()</c> can be empty. The function
- <seemfa marker="ssl:ssl#connect/3"><c>ssl:connect/3</c></seemfa>
+ <taglist>
+ <tag>{host, Host}</tag>
+ <item>
+ <marker id="host"></marker>
+ <p>Host = <c>string() | ip_address()</c></p>
+ </item>
+
+ <tag>{port, Port}</tag>
+ <item>
+ <marker id="port"></marker>
+ <p>Default is <c>0</c> which aliases to <c>21</c> or <c>990</c> when used with
+ <seeerl marker="#open"><c>{tls_sec_method,ftps}</c></seeerl>).</p>
+ </item>
+
+ <tag>{mode, Mode}</tag>
+ <item>
+ <marker id="mode"></marker>
+ <p>Default is <c>passive</c>.</p>
+ </item>
+
+ <tag>{verbose, Verbose}</tag>
+ <item>
+ <marker id="verbose"></marker>
+ <p>Determines if the FTP communication is to be
+ verbose or not.</p>
+ <p>Default is <c>false</c>.</p>
+ </item>
+
+ <tag>{debug, Debug}</tag>
+ <item>
+ <marker id="debug"></marker>
+ <p>Debugging using the dbg toolkit. </p>
+ <p>Default is <c>disable</c>.</p>
+ </item>
+
+ <tag>{ipfamily, IpFamily}</tag>
+ <item>
+ <marker id="ipfamily"></marker>
+ <p>With <c>inet6fb4</c> the client behaves as before, that is,
+ tries to use IPv6, and only if that does not work it
+ uses IPv4).</p>
+ <p>Default is <c>inet</c> (IPv4).</p>
+ </item>
+
+ <tag>{timeout, Timeout}</tag>
+ <item>
+ <marker id="timeout"></marker>
+ <p>Connection time-out.</p>
+ <p>Default is <c>60000</c> (milliseconds).</p>
+ </item>
+
+ <tag>{dtimeout, DTimeout}</tag>
+ <item>
+ <marker id="dtimeout"></marker>
+ <p>Data connect time-out.
+ The time the client waits for the server to connect to the
+ data socket.</p>
+ <p>Default is <c>infinity</c>. </p>
+ </item>
+
+ <tag>{tls, TLSOptions}</tag>
+ <item><marker id="tls_options"></marker>
+ <p>The FTP session is transported over <c>tls</c> (<c>ftps</c>, see
+ <url href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</url>).
+ The list <c><anno>TLSOptions</anno></c> can be empty. The function
+ <seemfa marker="ssl:ssl#connect/3"><c>ssl:connect/3</c></seemfa>
is used for securing both the control connection and the data sessions.
</p>
+ </item>
- <p>The suboption <c>{tls_sec_method, tls_sec_method()}</c> (defaults to
- <c>ftpes</c>) when set to <c>ftps</c> will connect immediately with SSL
+ <tag>{tls_sec_method, TLSSecMethod}</tag>
+ <item><marker id="tls_sec_method"></marker>
+ <p>When set to <c>ftps</c> will connect immediately with SSL
instead of upgrading with STARTTLS. This suboption is ignored unless
- the suboption <c>tls</c> is also set.
- </p>
+ the suboption <c>tls</c> is also set.</p>
+ <p>Default is <c>ftpes</c></p>
+ </item>
- <p>The option <c>{tls_ctrl_session_reuse, boolean()}</c> (defaults to
- <c>false</c>) when set to <c>true</c> the client will re-use the
+ <tag>{tls_ctrl_session_reuse, boolean()}</tag>
+ <item><marker id="tls_ctrl_session_reuse"></marker>
+ <p>When set to <c>true</c> the client will re-use the
TLS session from the control channel on the data channel as enforced by
many FTP servers as (<url href="https://scarybeastsecurity.blogspot.com/2009/02/vsftpd-210-released.html">proposed and implemented first by vsftpd</url>).
</p>
+ <p>Default is <c>false</c>.</p>
+ </item>
+
+ <tag>{sock_ctrl, SocketCtrls :: [SocketControl :: gen_tcp:option()]}</tag>
+ <item>
+ <p>Passes options from <c>SocketCtrls</c> down to the underlying transport layer (tcp).</p>
+ <p><seetype marker="kernel:gen_tcp#option">gen_tcp:option()</seetype> except for <c>ipv6_v6only</c>,
+ <c>active</c>, <c>packet</c>, <c>mode</c>, <c>packet_size</c> and <c>header</c>.</p>
+ <p>Default value is <c>SocketCtrls = []</c>.</p>
+ </item>
+
+ <tag>{sock_data_act, [SocketControl]}</tag>
+ <item>
+ <p>Passes options from <c>[SocketControl]</c> down to the underlying transport layer (tcp).</p>
+ <p><c>sock_data_act</c> uses the value of <c>sock_ctrl</c> as default value.</p>
+ </item>
+
+ <tag>{sock_data_pass, [SocketControl]}</tag>
+ <item>
+ <p>Passes options from <c>[SocketControl]</c> down to the underlying transport layer (tcp).
+ </p>
+ <p><c>sock_data_pass</c> uses the value of <c>sock_ctrl</c> as default value.</p>
+ </item>
- <p>The options <c>sock_ctrl</c>, <c>sock_data_act</c> and <c>sock_data_pass</c> passes options down to
- the underlying transport layer (tcp). The default value for <c>sock_ctrl</c> is <c>[]</c>. Both
- <c>sock_data_act</c> and <c>sock_data_pass</c> uses the value of <c>sock_ctrl</c> as default value.
- </p>
+ <tag>{progress, Progress}</tag>
+ <item>
+ <marker id="progress"></marker>
+ <p>Progress = <c>ignore | {Module, Function, InitialData}</c></p>
+ <p><c>Module = atom()</c>, <c>Function = atom()</c></p>
+ <p><c>InitialData = term()</c></p>
+ <p>Default is <c>ignore</c>.</p>
+
+ <p>Option <c>progress</c> is intended to be used by applications that
+ want to create some type of progress report, such as a progress bar in
+ a GUI. Default for the progress option is <c>ignore</c>,
+ that is, the option is not used. When the progress option is
+ specified, the following happens when <c>ftp:send/[3,4]</c> or
+ <c>ftp:recv/[3,4]</c> are called:</p>
+
+ <list type="bulleted">
+ <item>
+ <p>Before a file is transferred, the following call is
+ made to indicate the start of the file transfer and how large
+ the file is. The return value of the callback function
+ is to be a new value for the <c>UserProgressTerm</c> that will
+ be used as input the next time the callback function is
+ called.</p>
+ <p><c>
+ Module:Function(InitialData, File, {file_size, FileSize})
+ </c></p>
+ </item>
+
+ <item>
+ <p>Every time a chunk of bytes is transferred the
+ following call is made:</p>
+ <p><c>
+ Module:Function(UserProgressTerm, File, {transfer_size, TransferSize})
+ </c></p>
+ </item>
+
+ <item>
+ <p>At the end of the file the following call is
+ made to indicate the end of the transfer:</p>
+ <p><c>
+ Module:Function(UserProgressTerm, File, {transfer_size, 0})
+ </c></p>
+ </item>
+ </list>
+
+ <p>The callback function is to be defined as follows:</p>
+
+ <p><c>
+ Module:Function(UserProgressTerm, File, Size) -> UserProgressTerm
+ </c></p>
+
+ <p><c>
+ UserProgressTerm = term()
+ </c></p>
+
+ <p><c>
+ File = string()
+ </c></p>
+
+ <p><c>
+ Size = {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown}
+ </c></p>
+
+ <p>For remote files, <c>ftp</c> cannot determine the
+ file size in a platform independent way. In this case the size
+ becomes <c>unknown</c> and it is left to the application to
+ determine the size.</p>
+
+ <note>
+ <p>The callback is made by a middleman process, hence the
+ file transfer is not affected by the code in the progress
+ callback function. If the callback crashes, this is
+ detected by the FTP connection process, which then prints an
+ info-report and goes on as if the progress option was set
+ to <c>ignore</c>.</p>
+ </note>
+
+ <p>The file transfer type is set to the default of the FTP server
+ when the session is opened. This is usually ASCII mode.
+ </p>
+
+ <p>The current local working directory (compare <c>lpwd/1</c>) is set
+ to the value reported by <c>file:get_cwd/1</c>, the wanted
+ local directory.
+ </p>
+
+ <p>The return value <c>Pid</c> is used as a reference to the
+ newly created FTP client in all other functions, and they are to
+ be called by the process that created the connection. The FTP
+ client process monitors the process that created it and
+ terminates if that process terminates.</p>
+
+ </item>
+
+ </taglist>
- <p>A session opened in this way is closed using function
- <seeerl marker="#close">close</seeerl>.</p>
<marker id="pwd"></marker>
</desc>
</func>
<func>
- <name since="">pwd(Pid) -> {ok, Dir} | {error, Reason}</name>
+ <name since="" name="pwd" arity="1"/>
<fsummary>Gets the remote current working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Returns the current working directory at the remote server.</p>
@@ -614,23 +521,17 @@
</func>
<func>
- <name since="">recv(Pid, RemoteFile) -> </name>
- <name since="">recv(Pid, RemoteFile, LocalFile) -> ok | {error, Reason}</name>
+ <name since="" name="recv" arity="2"/>
+ <name since="" name="recv" arity="3"/>
<fsummary>Transfers a file from remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>RemoteFile = LocalFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | file_write_error_reason() </v>
- <v>file_write_error_reason() = see file:write/2</v>
- </type>
- <desc>
- <p>Transfers the file <c>RemoteFile</c> from the remote server
+ <desc>
+ <p>Transfers the file <c><anno>RemoteFileName</anno></c> from the remote server
to the file system of the local client. If
- <c>LocalFile</c> is specified, the local file will be
- <c>LocalFile</c>, otherwise
- <c>RemoteFile</c>.</p>
- <p>If the file write fails (for example, <c>enospc</c>), the command is
- aborted and <c>{error, file_write_error_reason()}</c> is returned.
+ <c><anno>LocalFileName</anno></c> is specified, the local file will be
+ <c><anno>LocalFileName</anno></c>, otherwise
+ <c><anno>RemoteFileName</anno></c>.</p>
+ <p>If the file write fails, the command is
+ aborted and <c>{error, term()}</c> is returned.
However, the file is <em>not</em> removed.</p>
<marker id="recv_bin"></marker>
@@ -638,16 +539,10 @@
</func>
<func>
- <name since="">recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, Reason}</name>
+ <name since="" name="recv_bin" arity="2"/>
<fsummary>Transfers a file from remote server as a binary.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
- <desc>
- <p>Transfers the file <c>RemoteFile</c> from the remote server and
+ <desc>
+ <p>Transfers the file <c><anno>RemoteFile</anno></c> from the remote server and
receives it as a binary.</p>
<marker id="recv_chunk_start"></marker>
@@ -655,15 +550,10 @@
</func>
<func>
- <name since="">recv_chunk_start(Pid, RemoteFile) -> ok | {error, Reason}</name>
+ <name since="" name="recv_chunk_start" arity="2"/>
<fsummary>Starts chunk-reading of the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Starts transfer of the file <c>RemoteFile</c> from the
+ <p>Starts transfer of the file <c><anno>RemoteFile</anno></c> from the
remote server.</p>
<marker id="recv_chunk"></marker>
@@ -671,15 +561,10 @@
</func>
<func>
- <name since="">recv_chunk(Pid) -> ok | {ok, Bin} | {error, Reason}</name>
+ <name since="" name="recv_chunk" arity="1" />
<fsummary>Receives a chunk of the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Receives a chunk of the remote file (<c>RemoteFile</c> of
+ <p>Receives a chunk of the remote file (<c>RemoteFile</c> of
<c>recv_chunk_start</c>). The return values have the following
meaning:</p>
<list type="bulleted">
@@ -693,30 +578,20 @@
</func>
<func>
- <name since="">rename(Pid, Old, New) -> ok | {error, Reason}</name>
+ <name since="" name="rename" arity="3"/>
<fsummary>Renames a file at the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>CurrFile = NewFile = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Renames <c>Old</c> to <c>New</c> at the remote server.</p>
+ <p>Renames <c><anno>Old</anno></c> to <c><anno>New</anno></c> at the remote server.</p>
<marker id="rmdir"></marker>
</desc>
</func>
<func>
- <name since="">rmdir(Pid, Dir) -> ok | {error, Reason}</name>
+ <name since="" name="rmdir" arity="2" />
<fsummary>Removes a remote directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Dir = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Removes directory <c>Dir</c> at the remote server.</p>
+ <p>Removes directory <c><anno>Dir</anno></c> at the remote server.</p>
<marker id="send"></marker>
<marker id="send2"></marker>
@@ -725,34 +600,23 @@
</func>
<func>
- <name since="">send(Pid, LocalFile) -></name>
- <name since="">send(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
+ <name since="" name="send" arity="2"/>
+ <name since="" name="send" arity="3"/>
<fsummary>Transfers a file to the remote server.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>LocalFile = RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
<desc>
- <p>Transfers the file <c>LocalFile</c> to the remote server. If
- <c>RemoteFile</c> is specified, the name of the remote file is set
- to <c>RemoteFile</c>, otherwise to <c>LocalFile</c>.</p>
+ <p>Transfers the file <c><anno>LocalFileName</anno></c> to the remote server. If
+ <c><anno>RemoteFileName</anno></c> is specified, the name of the remote file is set
+ to <c><anno>RemoteFileName</anno></c>, otherwise to <c><anno>LocalFileName</anno></c>.</p>
<marker id="send_bin"></marker>
</desc>
</func>
<func>
- <name since="">send_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
+ <name since="" name="send_bin" arity="3" />
<fsummary>Transfers a binary into a remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>RemoteFile = string()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
- <desc>
- <p>Transfers the binary <c>Bin</c> into the file <c>RemoteFile</c>
+ <desc>
+ <p>Transfers the binary <c><anno>Bin</anno></c> into the file <c><anno>RemoteFile</anno></c>
at the remote server.</p>
<marker id="send_chunk"></marker>
@@ -760,15 +624,10 @@
</func>
<func>
- <name since="">send_chunk(Pid, Bin) -> ok | {error, Reason}</name>
+ <name since="" name="send_chunk" arity="2" />
<fsummary>Writes a chunk to the remote file.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Bin = binary()</v>
- <v>Reason = echunk | restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Transfers the chunk <c>Bin</c> to the remote server, which
+ <p>Transfers the chunk <c><anno>Bin</anno></c> to the remote server, which
writes it into the file specified in the call to
<c>send_chunk_start/2</c>.</p>
<p>For some errors, for example, file system full, it is
@@ -780,15 +639,10 @@
</func>
<func>
- <name since="">send_chunk_start(Pid, File) -> ok | {error, Reason}</name>
+ <name since="" name="send_chunk_start" arity="2" />
<fsummary>Starts transfer of file chunks.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>File = string()</v>
- <v>Reason = restriction_reason() | common_reason()</v>
- </type>
<desc>
- <p>Starts transfer of chunks into the file <c>File</c> at the
+ <p>Starts transfer of chunks into the file <c><anno>RemoteFile</anno></c> at the
remote server.</p>
<marker id="send_chunk_end"></marker>
@@ -796,12 +650,8 @@
</func>
<func>
- <name since="">send_chunk_end(Pid) -> ok | {error, Reason}</name>
+ <name since="" name="send_chunk_end" arity="1" />
<fsummary>Stops transfer of chunks.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
- </type>
<desc>
<p>Stops transfer of chunks to the remote server. The file at the
remote server, specified in the call to <c>send_chunk_start/2</c>
@@ -812,14 +662,9 @@
</func>
<func>
- <name since="OTP 21.0">start_service(ServiceConfig) -> {ok, Pid} | {error, Reason}</name>
+ <name since="OTP 21.0" name="start_service" arity="1" />
<fsummary>Dynamically starts an <c>FTP</c>
session after the <c>ftp</c> application has been started.</fsummary>
- <type>
- <v>ServiceConfig = [{Option, Value}]</v>
- <v>Option = property()</v>
- <v>Value = term()</v>
- </type>
<desc>
<p>Dynamically starts an <c>FTP</c> session after the <c>ftp</c>
application has been started.</p>
@@ -831,25 +676,19 @@
</func>
<func>
- <name since="OTP 21.0">stop_service(Reference) -> ok | {error, Reason} </name>
+ <name since="OTP 21.0" name="stop_service" arity="1"/>
<fsummary>Stops an FTP session.</fsummary>
- <type>
- <v>Reference = pid() | term() - service-specified reference</v>
- <v>Reason = term()</v>
- </type>
+ <type_desc variable="Pid">
+ Service-specified reference
+ </type_desc>
<desc>
<p>Stops a started FTP session.</p>
</desc>
</func>
<func>
- <name since="">type(Pid, Type) -> ok | {error, Reason}</name>
+ <name since="" name="type" arity="2" />
<fsummary>Sets transfer type to <c>ascii</c>or <c>binary</c>.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Type = ascii | binary</v>
- <v>Reason = etype | restriction_reason() | common_reason()</v>
- </type>
<desc>
<p>Sets the file transfer type to <c>ascii</c> or <c>binary</c>. When
an FTP session is opened, the default transfer type of the
@@ -860,45 +699,30 @@
</func>
<func>
- <name since="">user(Pid, User, Password) -> ok | {error, Reason}</name>
+ <name since="" name="user" arity="3"/>
<fsummary>User login.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>User = Password = string()</v>
- <v>Reason = euser | common_reason()</v>
- </type>
<desc>
- <p>Performs login of <c>User</c> with <c>Password</c>.</p>
+ <p>Performs login of <c><anno>User</anno></c> with <c><anno>Pass</anno></c>.</p>
<marker id="user4"></marker>
</desc>
</func>
<func>
- <name since="">user(Pid, User, Password, Account) -> ok | {error, Reason}</name>
+ <name since="" name="user" arity="4" />
<fsummary>User login.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>User = Password = string()</v>
- <v>Reason = euser | common_reason() </v>
- </type>
<desc>
- <p>Performs login of <c>User</c> with <c>Password</c> to the account
- specified by <c>Account</c>.</p>
+ <p>Performs login of <c><anno>User</anno></c> with <c><anno>Pass</anno></c> to the account
+ specified by <c><anno>Account</anno></c>.</p>
<marker id="quote"></marker>
</desc>
</func>
<func>
- <name since="">quote(Pid, Command) -> [FTPLine]</name>
+ <name since="" name="quote" arity="2" />
<fsummary>Sends an arbitrary FTP command.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Command = string()</v>
- <v>FTPLine = string()</v>
- </type>
- <desc><note><p>The telnet end of line characters, from the FTP
+ <desc><note><p>The telnet end of line characters, from the FTP
protocol definition, CRLF, for example, "\\r\\n" has been removed.</p></note>
<p>Sends an arbitrary FTP command and returns verbatim a list
of the lines sent back by the FTP server. This function is
diff --git a/lib/ftp/doc/src/specs.xml b/lib/ftp/doc/src/specs.xml
new file mode 100644
index 0000000000..a01aa83f78
--- /dev/null
+++ b/lib/ftp/doc/src/specs.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<specs xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="../specs/specs_ftp.xml"/>
+</specs>
diff --git a/lib/ftp/test/ftp_SUITE.erl b/lib/ftp/test/ftp_SUITE.erl
index 961b3b5fe1..9239d18f74 100644
--- a/lib/ftp/test/ftp_SUITE.erl
+++ b/lib/ftp/test/ftp_SUITE.erl
@@ -1069,7 +1069,7 @@ error_ehost(_Config) ->
%%%----------------------------------------------------------------
error_datafail() ->
[{doc, "Test that failure to open data channel captures "
- "error emitted on ctrl chanenel"}].
+ "error emitted on ctrl channel"}].
error_datafail(Config) ->
Self = self(),
--
2.35.3