aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in5
-rw-r--r--erts/emulator/beam/erl_process.c28
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/mtx_SUITE.erl473
-rw-r--r--erts/emulator/test/mtx_SUITE_data/Makefile.src30
-rw-r--r--erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c692
-rw-r--r--erts/emulator/test/port_SUITE.erl4
-rw-r--r--erts/include/internal/ethr_mutex.h145
-rw-r--r--erts/include/internal/i386/atomic.h49
-rw-r--r--erts/include/internal/sparc32/atomic.h35
-rw-r--r--erts/lib_src/common/ethr_mutex.c631
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl5
-rw-r--r--lib/erl_docgen/Makefile19
-rw-r--r--lib/erl_docgen/ebin/.gitignore0
-rw-r--r--lib/erl_docgen/priv/bin/specs_gen.escript129
-rwxr-xr-xlib/erl_docgen/priv/bin/xref_mod_app.escript107
-rw-r--r--lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd7
-rw-r--r--lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl626
-rw-r--r--lib/erl_docgen/priv/xsl/db_man.xsl338
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl486
-rw-r--r--lib/erl_docgen/src/Makefile96
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src12
-rw-r--r--lib/erl_docgen/src/erl_docgen.appup.src1
-rw-r--r--lib/erl_docgen/src/otp_specs.erl701
-rw-r--r--lib/et/src/et_selector.erl4
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl23
-rw-r--r--lib/inets/doc/src/http_client.xml4
-rw-r--r--lib/inets/doc/src/httpc.xml13
-rw-r--r--lib/inets/doc/src/httpd.xml12
-rw-r--r--lib/inets/include/httpd.hrl41
-rw-r--r--lib/inets/src/http_client/Makefile1
-rw-r--r--lib/inets/src/http_client/httpc.erl50
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl119
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl6
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl9
-rw-r--r--lib/inets/src/http_lib/Makefile3
-rw-r--r--lib/inets/src/http_lib/http_transport.erl9
-rw-r--r--lib/inets/src/http_lib/http_uri.erl (renamed from lib/inets/src/http_client/http_uri.erl)50
-rw-r--r--lib/inets/src/http_server/Makefile8
-rw-r--r--lib/inets/src/http_server/httpd.erl5
-rw-r--r--lib/inets/src/http_server/httpd.hrl82
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl1
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl2
-rw-r--r--lib/inets/src/http_server/httpd_file.erl12
-rw-r--r--lib/inets/src/http_server/httpd_internal.hrl45
-rw-r--r--lib/inets/src/http_server/httpd_request.erl4
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl3
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl2
-rw-r--r--lib/inets/src/http_server/httpd_util.erl55
-rw-r--r--lib/inets/src/http_server/mod_actions.erl3
-rw-r--r--lib/inets/src/http_server/mod_alias.erl1
-rw-r--r--lib/inets/src/http_server/mod_auth.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_dets.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_plain.erl4
-rw-r--r--lib/inets/src/http_server/mod_auth_server.erl3
-rw-r--r--lib/inets/src/http_server/mod_cgi.erl1
-rw-r--r--lib/inets/src/http_server/mod_dir.erl8
-rw-r--r--lib/inets/src/http_server/mod_disk_log.erl4
-rw-r--r--lib/inets/src/http_server/mod_esi.erl1
-rw-r--r--lib/inets/src/http_server/mod_get.erl4
-rw-r--r--lib/inets/src/http_server/mod_head.erl2
-rw-r--r--lib/inets/src/http_server/mod_htaccess.erl3
-rw-r--r--lib/inets/src/http_server/mod_include.erl7
-rw-r--r--lib/inets/src/http_server/mod_log.erl3
-rw-r--r--lib/inets/src/http_server/mod_range.erl4
-rw-r--r--lib/inets/src/http_server/mod_responsecontrol.erl3
-rw-r--r--lib/inets/src/http_server/mod_security.erl3
-rw-r--r--lib/inets/src/http_server/mod_security_server.erl3
-rw-r--r--lib/inets/src/http_server/mod_trace.erl2
-rw-r--r--lib/inets/src/inets_app/inets.appup.src10
-rw-r--r--lib/inets/test/Makefile24
-rw-r--r--lib/inets/test/httpc_SUITE.erl45
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl36
-rw-r--r--lib/inets/test/inets_appup_test.erl5
-rw-r--r--lib/inets/vsn.mk21
-rw-r--r--lib/public_key/src/pubkey_cert.erl2
-rw-r--r--lib/public_key/src/public_key.erl25
-rw-r--r--lib/public_key/test/public_key_SUITE.erl4
-rw-r--r--lib/ssl/doc/src/ssl.xml12
-rw-r--r--lib/ssl/src/ssl_certificate.erl1
-rw-r--r--lib/ssl/src/ssl_cipher.erl1
-rw-r--r--lib/ssl/src/ssl_connection.erl1
-rw-r--r--lib/ssl/src/ssl_handshake.erl1
-rw-r--r--lib/ssl/src/ssl_handshake.hrl7
-rw-r--r--lib/ssl/src/ssl_internal.hrl40
-rw-r--r--lib/ssl/src/ssl_record.erl1
-rw-r--r--lib/ssl/src/ssl_ssl3.erl1
-rw-r--r--lib/ssl/src/ssl_tls1.erl1
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl3
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl5
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl3
-rw-r--r--lib/stdlib/test/ets_SUITE.erl21
-rw-r--r--make/otp.mk.in29
-rw-r--r--make/otp_release_targets.mk44
95 files changed, 4664 insertions, 929 deletions
diff --git a/Makefile.in b/Makefile.in
index 4b6e2e1190..ca92bf604d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -393,7 +393,7 @@ endif
# ---------------------------------------------------------------
# Target only used when building commercial ERTS patches
# ---------------------------------------------------------------
-release_docs docs:
+release_docs docs: mod2app
ifeq ($(OTP_SMALL_BUILD),true)
cd $(ERL_TOP)/lib && \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@
@@ -408,6 +408,9 @@ endif
cd $(ERL_TOP)/system/doc && \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@
+mod2app:
+ $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml
+
# ----------------------------------------------------------------------
ERLANG_EARS=$(BOOTSTRAP_ROOT)/bootstrap/erts
ELINK=$(BOOTSTRAP_ROOT)/bootstrap/erts/bin/elink
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index f252c2cbe2..fc950af8ce 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1842,6 +1842,9 @@ do { \
static void
check_balance(ErtsRunQueue *c_rq)
{
+#if ERTS_MAX_PROCESSES >= (1 << 27)
+# error check_balance() assumes ERTS_MAX_PROCESS < (1 << 27)
+#endif
ErtsRunQueueBalance avg = {0};
Sint64 scheds_reds, full_scheds_reds;
int forced, active, current_active, oowc, half_full_scheds, full_scheds,
@@ -1965,12 +1968,14 @@ check_balance(ErtsRunQueue *c_rq)
run_queue_info[qix].prio[pix].avail = 0;
}
else {
- int xreds = 0;
- int procreds = treds;
- procreds -= run_queue_info[qix].prio[ERTS_PORT_PRIO_LEVEL].reds;
+ Sint64 xreds = 0;
+ Sint64 procreds = treds;
+ procreds -=
+ ((Sint64)
+ run_queue_info[qix].prio[ERTS_PORT_PRIO_LEVEL].reds);
for (pix = 0; pix < ERTS_NO_PROC_PRIO_LEVELS; pix++) {
- int av;
+ Sint64 av;
if (xreds == 0)
av = 100;
@@ -1981,9 +1986,10 @@ check_balance(ErtsRunQueue *c_rq)
if (av == 0)
av = 1;
}
- run_queue_info[qix].prio[pix].avail = av;
+ run_queue_info[qix].prio[pix].avail = (int) av;
+ ASSERT(run_queue_info[qix].prio[pix].avail >= 0);
if (pix < PRIORITY_NORMAL) /* ie., max or high */
- xreds += run_queue_info[qix].prio[pix].reds;
+ xreds += (Sint64) run_queue_info[qix].prio[pix].reds;
}
run_queue_info[qix].prio[ERTS_PORT_PRIO_LEVEL].avail = 100;
}
@@ -2088,7 +2094,8 @@ check_balance(ErtsRunQueue *c_rq)
if (max_len != 0) {
int avail = avg.prio[pix].avail;
if (avail != 0) {
- max_len = ((100*max_len - 1) / avail) + 1;
+ max_len = (int) ((100*((Sint64) max_len) - 1)
+ / ((Sint64) avail)) + 1;
avg.prio[pix].max_len = max_len;
ASSERT(max_len >= 0);
}
@@ -2105,9 +2112,10 @@ check_balance(ErtsRunQueue *c_rq)
|| run_queue_info[qix].prio[pix].avail == 0)
limit = 0;
else
- limit = (((avg.prio[pix].max_len
- * run_queue_info[qix].prio[pix].avail) - 1)
- / 100 + 1);
+ limit = (int) (((((Sint64) avg.prio[pix].max_len)
+ * ((Sint64) run_queue_info[qix].prio[pix].avail))
+ - 1)
+ / 100 + 1);
run_queue_info[qix].prio[pix].migration_limit = limit;
}
}
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index a4c02da626..7259e1b84d 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -83,6 +83,7 @@ MODULES= \
receive_SUITE \
ref_SUITE \
register_SUITE \
+ mtx_SUITE \
save_calls_SUITE \
send_term_SUITE \
sensitive_SUITE \
diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl
new file mode 100644
index 0000000000..ae77fe4d89
--- /dev/null
+++ b/erts/emulator/test/mtx_SUITE.erl
@@ -0,0 +1,473 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Stress tests of rwmutex implementation.
+%%
+%% Author: Rickard Green
+%%
+-module(mtx_SUITE).
+
+%%-define(line_trace,true).
+
+-include("test_server.hrl").
+
+-export([all/1, init_per_suite/1, end_per_suite/1, init_per_testcase/2, fin_per_testcase/2]).
+
+-export([long_rwlock/1,
+ hammer_ets_rwlock/1,
+ hammer_rwlock/1,
+ hammer_rwlock_check/1,
+ hammer_tryrwlock/1,
+ hammer_tryrwlock_check/1,
+ hammer_sched_long_rwlock/1,
+ hammer_sched_long_rwlock_check/1,
+ hammer_sched_long_freqread_rwlock/1,
+ hammer_sched_long_freqread_rwlock_check/1,
+ hammer_sched_long_tryrwlock/1,
+ hammer_sched_long_tryrwlock_check/1,
+ hammer_sched_long_freqread_tryrwlock/1,
+ hammer_sched_long_freqread_tryrwlock_check/1,
+ hammer_sched_rwlock/1,
+ hammer_sched_rwlock_check/1,
+ hammer_sched_freqread_rwlock/1,
+ hammer_sched_freqread_rwlock_check/1,
+ hammer_sched_tryrwlock/1,
+ hammer_sched_tryrwlock_check/1,
+ hammer_sched_freqread_tryrwlock/1,
+ hammer_sched_freqread_tryrwlock_check/1]).
+
+init_per_suite(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Lib = filename:join([DataDir, atom_to_list(?MODULE)]),
+ ok = erlang:load_nif(Lib, none),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?t:minutes(15)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+all(suite) ->
+ [
+ long_rwlock,
+ hammer_rwlock_check,
+ hammer_rwlock,
+ hammer_tryrwlock_check,
+ hammer_tryrwlock,
+ hammer_ets_rwlock,
+ hammer_sched_long_rwlock_check,
+ hammer_sched_long_rwlock,
+ hammer_sched_long_freqread_rwlock_check,
+ hammer_sched_long_freqread_rwlock,
+ hammer_sched_long_tryrwlock_check,
+ hammer_sched_long_tryrwlock,
+ hammer_sched_long_freqread_tryrwlock_check,
+ hammer_sched_long_freqread_tryrwlock,
+ hammer_sched_rwlock_check,
+ hammer_sched_rwlock,
+ hammer_sched_freqread_rwlock_check,
+ hammer_sched_freqread_rwlock,
+ hammer_sched_tryrwlock_check,
+ hammer_sched_tryrwlock,
+ hammer_sched_freqread_tryrwlock_check,
+ hammer_sched_freqread_tryrwlock
+ ].
+
+long_rwlock(Config) when is_list(Config) ->
+ statistics(runtime),
+ LLRes = long_rw_test(),
+ {_, RunTime} = statistics(runtime),
+ %% A very short run time is expected, since
+ %% threads in the test mostly wait
+ ?t:format("RunTime=~p~n", [RunTime]),
+ ?line true = RunTime < 100,
+ ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.",
+ case LLRes of
+ ok ->
+ {comment, RunTimeStr};
+ {comment, Comment} ->
+ {comment, Comment ++ " " ++ RunTimeStr}
+ end.
+
+hammer_rwlock(Config) when is_list(Config) ->
+ hammer_rw_test(false).
+
+hammer_rwlock_check(Config) when is_list(Config) ->
+ hammer_rw_test(true).
+
+hammer_tryrwlock(Config) when is_list(Config) ->
+ hammer_tryrw_test(false).
+
+hammer_tryrwlock_check(Config) when is_list(Config) ->
+ hammer_tryrw_test(true).
+
+hammer_sched_rwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, false, true, 0, 0).
+
+hammer_sched_rwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, true, true, 0, 0).
+
+hammer_sched_freqread_rwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, false, true, 0, 0).
+
+hammer_sched_freqread_rwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, true, true, 0, 0).
+
+hammer_sched_tryrwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, false, false, 0, 100).
+
+hammer_sched_tryrwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, true, false, 0, 100).
+
+hammer_sched_freqread_tryrwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, false, false, 0, 100).
+
+hammer_sched_freqread_tryrwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, true, false, 0, 100).
+
+hammer_sched_long_rwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, false, true, 100, 0).
+
+hammer_sched_long_rwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, true, true, 100, 0).
+
+hammer_sched_long_freqread_rwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, false, true, 100, 0).
+
+hammer_sched_long_freqread_rwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, true, true, 100, 0).
+
+hammer_sched_long_tryrwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, false, false, 100, 100).
+
+hammer_sched_long_tryrwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(false, true, false, 100, 100).
+
+hammer_sched_long_freqread_tryrwlock(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, false, false, 100, 100).
+
+hammer_sched_long_freqread_tryrwlock_check(Config) when is_list(Config) ->
+ hammer_sched_rwlock_test(true, true, false, 100, 100).
+
+hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked) ->
+ case create_rwlock(FreqRead, LockCheck) of
+ enotsup ->
+ {skipped, "Not supported."};
+ RWLock ->
+ Onln = erlang:system_info(schedulers_online),
+ NWPs = case Onln div 3 of
+ 1 -> case Onln < 4 of
+ true -> 1;
+ false -> 2
+ end;
+ X -> X
+ end,
+ NRPs = Onln - NWPs,
+ NoLockOps = ((((50000000 div Onln)
+ div case {Blocking, WaitLocked} of
+ {false, 0} -> 1;
+ _ -> 10
+ end)
+ div (case WaitLocked == 0 of
+ true -> 1;
+ false -> WaitLocked*250
+ end))
+ div handicap()),
+ ?t:format("NoLockOps=~p~n", [NoLockOps]),
+ Sleep = case Blocking of
+ true -> NoLockOps;
+ false -> NoLockOps div 10
+ end,
+ WPs = lists:map(
+ fun (Sched) ->
+ spawn_opt(
+ fun () ->
+ io:format("Writer on scheduler ~p.~n",
+ [Sched]),
+ Sched = erlang:system_info(scheduler_id),
+ receive go -> gone end,
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ true,
+ WaitLocked,
+ WaitUnlocked,
+ NoLockOps,
+ Sleep),
+ Sched = erlang:system_info(scheduler_id)
+ end,
+ [link, {scheduler, Sched}])
+ end,
+ lists:seq(1, NWPs)),
+ RPs = lists:map(
+ fun (Sched) ->
+ spawn_opt(
+ fun () ->
+ io:format("Reader on scheduler ~p.~n",
+ [Sched]),
+ Sched = erlang:system_info(scheduler_id),
+ receive go -> gone end,
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ false,
+ WaitLocked,
+ WaitUnlocked,
+ NoLockOps,
+ Sleep),
+ Sched = erlang:system_info(scheduler_id)
+ end,
+ [link, {scheduler, Sched}])
+ end,
+ lists:seq(NWPs + 1, NWPs + NRPs)),
+ Procs = WPs ++ RPs,
+ case {Blocking, WaitLocked} of
+ {_, 0} -> ok;
+ {false, _} -> ok;
+ _ -> statistics(runtime)
+ end,
+ lists:foreach(fun (P) -> P ! go end, Procs),
+ lists:foreach(fun (P) ->
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end,
+ Procs),
+ case {Blocking, WaitLocked} of
+ {_, 0} -> ok;
+ {false, _} -> ok;
+ _ ->
+ {_, RunTime} = statistics(runtime),
+ ?t:format("RunTime=~p~n", [RunTime]),
+ ?line true = RunTime < 500,
+ {comment,
+ "Run-time during test was "
+ ++ integer_to_list(RunTime)
+ ++ " ms."}
+ end
+ end.
+
+hammer_sched_rwlock_proc(_RWLock,
+ _Blocking,
+ _WriteOp,
+ _WaitLocked,
+ _WaitUnlocked,
+ 0,
+ _Sleep) ->
+ ok;
+hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ WriteOp,
+ WaitLocked,
+ WaitUnlocked,
+ Times,
+ Sleep) when Times rem Sleep == 0 ->
+ rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, WaitUnlocked),
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ WriteOp,
+ WaitLocked,
+ WaitUnlocked,
+ Times - 1,
+ Sleep);
+hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ WriteOp,
+ WaitLocked,
+ WaitUnlocked,
+ Times,
+ Sleep) ->
+ rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, 0),
+ hammer_sched_rwlock_proc(RWLock,
+ Blocking,
+ WriteOp,
+ WaitLocked,
+ WaitUnlocked,
+ Times - 1,
+ Sleep).
+
+-define(HAMMER_ETS_RWLOCK_REPEAT_TIMES, 1).
+-define(HAMMER_ETS_RWLOCK_TSIZE, 500).
+
+hammer_ets_rwlock(Config) when is_list(Config) ->
+ {Ops, Procs} = case handicap() of
+ 1 -> {20000, 500};
+ 2 -> {20000, 50};
+ 3 -> {2000, 50};
+ _ -> {200, 50}
+ end,
+ ?t:format("Procs=~p~nOps=~p~n", [Procs, Ops]),
+ lists:foreach(fun (XOpts) ->
+ ?t:format("Running with extra opts: ~p", [XOpts]),
+ hammer_ets_rwlock_test(XOpts, true, 2, Ops,
+ Procs, false)
+ end,
+ [[],
+ [{read_concurrency, true}],
+ [{write_concurrency, true}],
+ [{read_concurrency, true},{write_concurrency, true}]]),
+ ok.
+
+%% Aux funcs
+
+long_rw_test() ->
+ exit(no_nif_implementation).
+
+hammer_rw_test(_Arg) ->
+ exit(no_nif_implementation).
+
+hammer_tryrw_test(_Arg) ->
+ exit(no_nif_implementation).
+
+create_rwlock(_FreqRead, _LockCheck) ->
+ exit(no_nif_implementation).
+
+rwlock_op(_RWLock, _Blocking, _WriteOp, _WaitLocked, _WaitUnlocked) ->
+ exit(no_nif_implementation).
+
+hammer_ets_rwlock_put_data() ->
+ put(?MODULE, {"here are some", data, "to store", make_ref()}).
+
+hammer_ets_rwlock_get_data() ->
+ get(?MODULE).
+
+hammer_ets_rwlock_ops(_T, _UW, _N, _C, _SC, 0) ->
+ ok;
+hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) when N >= ?HAMMER_ETS_RWLOCK_TSIZE ->
+ hammer_ets_rwlock_ops(T, UW, 0, C, SC, Tot);
+hammer_ets_rwlock_ops(T, UW, N, 0, SC, Tot) ->
+ case UW of
+ true ->
+ true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()});
+ false ->
+ [{N, _, _}] = ets:lookup(T, N)
+ end,
+ hammer_ets_rwlock_ops(T, UW, N+1, SC, SC, Tot-1);
+hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) ->
+ case UW of
+ false ->
+ true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()});
+ true ->
+ [{N, _, _}] = ets:lookup(T, N)
+ end,
+ hammer_ets_rwlock_ops(T, UW, N+1, C-1, SC, Tot-1).
+
+hammer_ets_rwlock_init(T, N) when N < ?HAMMER_ETS_RWLOCK_TSIZE ->
+ ets:insert(T, {N, N, N}),
+ hammer_ets_rwlock_init(T, N+1);
+hammer_ets_rwlock_init(_T, _N) ->
+ ok.
+
+hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
+ receive after 100 -> ok end,
+ {TP, TM} = spawn_monitor(
+ fun () ->
+ _L = repeat_list(
+ fun () ->
+ Caller = self(),
+ T = fun () ->
+ Parent = self(),
+ hammer_ets_rwlock_put_data(),
+ T=ets:new(x, [public | XOpts]),
+ hammer_ets_rwlock_init(T, 0),
+ Ps0 = repeat_list(
+ fun () ->
+ spawn_link(
+ fun () ->
+ hammer_ets_rwlock_put_data(),
+ receive go -> ok end,
+ hammer_ets_rwlock_ops(T, UW, N, C, C, N),
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end)
+ end,
+ NP - case SC of
+ false -> 0;
+ _ -> 1
+ end),
+ Ps = case SC of
+ false -> Ps0;
+ _ -> [spawn_link(fun () ->
+ hammer_ets_rwlock_put_data(),
+ receive go -> ok end,
+ hammer_ets_rwlock_ops(T, UW, N, SC, SC, N),
+ Parent ! {done, self()},
+ receive after infinity -> ok end
+ end) | Ps0]
+ end,
+ Start = now(),
+ lists:foreach(fun (P) -> P ! go end, Ps),
+ lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps),
+ Stop = now(),
+ lists:foreach(fun (P) ->
+ unlink(P),
+ exit(P, bang),
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} -> ok
+ end
+ end, Ps),
+ Res = timer:now_diff(Stop, Start)/1000000,
+ Caller ! {?MODULE, self(), Res}
+ end,
+ TP = spawn_link(T),
+ receive
+ {?MODULE, TP, Res} ->
+ Res
+ end
+ end,
+ ?HAMMER_ETS_RWLOCK_REPEAT_TIMES)
+ end),
+ receive
+ {'DOWN', TM, process, TP, _} -> ok
+ end.
+
+repeat_list(Fun, N) ->
+ repeat_list(Fun, N, []).
+
+repeat_list(_Fun, 0, Acc) ->
+ Acc;
+repeat_list(Fun, N, Acc) ->
+ repeat_list(Fun, N-1, [Fun()|Acc]).
+
+
+handicap() ->
+ X0 = case catch (erlang:system_info(logical_processors_available) >=
+ erlang:system_info(schedulers_online)) of
+ true -> 1;
+ _ -> 2
+ end,
+ case erlang:system_info(build_type) of
+ opt ->
+ X0;
+ ReallySlow when ReallySlow == debug;
+ ReallySlow == valgrind;
+ ReallySlow == purify ->
+ X0*3;
+ _Slow ->
+ X0*2
+ end.
+
diff --git a/erts/emulator/test/mtx_SUITE_data/Makefile.src b/erts/emulator/test/mtx_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..b6c843269c
--- /dev/null
+++ b/erts/emulator/test/mtx_SUITE_data/Makefile.src
@@ -0,0 +1,30 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include @erts_lib_include_internal_generated@@[email protected]
+include @erts_lib_include_internal_generated@@DS@erts_internal.mk
+
+NIF_LIBS = mtx_SUITE@dll@
+
+SHLIB_EXTRA_CFLAGS = $(ETHR_DEFS) -I@erts_lib_include_internal@ -I@erts_lib_include_internal_generated@
+LIBS = @ERTS_LIBS@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c
new file mode 100644
index 0000000000..818023211c
--- /dev/null
+++ b/erts/emulator/test/mtx_SUITE_data/mtx_SUITE.c
@@ -0,0 +1,692 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Stress tests of rwmutex implementation.
+ *
+ * Author: Rickard Green
+ */
+
+#include "erl_nif.h"
+
+#ifdef __WIN32__
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h>
+#else
+# include "ethread.h"
+# include "erl_misc_utils.h"
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+static int
+fail(const char *file, int line, const char *function, const char *assertion);
+
+#undef ASSERT
+#define ASSERT(X) ((void) ((X) ? 1 : fail(__FILE__, __LINE__, __func__, #X)))
+
+#ifdef __WIN32__
+/*
+ * We cannot access the ethread symbols directly; test
+ * what we got in the nif api instead...
+ */
+#define HAVE_FREQREAD_SUPPORT 0
+#define RWMUTEX_T ErlNifRWLock
+#define RWMUTEX_CREATE(FR) enif_rwlock_create("dummy")
+#define RWMUTEX_DESTROY enif_rwlock_destroy
+#define RWMUTEX_WLOCK enif_rwlock_rwlock
+#define RWMUTEX_TRYWLOCK enif_rwlock_tryrwlock
+#define RWMUTEX_WUNLOCK enif_rwlock_rwunlock
+#define RWMUTEX_TRYRLOCK enif_rwlock_tryrlock
+#define RWMUTEX_RLOCK enif_rwlock_rlock
+#define RWMUTEX_RUNLOCK enif_rwlock_runlock
+#define THR_ID ErlNifTid
+#define THR_CREATE(A, B, C, D) enif_thread_create("dummy", (A), (B), (C), (D))
+#define THR_JOIN enif_thread_join
+#define ATOMIC_T volatile LONG
+#define ATOMIC_INIT(VarP, Val) (*(VarP) = (Val))
+#define ATOMIC_SET(VarP, Val) (*(VarP) = (Val))
+#define ATOMIC_READ(VarP) (*(VarP))
+#define ATOMIC_INC InterlockedIncrement
+#define ATOMIC_DEC InterlockedDecrement
+
+#else
+
+#ifdef ETHR_USE_OWN_RWMTX_IMPL__
+# define HAVE_FREQREAD_SUPPORT 1
+#else
+# define HAVE_FREQREAD_SUPPORT 0
+#endif
+
+#define RWMUTEX_T ethr_rwmutex
+static ethr_rwmutex *
+RWMUTEX_CREATE(int freqread)
+{
+ ethr_rwmutex *rwmtx = enif_alloc(sizeof(ethr_rwmutex));
+ ethr_rwmutex_opt rwmtx_opt = ETHR_RWMUTEX_OPT_DEFAULT_INITER;
+ if (freqread)
+ rwmtx_opt.type = ETHR_RWMUTEX_TYPE_FREQUENT_READ;
+ ASSERT(rwmtx);
+ ASSERT(ethr_rwmutex_init_opt(rwmtx, &rwmtx_opt) == 0);
+ return rwmtx;
+}
+static void
+RWMUTEX_DESTROY(ethr_rwmutex *rwmtx)
+{
+ ASSERT(ethr_rwmutex_destroy(rwmtx) == 0);
+ enif_free(rwmtx);
+}
+#define RWMUTEX_TRYWLOCK ethr_rwmutex_tryrwlock
+#define RWMUTEX_WLOCK ethr_rwmutex_rwlock
+#define RWMUTEX_WUNLOCK ethr_rwmutex_rwunlock
+#define RWMUTEX_TRYRLOCK ethr_rwmutex_tryrlock
+#define RWMUTEX_RLOCK ethr_rwmutex_rlock
+#define RWMUTEX_RUNLOCK ethr_rwmutex_runlock
+#define THR_ID ethr_tid
+#define THR_CREATE ethr_thr_create
+#define THR_JOIN ethr_thr_join
+#define ATOMIC_T ethr_atomic_t
+#define ATOMIC_INIT ethr_atomic_init
+#define ATOMIC_SET ethr_atomic_set
+#define ATOMIC_READ ethr_atomic_read
+#define ATOMIC_INC ethr_atomic_inc
+#define ATOMIC_DEC ethr_atomic_dec
+
+#endif
+
+
+#if !defined(__func__)
+# if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
+# if !defined(__GNUC__) || __GNUC__ < 2
+# define __func__ "[unknown_function]"
+# else
+# define __func__ __FUNCTION__
+# endif
+# endif
+#endif
+
+static void milli_sleep(int ms);
+static int get_bool(ErlNifEnv* env, ERL_NIF_TERM term);
+
+/*
+ * Long rwlock testcase
+ */
+
+#define LONG_RW_NO_W_THREADS 6
+#define LONG_RW_NO_THREADS 20
+#define LONG_RW_NO_WLOCK_COUNT 100
+
+typedef struct {
+ RWMUTEX_T *rwlock;
+ ATOMIC_T *is_wlocked;
+ ATOMIC_T *is_rlocked;
+ int *stop;
+ int *count;
+ int sleep;
+} long_rw_t;
+
+static void *
+long_rw_w(void *varg)
+{
+ long_rw_t *arg = varg;
+ int stop = 0;
+ do {
+ RWMUTEX_WLOCK(arg->rwlock);
+ ASSERT(!ATOMIC_READ(arg->is_wlocked));
+ ATOMIC_SET(arg->is_wlocked, 1);
+ ASSERT(!ATOMIC_READ(arg->is_rlocked));
+ milli_sleep(arg->sleep);
+ if (++(*arg->count) > LONG_RW_NO_WLOCK_COUNT)
+ stop = *arg->stop = 1;
+ ATOMIC_SET(arg->is_wlocked, 0);
+ ASSERT(!ATOMIC_READ(arg->is_rlocked));
+ RWMUTEX_WUNLOCK(arg->rwlock);
+ } while (!stop);
+ return NULL;
+}
+
+static void *
+long_rw_r(void *varg)
+{
+ long_rw_t *arg = varg;
+ int stop;
+ do {
+ RWMUTEX_RLOCK(arg->rwlock);
+ ASSERT(!ATOMIC_READ(arg->is_wlocked));
+ ATOMIC_INC(arg->is_rlocked);
+ milli_sleep(arg->sleep);
+ stop = *arg->stop;
+ ATOMIC_DEC(arg->is_rlocked);
+ ASSERT(!ATOMIC_READ(arg->is_wlocked));
+ RWMUTEX_RUNLOCK(arg->rwlock);
+ } while (!stop);
+ return NULL;
+}
+
+
+static ERL_NIF_TERM long_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ int res, freqread, i, count, stop;
+ ATOMIC_T is_wlocked, is_rlocked;
+ THR_ID tid[LONG_RW_NO_THREADS];
+ long_rw_t arg;
+ long_rw_t targ[LONG_RW_NO_THREADS];
+
+ ATOMIC_INIT(&is_wlocked, 0);
+ ATOMIC_INIT(&is_rlocked, 0);
+
+ freqread = 0;
+
+ arg.is_wlocked = &is_wlocked;
+ arg.is_rlocked = &is_rlocked;
+ arg.count = &count;
+ arg.stop = &stop;
+
+ restart:
+
+ stop = 0;
+ count = 0;
+
+ arg.rwlock = RWMUTEX_CREATE(freqread);
+
+ ASSERT(arg.rwlock);
+
+ for (i = 0; i < LONG_RW_NO_W_THREADS; i++) {
+ targ[i] = arg;
+ targ[i].sleep = 100 + i*10;
+ ASSERT(THR_CREATE(&tid[i], long_rw_w, &targ[i], NULL) == 0);
+ }
+ for (; i < LONG_RW_NO_THREADS; i++) {
+ targ[i] = arg;
+ targ[i].sleep = 100;
+ ASSERT(THR_CREATE(&tid[i], long_rw_r, &targ[i], NULL) == 0);
+ }
+ for (i = 0; i < LONG_RW_NO_THREADS; i++)
+ ASSERT(THR_JOIN(tid[i], NULL) == 0);
+
+ ASSERT(!ATOMIC_READ(arg.is_wlocked));
+ ASSERT(!ATOMIC_READ(arg.is_rlocked));
+
+ RWMUTEX_DESTROY(arg.rwlock);
+
+ if (HAVE_FREQREAD_SUPPORT && !freqread) {
+ freqread = 1;
+ goto restart;
+ }
+
+ if (freqread)
+ return enif_make_atom(env, "ok");
+ else
+ return enif_make_tuple2(env,
+ enif_make_atom(env,
+ "comment"),
+ enif_make_string(env,
+ "No frequent read test made.",
+ ERL_NIF_LATIN1));
+}
+
+/*
+ * Hammer rwlock testcase
+ */
+
+#define HAMMER_RW_NO_W_THREADS 6
+#define HAMMER_RW_NO_THREADS 20
+#define HAMMER_RW_NO_WLOCK_COUNT 1000000
+
+typedef struct {
+ RWMUTEX_T *rwlock;
+ ATOMIC_T is_locked;
+ int lock_check;
+ int stop;
+ int count;
+} hammer_rw_t;
+
+static void *
+hammer_rw_w(void *varg)
+{
+ hammer_rw_t *arg = varg;
+ int stop = 0;
+ do {
+ RWMUTEX_WLOCK(arg->rwlock);
+ if (arg->lock_check) {
+ ASSERT(!ATOMIC_READ(&arg->is_locked));
+ ATOMIC_SET(&arg->is_locked, -1);
+ }
+ if (++arg->count > HAMMER_RW_NO_WLOCK_COUNT)
+ stop = arg->stop = 1;
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
+ ATOMIC_SET(&arg->is_locked, 0);
+ }
+ RWMUTEX_WUNLOCK(arg->rwlock);
+ } while (!stop);
+ return NULL;
+}
+
+static void *
+hammer_rw_r(void *varg)
+{
+ hammer_rw_t *arg = varg;
+ int stop;
+ do {
+ RWMUTEX_RLOCK(arg->rwlock);
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
+ ATOMIC_INC(&arg->is_locked);
+ }
+ stop = arg->stop;
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
+ ATOMIC_DEC(&arg->is_locked);
+ }
+ RWMUTEX_RUNLOCK(arg->rwlock);
+ } while (!stop);
+ return NULL;
+}
+
+
+static ERL_NIF_TERM hammer_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ hammer_rw_t arg;
+ char buf[10];
+ int res, freqread, i;
+ THR_ID tid[HAMMER_RW_NO_THREADS];
+
+ if (argc != 1)
+ goto badarg;
+
+ arg.lock_check = get_bool(env, argv[0]);
+ if (arg.lock_check < 0)
+ goto badarg;
+
+ ATOMIC_INIT(&arg.is_locked, 0);
+
+ freqread = 0;
+
+ restart:
+ arg.stop = 0;
+ arg.count = 0;
+
+ arg.rwlock = RWMUTEX_CREATE(freqread);
+
+ ASSERT(arg.rwlock);
+
+ for (i = 0; i < HAMMER_RW_NO_W_THREADS; i++)
+ ASSERT(THR_CREATE(&tid[i], hammer_rw_w, &arg, NULL) == 0);
+ for (; i < HAMMER_RW_NO_THREADS; i++)
+ ASSERT(THR_CREATE(&tid[i], hammer_rw_r, &arg, NULL) == 0);
+ for (i = 0; i < HAMMER_RW_NO_THREADS; i++)
+ ASSERT(THR_JOIN(tid[i], NULL) == 0);
+
+ ASSERT(!ATOMIC_READ(&arg.is_locked));
+
+ RWMUTEX_DESTROY(arg.rwlock);
+
+ if (HAVE_FREQREAD_SUPPORT && !freqread) {
+ freqread = 1;
+ goto restart;
+ }
+
+ if (freqread)
+ return enif_make_atom(env, "ok");
+ else
+ return enif_make_tuple2(env,
+ enif_make_atom(env,
+ "comment"),
+ enif_make_string(env,
+ "No frequent read test made.",
+ ERL_NIF_LATIN1));
+ badarg:
+ return enif_make_badarg(env);
+}
+
+/*
+ * Hammer try rwlock testcase
+ */
+
+#define HAMMER_TRYRW_NO_W_THREADS 10
+#define HAMMER_TRYRW_NO_THREADS 20
+#define HAMMER_TRYRW_NO_WLOCK_COUNT 10000000
+#define HAMMER_TRYRW_NO_RLOCK_COUNT 10000000
+#define HAMMER_TRYRW_NO_WLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_WLOCK_COUNT)/8)
+#define HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_RLOCK_COUNT)/8)
+
+typedef struct {
+ RWMUTEX_T *rwlock;
+ ATOMIC_T is_locked;
+ int lock_check;
+ int w_count;
+ ATOMIC_T r_count;
+} hammer_tryrw_t;
+
+static void *
+hammer_tryrw_w(void *varg)
+{
+ hammer_tryrw_t *arg = varg;
+ int stop = 0;
+ int wait = 0;
+ do {
+ while (EBUSY == RWMUTEX_TRYWLOCK(arg->rwlock));
+ if (arg->lock_check) {
+ ASSERT(!ATOMIC_READ(&arg->is_locked));
+ ATOMIC_SET(&arg->is_locked, -1);
+ }
+ if (++arg->w_count > HAMMER_TRYRW_NO_WLOCK_COUNT)
+ stop = 1;
+ else if (arg->w_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
+ wait = 1;
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
+ ATOMIC_SET(&arg->is_locked, 0);
+ }
+ RWMUTEX_WUNLOCK(arg->rwlock);
+ if (wait)
+ milli_sleep(1);
+ } while (!stop);
+ return NULL;
+}
+
+static void *
+hammer_tryrw_r(void *varg)
+{
+ hammer_tryrw_t *arg = varg;
+ long r_count;
+ int stop = 0;
+ int wait = 0;
+ do {
+ while (EBUSY == RWMUTEX_TRYRLOCK(arg->rwlock));
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
+ ATOMIC_INC(&arg->is_locked);
+ }
+ ATOMIC_INC(&arg->r_count);
+ r_count = ATOMIC_READ(&arg->r_count);
+ if (r_count > HAMMER_TRYRW_NO_RLOCK_COUNT)
+ stop = 1;
+ else if (r_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
+ wait = 1;
+ if (arg->lock_check) {
+ ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
+ ATOMIC_DEC(&arg->is_locked);
+ }
+ RWMUTEX_RUNLOCK(arg->rwlock);
+ if (wait)
+ milli_sleep(1);
+ } while (!stop);
+ return NULL;
+}
+
+
+static ERL_NIF_TERM hammer_tryrw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ hammer_tryrw_t arg;
+ char buf[10];
+ int res, freqread, i;
+ THR_ID tid[HAMMER_TRYRW_NO_THREADS];
+
+ if (argc != 1)
+ goto badarg;
+
+ arg.lock_check = get_bool(env, argv[0]);
+ if (arg.lock_check < 0)
+ goto badarg;
+
+ ATOMIC_INIT(&arg.is_locked, 0);
+ freqread = 0;
+
+ restart:
+
+ arg.w_count = 0;
+ ATOMIC_INIT(&arg.r_count, 0);
+
+ arg.rwlock = RWMUTEX_CREATE(freqread);
+
+ ASSERT(arg.rwlock);
+
+ for (i = 0; i < HAMMER_TRYRW_NO_W_THREADS; i++)
+ ASSERT(THR_CREATE(&tid[i], hammer_tryrw_w, &arg, NULL) == 0);
+ for (; i < HAMMER_TRYRW_NO_THREADS; i++)
+ ASSERT(THR_CREATE(&tid[i], hammer_tryrw_r, &arg, NULL) == 0);
+ for (i = 0; i < HAMMER_TRYRW_NO_THREADS; i++)
+ ASSERT(THR_JOIN(tid[i], NULL) == 0);
+
+ ASSERT(!ATOMIC_READ(&arg.is_locked));
+
+ RWMUTEX_DESTROY(arg.rwlock);
+
+ if (HAVE_FREQREAD_SUPPORT && !freqread) {
+ freqread = 1;
+ goto restart;
+ }
+
+ if (freqread)
+ return enif_make_atom(env, "ok");
+ else
+ return enif_make_tuple2(env,
+ enif_make_atom(env,
+ "comment"),
+ enif_make_string(env,
+ "No frequent read test made.",
+ ERL_NIF_LATIN1));
+ badarg:
+ return enif_make_badarg(env);
+}
+
+typedef struct {
+ int lock_check;
+ ATOMIC_T is_locked;
+ RWMUTEX_T *rwlock;
+} rwlock_resource_t;
+
+static void
+rwlock_destructor(ErlNifEnv* env, void* obj)
+{
+ rwlock_resource_t *rwlr = obj;
+ if (rwlr->lock_check)
+ ASSERT(!ATOMIC_READ(&rwlr->is_locked));
+ RWMUTEX_DESTROY(rwlr->rwlock);
+}
+
+/*
+ * create_rwlock(FreqRead, LockCheck)
+ */
+
+static ERL_NIF_TERM
+create_rwlock(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ int lock_check, freqread;
+ ERL_NIF_TERM rwlock_term;
+ rwlock_resource_t *rwlr;
+ char buf[100];
+
+ if (argc != 2)
+ goto badarg;
+
+ freqread = get_bool(env, argv[0]);
+ if (freqread < 0)
+ goto badarg;
+
+ if (!HAVE_FREQREAD_SUPPORT && freqread)
+ return enif_make_atom(env, "enotsup");
+
+ lock_check = get_bool(env, argv[1]);
+ if (lock_check < 0)
+ goto badarg;
+
+ rwlr = enif_alloc_resource(enif_priv_data(env), sizeof(rwlock_resource_t));
+ rwlr->lock_check = lock_check;
+ ATOMIC_INIT(&rwlr->is_locked, 0);
+ rwlr->rwlock = RWMUTEX_CREATE(freqread);
+ rwlock_term = enif_make_resource(env, rwlr);
+ enif_release_resource(rwlr);
+ return rwlock_term;
+
+ badarg:
+ return enif_make_badarg(env);
+}
+
+/*
+ * rwlock_op(RWLock, Blocking, WriteOp, WaitTime)
+ */
+
+static ERL_NIF_TERM
+rwlock_op(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ rwlock_resource_t *rwlr;
+ int blocking, write, wait_locked, wait_unlocked;
+
+ if (argc != 5)
+ goto badarg;
+
+ if (!enif_get_resource(env, argv[0], enif_priv_data(env), (void **) &rwlr))
+ goto badarg;
+
+ blocking = get_bool(env, argv[1]);
+ if (blocking < 0)
+ goto badarg;
+
+ write = get_bool(env, argv[2]);
+ if (write < 0)
+ goto badarg;
+
+ if (!enif_get_int(env, argv[3], &wait_locked))
+ goto badarg;
+ if (wait_locked < 0)
+ goto badarg;
+
+ if (!enif_get_int(env, argv[4], &wait_unlocked))
+ goto badarg;
+ if (wait_unlocked < 0)
+ goto badarg;
+
+ if (write) {
+ if (blocking)
+ RWMUTEX_WLOCK(rwlr->rwlock);
+ else
+ while (EBUSY == RWMUTEX_TRYWLOCK(rwlr->rwlock));
+ if (rwlr->lock_check) {
+ ASSERT(!ATOMIC_READ(&rwlr->is_locked));
+ ATOMIC_SET(&rwlr->is_locked, -1);
+ }
+ }
+ else {
+ if (blocking)
+ RWMUTEX_RLOCK(rwlr->rwlock);
+ else
+ while (EBUSY == RWMUTEX_TRYRLOCK(rwlr->rwlock));
+ if (rwlr->lock_check) {
+ ASSERT(ATOMIC_READ(&rwlr->is_locked) >= 0);
+ ATOMIC_INC(&rwlr->is_locked);
+ }
+ }
+
+ if (wait_locked)
+ milli_sleep(wait_locked);
+
+ if (write) {
+ if (rwlr->lock_check) {
+ ASSERT(ATOMIC_READ(&rwlr->is_locked) == -1);
+ ATOMIC_SET(&rwlr->is_locked, 0);
+ }
+ RWMUTEX_WUNLOCK(rwlr->rwlock);
+ }
+ else {
+ if (rwlr->lock_check) {
+ ASSERT(ATOMIC_READ(&rwlr->is_locked) > 0);
+ ATOMIC_DEC(&rwlr->is_locked);
+ }
+ RWMUTEX_RUNLOCK(rwlr->rwlock);
+ }
+
+ if (wait_unlocked)
+ milli_sleep(wait_unlocked);
+
+ return enif_make_atom(env, "ok");
+ badarg:
+ return enif_make_badarg(env);
+}
+
+static int load_nif_lib(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ *priv_data = enif_open_resource_type(env,
+ NULL,
+ "rwlock_resource",
+ rwlock_destructor,
+ ERL_NIF_RT_CREATE,
+ NULL);
+ if (*priv_data)
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * 0 -> false
+ * >0 -> true
+ * <0 -> error
+ */
+
+static int
+get_bool(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ int res;
+ char buf[10];
+
+ res = enif_get_atom(env, term, buf, sizeof(buf), ERL_NIF_LATIN1);
+ if (res == 0)
+ return -1;
+ if (strcmp("false", buf) == 0)
+ return 0;
+ else if (strcmp("true", buf) == 0)
+ return 1;
+ else
+ return -1;
+}
+
+static int
+fail(const char *file, int line, const char *function, const char *assertion)
+{
+ fprintf(stderr, "%s:%d: Assertion failed in %s(): %s\n",
+ file, line, function, assertion);
+ abort();
+}
+
+static void
+milli_sleep(int ms)
+{
+#ifdef __WIN32__
+ Sleep(ms);
+#else
+ while (erts_milli_sleep(ms) != 0);
+#endif
+}
+
+static ErlNifFunc nif_funcs[] = {
+ {"long_rw_test", 0, long_rw_test},
+ {"hammer_rw_test", 1, hammer_rw_test},
+ {"hammer_tryrw_test", 1, hammer_tryrw_test},
+ {"create_rwlock", 2, create_rwlock},
+ {"rwlock_op", 5, rwlock_op}
+};
+
+ERL_NIF_INIT(mtx_SUITE, nif_funcs, load_nif_lib, NULL, NULL, NULL)
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 93f9534138..7fe532abd0 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -2314,13 +2314,15 @@ close_deaf_port(Config) when is_list(Config) ->
?line port_close(Port),
Res = close_deaf_port_1(0, DeadPort),
+ io:format("Waiting for OS procs to terminate...\n"),
+ receive after 5*1000 -> ok end,
?line test_server:timetrap_cancel(Dog),
Res.
close_deaf_port_1(1000, _) ->
ok;
close_deaf_port_1(N, Cmd) ->
- Timeout = integer_to_list(random:uniform(10*1000)),
+ Timeout = integer_to_list(random:uniform(5*1000)),
?line try open_port({spawn_executable,Cmd},[{args,[Timeout]}]) of
Port ->
?line erlang:port_command(Port,"Hello, can you hear me!?!?"),
diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h
index 8d9d5e3d08..636fdc1e2f 100644
--- a/erts/include/internal/ethr_mutex.h
+++ b/erts/include/internal/ethr_mutex.h
@@ -33,6 +33,13 @@
# define ETHR_MTX_HARD_DEBUG
#endif
+#if 0
+# define ETHR_MTX_CHK_EXCL
+#if 1
+# define ETHR_MTX_CHK_NON_EXCL
+#endif
+#endif
+
#ifdef ETHR_MTX_HARD_DEBUG
# ifdef __GNUC__
# warning ETHR_MTX_HARD_DEBUG
@@ -49,6 +56,15 @@
#if defined(ETHR_USE_OWN_RWMTX_IMPL__) || defined(ETHR_USE_OWN_MTX_IMPL__)
+#ifdef ETHR_DEBUG
+# ifndef ETHR_MTX_CHK_EXCL
+# define ETHR_MTX_CHK_EXCL
+# endif
+# ifndef ETHR_MTX_CHK_NON_EXCL
+# define ETHR_MTX_CHK_NON_EXCL
+# endif
+#endif
+
#if 0
# define ETHR_MTX_Q_LOCK_SPINLOCK__
# define ETHR_MTX_QLOCK_TYPE__ ethr_spinlock_t
@@ -68,8 +84,8 @@
/* frequent read kind */
#define ETHR_RWMTX_R_FLG__ (((long) 1) << 28)
-#define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_FLG__ - 1)
-#define ETHR_RWMTX_R_MASK__ (ETHR_RWMTX_R_WAIT_FLG__ - 1)
+#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((long) 1) << 27)
+#define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_ABRT_UNLCK_FLG__ - 1)
/* normal kind */
#define ETHR_RWMTX_RS_MASK__ (ETHR_RWMTX_R_WAIT_FLG__ - 1)
@@ -91,6 +107,12 @@ struct ethr_mutex_base_ {
#ifdef ETHR_MTX_HARD_DEBUG_WSQ
int ws;
#endif
+#ifdef ETHR_MTX_CHK_EXCL
+ ethr_atomic_t exclusive;
+#endif
+#ifdef ETHR_MTX_CHK_NON_EXCL
+ ethr_atomic_t non_exclusive;
+#endif
#ifdef ETHR_MTX_HARD_DEBUG_LFS
ethr_atomic_t hdbg_lfs;
#endif
@@ -344,6 +366,116 @@ do { \
#define ETHR_MTX_HARD_DEBUG_FENCE_INIT(X)
#endif
+#ifdef ETHR_MTX_CHK_EXCL
+
+#if !defined(ETHR_DEBUG) && defined(__GNUC__)
+#warning "check exclusive is enabled"
+#endif
+
+# define ETHR_MTX_CHK_EXCL_INIT__(MTXB) \
+ ethr_atomic_init(&(MTXB)->exclusive, 0)
+
+# define ETHR_MTX_CHK_EXCL_IS_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ if (!ethr_atomic_read(&(MTXB)->exclusive)) \
+ ethr_assert_failed(__FILE__, __LINE__, __func__,\
+ "is exclusive"); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ if (ethr_atomic_read(&(MTXB)->exclusive)) \
+ ethr_assert_failed(__FILE__, __LINE__, __func__,\
+ "is not exclusive"); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_SET_EXCL(MTXB) \
+do { \
+ ETHR_MTX_CHK_EXCL_IS_NOT_EXCL((MTXB)); \
+ ethr_atomic_set(&(MTXB)->exclusive, 1); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_UNSET_EXCL(MTXB) \
+do { \
+ ETHR_MTX_CHK_EXCL_IS_EXCL((MTXB)); \
+ ethr_atomic_set(&(MTXB)->exclusive, 0); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+
+#ifdef ETHR_MTX_CHK_NON_EXCL
+
+#if !defined(ETHR_DEBUG) && defined(__GNUC__)
+#warning "check non-exclusive is enabled"
+#endif
+
+# define ETHR_MTX_CHK_NON_EXCL_INIT__(MTXB) \
+ ethr_atomic_init(&(MTXB)->non_exclusive, 0)
+# define ETHR_MTX_CHK_EXCL_IS_NON_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ if (!ethr_atomic_read(&(MTXB)->non_exclusive)) \
+ ethr_assert_failed(__FILE__, __LINE__, __func__,\
+ "is non-exclusive"); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ if (ethr_atomic_read(&(MTXB)->non_exclusive)) \
+ ethr_assert_failed(__FILE__, __LINE__, __func__,\
+ "is not non-exclusive"); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ ethr_atomic_inc(&(MTXB)->non_exclusive); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL_NO(MTXB, NO) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ ethr_atomic_add(&(MTXB)->non_exclusive, (NO)); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+# define ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(MTXB) \
+do { \
+ ETHR_COMPILER_BARRIER; \
+ ethr_atomic_dec(&(MTXB)->non_exclusive); \
+ ETHR_COMPILER_BARRIER; \
+} while (0)
+#else
+# define ETHR_MTX_CHK_NON_EXCL_INIT__(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL_NO(MTXB, NO)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(MTXB)
+#endif
+
+#else
+# define ETHR_MTX_CHK_EXCL_INIT__(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_SET_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_UNSET_EXCL(MTXB)
+# define ETHR_MTX_CHK_NON_EXCL_INIT__(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL_NO(MTXB, NO)
+# define ETHR_MTX_CHK_EXCL_SET_NON_EXCL(MTXB)
+# define ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(MTXB)
+#endif
+
+# define ETHR_MTX_CHK_EXCL_INIT(MTXB) \
+do { \
+ ETHR_MTX_CHK_EXCL_INIT__((MTXB)); \
+ ETHR_MTX_CHK_NON_EXCL_INIT__((MTXB)); \
+} while (0)
+
+
#ifdef ETHR_USE_OWN_MTX_IMPL__
#define ETHR_MTX_DEFAULT_MAIN_SPINCOUNT_MAX 2000
@@ -369,6 +501,11 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_trylock)(ethr_mutex *mtx)
act = ethr_atomic_cmpxchg_acqb(&mtx->mtxb.flgs, ETHR_RWMTX_W_FLG__, 0);
res = (act == 0) ? 0 : EBUSY;
+#ifdef ETHR_MTX_CHK_EXCL
+ if (res == 0)
+ ETHR_MTX_CHK_EXCL_SET_EXCL(&mtx->mtxb);
+#endif
+
ETHR_MTX_HARD_DEBUG_LFS_TRYRWLOCK(&mtx->mtxb, res);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx);
@@ -386,6 +523,8 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_lock)(ethr_mutex *mtx)
if (act != 0)
ethr_mutex_lock_wait__(mtx, act);
+ ETHR_MTX_CHK_EXCL_SET_EXCL(&mtx->mtxb);
+
ETHR_MTX_HARD_DEBUG_LFS_RWLOCK(&mtx->mtxb);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx);
@@ -400,6 +539,8 @@ ETHR_INLINE_FUNC_NAME_(ethr_mutex_unlock)(ethr_mutex *mtx)
ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx);
ETHR_MTX_HARD_DEBUG_LFS_RWUNLOCK(&mtx->mtxb);
+ ETHR_MTX_CHK_EXCL_UNSET_EXCL(&mtx->mtxb);
+
act = ethr_atomic_cmpxchg_relb(&mtx->mtxb.flgs, 0, ETHR_RWMTX_W_FLG__);
if (act != ETHR_RWMTX_W_FLG__)
ethr_mutex_unlock_wake__(mtx, act);
diff --git a/erts/include/internal/i386/atomic.h b/erts/include/internal/i386/atomic.h
index f28258059f..52d01aab32 100644
--- a/erts/include/internal/i386/atomic.h
+++ b/erts/include/internal/i386/atomic.h
@@ -167,15 +167,52 @@ ethr_native_atomic_xchg(ethr_native_atomic_t *var, long val)
* Atomic ops with at least specified barriers.
*/
-#define ethr_native_atomic_read_acqb ethr_native_atomic_read
-#define ethr_native_atomic_inc_return_acqb ethr_native_atomic_inc_return
+static ETHR_INLINE long
+ethr_native_atomic_read_acqb(ethr_native_atomic_t *var)
+{
+ long val;
+#if defined(__x86_64__) || !defined(ETHR_PRE_PENTIUM4_COMPAT)
+ val = var->counter;
+#else
+ val = ethr_native_atomic_add_return(var, 0);
+#endif
+ __asm__ __volatile__("" : : : "memory");
+ return val;
+}
+
+static ETHR_INLINE void
+ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long i)
+{
+ __asm__ __volatile__("" : : : "memory");
#if defined(__x86_64__) || !defined(ETHR_PRE_PENTIUM4_COMPAT)
-#define ethr_native_atomic_set_relb ethr_native_atomic_set
+ var->counter = i;
#else
-#define ethr_native_atomic_set_relb ethr_native_atomic_xchg
+ (void) ethr_native_atomic_xchg(var, i);
#endif
-#define ethr_native_atomic_dec_relb ethr_native_atomic_dec
-#define ethr_native_atomic_dec_return_relb ethr_native_atomic_dec_return
+}
+
+static ETHR_INLINE long
+ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var)
+{
+ long res = ethr_native_atomic_inc_return(var);
+ __asm__ __volatile__("" : : : "memory");
+ return res;
+}
+
+static ETHR_INLINE void
+ethr_native_atomic_dec_relb(ethr_native_atomic_t *var)
+{
+ __asm__ __volatile__("" : : : "memory");
+ ethr_native_atomic_dec(var);
+}
+
+static ETHR_INLINE long
+ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var)
+{
+ __asm__ __volatile__("" : : : "memory");
+ return ethr_native_atomic_dec_return(var);
+}
+
#define ethr_native_atomic_cmpxchg_acqb ethr_native_atomic_cmpxchg
#define ethr_native_atomic_cmpxchg_relb ethr_native_atomic_cmpxchg
diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h
index 2a995d4465..2da6472393 100644
--- a/erts/include/internal/sparc32/atomic.h
+++ b/erts/include/internal/sparc32/atomic.h
@@ -176,38 +176,59 @@ ethr_native_atomic_cmpxchg(ethr_native_atomic_t *var, long new, long old)
* Atomic ops with at least specified barriers.
*/
+/* TODO: relax acquire barriers */
+
static ETHR_INLINE long
ethr_native_atomic_read_acqb(ethr_native_atomic_t *var)
{
long res = ethr_native_atomic_read(var);
- __asm__ __volatile__("membar #StoreLoad|#StoreStore");
+ __asm__ __volatile__("membar #LoadLoad|#LoadStore|#StoreLoad|#StoreStore" : : : "memory");
return res;
}
static ETHR_INLINE void
ethr_native_atomic_set_relb(ethr_native_atomic_t *var, long i)
{
- __asm__ __volatile__("membar #LoadStore|#StoreStore");
+ __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory");
ethr_native_atomic_set(var, i);
}
+static ETHR_INLINE long
+ethr_native_atomic_inc_return_acqb(ethr_native_atomic_t *var)
+{
+ long res = ethr_native_atomic_inc_return(var);
+ __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory");
+ return res;
+}
+
static ETHR_INLINE void
ethr_native_atomic_dec_relb(ethr_native_atomic_t *var)
{
- __asm__ __volatile__("membar #LoadStore|#StoreStore");
+ __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory");
ethr_native_atomic_dec(var);
}
static ETHR_INLINE long
ethr_native_atomic_dec_return_relb(ethr_native_atomic_t *var)
{
- __asm__ __volatile__("membar #LoadStore|#StoreStore");
+ __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory");
return ethr_native_atomic_dec_return(var);
}
-#define ethr_native_atomic_inc_return_acqb ethr_native_atomic_inc_return
-#define ethr_native_atomic_cmpxchg_acqb ethr_native_atomic_cmpxchg
-#define ethr_native_atomic_cmpxchg_relb ethr_native_atomic_cmpxchg
+static ETHR_INLINE long
+ethr_native_atomic_cmpxchg_acqb(ethr_native_atomic_t *var, long new, long old)
+{
+ long res = ethr_native_atomic_cmpxchg(var, new, old);
+ __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory");
+ return res;
+}
+
+static ETHR_INLINE long
+ethr_native_atomic_cmpxchg_relb(ethr_native_atomic_t *var, long new, long old)
+{
+ __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory");
+ return ethr_native_atomic_cmpxchg(var, new, old);
+}
#endif /* ETHR_TRY_INLINE_FUNCS */
diff --git a/erts/lib_src/common/ethr_mutex.c b/erts/lib_src/common/ethr_mutex.c
index 78323b62a3..c5738c3d77 100644
--- a/erts/lib_src/common/ethr_mutex.c
+++ b/erts/lib_src/common/ethr_mutex.c
@@ -205,9 +205,14 @@ static void hard_debug_chk_q__(struct ethr_mutex_base_ *, int);
#ifdef ETHR_USE_OWN_RWMTX_IMPL__
static void
+rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx,
+ long initial,
+ int q_locked);
+static void
rwmutex_unlock_wake(ethr_rwmutex *rwmtx,
int have_w,
- long initial);
+ long initial,
+ int transfer_read_lock);
static int
rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
long initial,
@@ -334,7 +339,7 @@ rwmutex_freqread_rdrs_read(ethr_rwmutex *rwmtx, int ix)
ETHR_ASSERT(res >= 0);
break;
case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ:
- ETHR_ASSERT(res == 0 || res == 1);
+ ETHR_ASSERT(ix == 0 ? res >= 0 : (res == 0 || res == 1));
break;
default:
ETHR_ASSERT(0);
@@ -400,6 +405,7 @@ event_wait(struct ethr_mutex_base_ *mtxb,
int locked = 0;
long act;
int need_try_complete_runlock = 0;
+ int transfer_read_lock = 0;
/* Need to enqueue and wait... */
@@ -444,8 +450,8 @@ event_wait(struct ethr_mutex_base_ *mtxb,
while (1) {
long new, exp = act;
- int freqread_tryrlock = 0;
need_try_complete_runlock = 0;
+ transfer_read_lock = 0;
if (type == ETHR_RWMTX_W_WAIT_FLG__) {
if (is_freq_read && act == ETHR_RWMTX_R_FLG__)
@@ -465,14 +471,11 @@ event_wait(struct ethr_mutex_base_ *mtxb,
new = act + 1; /* Try to get it */
}
else {
- if (act & ~ETHR_RWMTX_R_FLG__)
- new = act | ETHR_RWMTX_R_WAIT_FLG__;
- else { /* Try to get it */
- ethr_rwmutex *rwmtx = (ethr_rwmutex *) mtxb;
- rwmutex_freqread_rdrs_inc(rwmtx, tse);
- ETHR_MEMORY_BARRIER;
- new = act | ETHR_RWMTX_R_FLG__;
- freqread_tryrlock = 1;
+ new = act | ETHR_RWMTX_R_WAIT_FLG__;
+ if ((act & (ETHR_RWMTX_W_FLG__
+ | ETHR_RWMTX_W_WAIT_FLG__)) == 0) {
+ /* Transfer read lock to this thread. */
+ transfer_read_lock = 1;
}
}
}
@@ -488,24 +491,6 @@ event_wait(struct ethr_mutex_base_ *mtxb,
goto done;
}
}
-
- if (freqread_tryrlock) {
- ethr_rwmutex *rwmtx = (ethr_rwmutex *) mtxb;
-
- /* We didn't set ETHR_RWMTX_R_FLG__, however someone
- else might have */
- if (act == ETHR_RWMTX_R_FLG__)
- goto done; /* Got it by help from someone else */
-
- ETHR_ASSERT((act & ETHR_RWMTX_WAIT_FLGS__) == 0);
- /*
- * We know that no waiter flags have been set, i.e.,
- * we cannot get into a situation where we need to wake
- * someone up here. Just restore the readers counter
- * and do it over again...
- */
- rwmutex_freqread_rdrs_dec(rwmtx, tse);
- }
}
/* Enqueue */
@@ -535,20 +520,36 @@ event_wait(struct ethr_mutex_base_ *mtxb,
/* Wait */
locked = 0;
- ETHR_MTX_Q_UNLOCK(&mtxb->qlck);
- if (need_try_complete_runlock) {
+ ETHR_ASSERT(!(transfer_read_lock && need_try_complete_runlock));
+
+ if (transfer_read_lock) {
ETHR_ASSERT(((ethr_rwmutex *) mtxb)->type
!= ETHR_RWMUTEX_TYPE_NORMAL);
/*
- * We were the only one in queue when we enqueued, and it
- * was seemingly read locked. We need to try to complete a
- * runlock otherwise we might be hanging forever. If the
- * runlock could be completed we will be dequeued and
- * woken by ourselves.
+ * We are the only one in the queue and we are not write
+ * locked; rwmutex_transfer_read_lock() will:
+ * - transfer a read lock to us (since we're first in q)
+ * - unlock the Q-lock
*/
- rwmutex_try_complete_runlock((ethr_rwmutex *) mtxb,
- act, tse, 0, 1, 0);
+ rwmutex_transfer_read_lock(((ethr_rwmutex *) mtxb), act, 1);
+ }
+ else {
+ ETHR_MTX_Q_UNLOCK(&mtxb->qlck);
+
+ if (need_try_complete_runlock) {
+ ETHR_ASSERT(((ethr_rwmutex *) mtxb)->type
+ != ETHR_RWMUTEX_TYPE_NORMAL);
+ /*
+ * We were the only one in queue when we enqueued, and it
+ * was seemingly read locked. We need to try to complete a
+ * runlock otherwise we might be hanging forever. If the
+ * runlock could be completed we will be dequeued and
+ * woken by ourselves.
+ */
+ rwmutex_try_complete_runlock((ethr_rwmutex *) mtxb,
+ act, tse, 0, 1, 0);
+ }
}
while (1) {
@@ -653,8 +654,6 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb,
ethr_ts_event *tse = NULL;
int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
int res;
- int freq_read_size = -1;
- int freq_read_start_ix = -1;
ETHR_ASSERT(!is_freq_read || is_rwmtx);
@@ -666,44 +665,23 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb,
*/
while (1) {
- long exp;
-
while (act != 0) {
if (is_freq_read && act == ETHR_RWMTX_R_FLG__) {
ethr_rwmutex *rwmtx = (ethr_rwmutex *) mtxb;
+ scnt--;
if (!tse)
tse = ethr_get_ts_event();
- if (freq_read_size < 0) {
- if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) {
- freq_read_size = reader_groups_array_size;
- freq_read_start_ix = tse->rgix;
- }
- else {
- freq_read_size = main_threads_array_size;
- freq_read_start_ix = tse->mtix;
- }
- }
- res = check_readers_array(rwmtx,
- freq_read_start_ix,
- freq_read_size);
- scnt--;
- if (res == 0) {
- act = ethr_atomic_read(&mtxb->flgs);
- if (act & ETHR_RWMTX_R_MASK__) {
- res = rwmutex_try_complete_runlock(rwmtx, act,
- tse, 0, 0,
- 1);
- if (res != EBUSY)
- goto done; /* Got it */
- }
- if (scnt <= 0)
- goto chk_spin;
- if (--until_yield == 0) {
- until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
- ETHR_YIELD();
- }
- continue;
+ res = rwmutex_try_complete_runlock(rwmtx, act,
+ tse, 0, 0,
+ 1);
+ if (res != EBUSY)
+ goto done; /* Got it */
+ if (scnt <= 0)
+ goto chk_spin;
+ if (--until_yield == 0) {
+ until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
+ ETHR_YIELD();
}
}
@@ -728,11 +706,9 @@ write_lock_wait(struct ethr_mutex_base_ *mtxb,
scnt--;
}
- exp = act;
-
act = ethr_atomic_cmpxchg_acqb(&mtxb->flgs,
ETHR_RWMTX_W_FLG__,
- exp);
+ 0);
if (act == 0)
goto done; /* Got it */
}
@@ -753,6 +729,7 @@ mtxb_init(struct ethr_mutex_base_ *mtxb,
#ifdef ETHR_MTX_HARD_DEBUG_WSQ
mtxb->ws = 0;
#endif
+ ETHR_MTX_CHK_EXCL_INIT(mtxb);
if (no_spin) {
mtxb->main_scnt = 0;
mtxb->aux_scnt = 0;
@@ -1254,7 +1231,7 @@ ethr_cond_wait(ethr_cond *cnd, ethr_mutex *mtx)
ETHR_MTX_HARD_DEBUG_LFS_RWLOCK(&mtx->mtxb);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(cnd);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(mtx);
-
+ ETHR_MTX_CHK_EXCL_SET_EXCL(&mtx->mtxb);
tse->udata = udata;
ethr_leave_ts_event(tse);
return 0;
@@ -1499,7 +1476,62 @@ int check_readers_array(ethr_rwmutex *rwmtx,
return 0;
}
-static ETHR_INLINE void
+static void
+rwmutex_freqread_rdrs_dec_chk_wakeup(ethr_rwmutex *rwmtx,
+ ethr_ts_event *tse,
+ long initial)
+{
+ long act = initial;
+
+ if ((act & (ETHR_RWMTX_W_FLG__|
+ ETHR_RWMTX_R_ABRT_UNLCK_FLG__)) == 0) {
+ if ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
+ if (act & ETHR_RWMTX_R_PEND_UNLCK_MASK__) {
+ /*
+ * We *need* to try to complete the runlock.
+ * A writer that just enqueued (not seen by us
+ * in flag field) may depend on someone else
+ * completing the runlock. We just took over
+ * that responsibilty since we modified reader
+ * groups.
+ */
+ rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 0, 0);
+ }
+ }
+ else if ((act & ETHR_RWMTX_WAIT_FLGS__) == ETHR_RWMTX_R_WAIT_FLG__)
+ rwmutex_transfer_read_lock(rwmtx, act, 0);
+ else if ((act & ETHR_RWMTX_WAIT_FLGS__) == ETHR_RWMTX_W_WAIT_FLG__)
+ rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 0, 0);
+ else {
+ /*
+ * Don't know if we got readers or writers
+ * first in queue; need to peek
+ */
+ ETHR_MTX_Q_LOCK(&rwmtx->mtxb.qlck);
+ if (!rwmtx->mtxb.q)
+ ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
+ else if (is_w_waiter(rwmtx->mtxb.q)) {
+ act = ethr_atomic_read(&rwmtx->mtxb.flgs);
+ ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
+ if ((act & ETHR_RWMTX_W_FLG__) == 0)
+ rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 0, 0);
+ }
+ else {
+ /*
+ * rwmutex_transfer_read_lock() will
+ * unlock Q lock.
+ */
+ act = ethr_atomic_read(&rwmtx->mtxb.flgs);
+ if (act & ETHR_RWMTX_W_FLG__)
+ ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
+ else
+ rwmutex_transfer_read_lock(rwmtx, act, 1);
+ }
+ }
+ }
+}
+
+static void
rwmutex_freqread_restore_failed_tryrlock(ethr_rwmutex *rwmtx,
ethr_ts_event *tse)
{
@@ -1509,24 +1541,11 @@ rwmutex_freqread_restore_failed_tryrlock(ethr_rwmutex *rwmtx,
*/
act = rwmutex_freqread_rdrs_dec_read(rwmtx, tse);
- ETHR_WRITE_MEMORY_BARRIER;
+ ETHR_MEMORY_BARRIER;
if (act == 0) {
-
-#ifndef ETHR_WRITE_MEMORY_BARRIER_IS_FULL
- ETHR_READ_MEMORY_BARRIER;
-#endif
-
act = ethr_atomic_read(&rwmtx->mtxb.flgs);
-
- if ((act & ETHR_RWMTX_W_FLG__) == 0
- && act & (ETHR_RWMTX_WAIT_FLGS__|ETHR_RWMTX_R_PEND_UNLCK_MASK__)) {
- /*
- * We either got waiters, or someone else trying
- * to read unlock which we might have to help.
- */
- rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 1, 0);
- }
+ rwmutex_freqread_rdrs_dec_chk_wakeup(rwmtx, tse, act);
}
}
@@ -1542,12 +1561,16 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
long act = initial;
int six, res, length;
+ ETHR_ASSERT((act & ETHR_RWMTX_W_FLG__) == 0);
+
+ if (act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__)
+ return try_write_lock ? EBUSY : 0;
+
tse_tmp = tse;
if (!tse_tmp)
tse_tmp = ethr_get_ts_event();
- if ((act & ETHR_RWMTX_WAIT_FLGS__)
- && (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0)
+ if ((act & ETHR_RWMTX_WAIT_FLGS__) && (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0)
goto check_waiters;
if (rwmtx->type == ETHR_RWMUTEX_TYPE_FREQUENT_READ) {
@@ -1569,14 +1592,21 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
if (check_before_try) {
res = check_readers_array(rwmtx, six, length);
+
+ ETHR_MEMORY_BARRIER;
+
if (res == EBUSY)
return try_write_lock ? EBUSY : 0;
}
+ restart:
+
while (1) {
long exp = act;
long new = act+1;
+ ETHR_ASSERT((act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__) == 0);
+
ETHR_ASSERT((act & ETHR_RWMTX_R_PEND_UNLCK_MASK__)
< ETHR_RWMTX_R_PEND_UNLCK_MASK__);
@@ -1585,8 +1615,10 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
act = new;
break;
}
+
if (!try_write_lock) {
- if (act == ETHR_RWMTX_W_FLG__ || act == 0)
+ if (act == 0 || (act & (ETHR_RWMTX_W_FLG__
+ | ETHR_RWMTX_R_ABRT_UNLCK_FLG__)))
return 0;
if ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
if ((act & ETHR_RWMTX_R_FLG__) == 0)
@@ -1601,33 +1633,50 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
else {
if (act == 0)
goto tryrwlock;
- if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_WAIT_FLGS__))
+ if (act & (ETHR_RWMTX_W_FLG__
+ | ETHR_RWMTX_R_ABRT_UNLCK_FLG__))
return EBUSY;
}
}
res = check_readers_array(rwmtx, six, length);
- if (res == EBUSY) {
- act = ethr_atomic_dec_read(&rwmtx->mtxb.flgs);
- if (act & ETHR_RWMTX_R_MASK__)
- return try_write_lock ? EBUSY : 0;
- }
- else {
- while (1) {
- long exp = act;
- long new = act;
- new &= ~ETHR_RWMTX_R_FLG__;
- new--;
- ETHR_ASSERT(act & ETHR_RWMTX_R_PEND_UNLCK_MASK__);
+ ETHR_MEMORY_BARRIER;
- act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
- if (exp == act) {
- if (new & ETHR_RWMTX_R_PEND_UNLCK_MASK__)
- return try_write_lock ? EBUSY : 0;
- act = new;
- break;
+ ETHR_ASSERT((act & ETHR_RWMTX_W_FLG__) == 0);
+
+ while (1) {
+ int finished_abort = 0;
+ long exp = act;
+ long new = act;
+
+ new--;
+ if (act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__) {
+ if ((new & ETHR_RWMTX_R_PEND_UNLCK_MASK__) == 0) {
+ new &= ~ETHR_RWMTX_R_ABRT_UNLCK_FLG__;
+ finished_abort = 1;
}
+ ETHR_ASSERT(act & ETHR_RWMTX_R_FLG__);
+ }
+ else if ((act & ETHR_RWMTX_R_FLG__) && res != EBUSY) {
+ new &= ~ETHR_RWMTX_R_FLG__;
+ }
+
+ ETHR_ASSERT(act & ETHR_RWMTX_R_PEND_UNLCK_MASK__);
+
+ act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
+ if (exp == act) {
+ act = new;
+ if (act & ETHR_RWMTX_W_FLG__)
+ return try_write_lock ? EBUSY : 0;
+ if (finished_abort && (act & ETHR_RWMTX_WAIT_FLGS__))
+ goto restart;
+ if (act & (ETHR_RWMTX_R_FLG__
+ | ETHR_RWMTX_R_ABRT_UNLCK_FLG__
+ | ETHR_RWMTX_R_PEND_UNLCK_MASK__))
+ return try_write_lock ? EBUSY : 0;
+ /* Read unlock completed */
+ break;
}
}
@@ -1637,12 +1686,9 @@ rwmutex_try_complete_runlock(ethr_rwmutex *rwmtx,
* to write lock it).
*/
- if (act & ETHR_RWMTX_W_FLG__)
- return try_write_lock ? EBUSY : 0;
-
if (act & ETHR_RWMTX_WAIT_FLGS__) {
check_waiters:
- rwmutex_unlock_wake(rwmtx, 0, act);
+ rwmutex_unlock_wake(rwmtx, 0, act, 0);
return try_write_lock ? EBUSY : 0;
}
@@ -1670,7 +1716,7 @@ rwmutex_incdec_restore_failed_tryrlock(ethr_rwmutex *rwmtx)
act = ethr_atomic_dec_read(&rwmtx->mtxb.flgs);
if ((act & ETHR_RWMTX_WAIT_FLGS__)
&& (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0) {
- rwmutex_unlock_wake(rwmtx, 0, act);
+ rwmutex_unlock_wake(rwmtx, 0, act, 0);
}
}
@@ -1700,7 +1746,7 @@ rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx,
#endif
while (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) {
- if (scnt >= 0) {
+ if (scnt <= 0) {
tse = ethr_get_ts_event();
if (update_spincount(&rwmtx->mtxb, tse, &start_scnt, &scnt)) {
event_wait(&rwmtx->mtxb, tse, scnt,
@@ -1736,10 +1782,83 @@ rwmutex_normal_rlock_wait(ethr_rwmutex *rwmtx,
static void
rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx,
- ethr_ts_event *tse,
- long initial)
+ ethr_ts_event *tse);
+
+static int
+rwmutex_freqread_rlock(ethr_rwmutex *rwmtx, ethr_ts_event *tse, int trylock)
{
- long act = initial;
+ int res = 0;
+ long act;
+
+ rwmutex_freqread_rdrs_inc(rwmtx, tse);
+
+ ETHR_MEMORY_BARRIER;
+
+ act = ethr_atomic_read_acqb(&rwmtx->mtxb.flgs);
+
+ if (act != ETHR_RWMTX_R_FLG__) {
+ int wake_other_readers;
+
+ while (1) {
+ long exp, new;
+
+ wake_other_readers = 0;
+
+ if (act == 0)
+ new = act | ETHR_RWMTX_R_FLG__;
+ else if (act == ETHR_RWMTX_R_FLG__)
+ break; /* Got it */
+ else if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) {
+ rwmutex_freqread_restore_failed_tryrlock(rwmtx, tse);
+ if (trylock)
+ res = EBUSY;
+ else
+ rwmutex_freqread_rlock_wait(rwmtx, tse);
+ break;
+ }
+ else if (act & ETHR_RWMTX_R_ABRT_UNLCK_FLG__) {
+ if ((act & ETHR_RWMTX_R_FLG__) == 0)
+ ETHR_FATAL_ERROR__(EFAULT);
+ /*
+ * An aborted runlock, not write locked, and no write
+ * waiters, i.e., we got it...
+ */
+ if (act & ETHR_RWMTX_R_WAIT_FLG__)
+ wake_other_readers = 1;
+ break;
+ }
+ else {
+ new = act | ETHR_RWMTX_R_FLG__;
+ if (act & ETHR_RWMTX_R_PEND_UNLCK_MASK__) {
+ /*
+ * Someone is doing tryrwlock (no writer and no
+ * write waiters); we will try to abort that...
+ */
+ new |= ETHR_RWMTX_R_ABRT_UNLCK_FLG__;
+ }
+
+ if (act & ETHR_RWMTX_R_WAIT_FLG__)
+ wake_other_readers = 1;
+ }
+
+ exp = act;
+ act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, new, exp);
+ if (act == exp)
+ break;
+ }
+
+ if (wake_other_readers)
+ rwmutex_transfer_read_lock(rwmtx, act, 0);
+ }
+
+ return res;
+}
+
+static void
+rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx,
+ ethr_ts_event *tse)
+{
+ long act;
int scnt, start_scnt;
int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS;
@@ -1752,12 +1871,10 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx,
while (1) {
- rwmutex_freqread_restore_failed_tryrlock(rwmtx, tse);
-
act = ethr_atomic_read(&rwmtx->mtxb.flgs);
- while (act & ~(ETHR_RWMTX_R_FLG__|ETHR_RWMTX_R_WAIT_FLG__)) {
- if (scnt >= 0) {
+ while (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) {
+ if (scnt <= 0) {
if (update_spincount(&rwmtx->mtxb, tse, &start_scnt, &scnt)) {
event_wait(&rwmtx->mtxb, tse, scnt,
ETHR_RWMTX_R_WAIT_FLG__, 1, 1);
@@ -1773,30 +1890,8 @@ rwmutex_freqread_rlock_wait(ethr_rwmutex *rwmtx,
scnt--;
}
- rwmutex_freqread_rdrs_inc(rwmtx, tse);
-
- ETHR_MEMORY_BARRIER;
-
- act = ethr_atomic_read(&rwmtx->mtxb.flgs);
-
- if (act == ETHR_RWMTX_R_FLG__)
- return; /* Got it */
-
- while (1) {
- long exp, new;
-
- if (act & ~(ETHR_RWMTX_R_FLG__|ETHR_RWMTX_R_WAIT_FLG__))
- break; /* Busy (need to restore inc) */
-
- if (act & ETHR_RWMTX_R_FLG__)
- return; /* Got it */
-
- exp = act;
- new = act | ETHR_RWMTX_R_FLG__;
- act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
- if (act == exp)
- return; /* Got it */
- }
+ if (rwmutex_freqread_rlock(rwmtx, tse, 1) != EBUSY)
+ break; /* Got it */
}
}
@@ -1816,14 +1911,23 @@ static ETHR_INLINE void
rwlock_wake_set_flags(ethr_rwmutex *rwmtx, long new_initial, int act_initial)
{
long act, act_mask;
+ int chk_abrt_flg;
+
+ ETHR_MEMORY_BARRIER;
+
if (rwmtx->type != ETHR_RWMUTEX_TYPE_NORMAL) {
/* r pend unlock mask may vary and must be retained */
act_mask = ETHR_RWMTX_R_PEND_UNLCK_MASK__;
+ if (new_initial & ETHR_RWMTX_R_FLG__)
+ chk_abrt_flg = 1;
+ else
+ chk_abrt_flg = 0;
}
else {
#ifdef ETHR_RLOCK_WITH_INC_DEC
/* rs mask may vary and must be retained */
act_mask = ETHR_RWMTX_RS_MASK__;
+ chk_abrt_flg = 0;
#else
/* rs mask always zero */
ETHR_ASSERT((act_initial & ETHR_RWMTX_RS_MASK__) == 0);
@@ -1836,6 +1940,8 @@ rwlock_wake_set_flags(ethr_rwmutex *rwmtx, long new_initial, int act_initial)
while (1) {
long exp = act;
long new = new_initial + (act & act_mask);
+ if (chk_abrt_flg && (act & act_mask))
+ new |= ETHR_RWMTX_R_ABRT_UNLCK_FLG__;
act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
if (act == exp)
break;
@@ -1883,6 +1989,14 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx,
exp |= ETHR_RWMTX_R_WAIT_FLG__;
if (rwmtx->rq_end->next != rwmtx->mtxb.q)
exp |= ETHR_RWMTX_W_WAIT_FLG__;
+ else if (exp == ETHR_RWMTX_R_WAIT_FLG__) {
+ if (!have_w) {
+ if (rwmtx->type != ETHR_RWMUTEX_TYPE_NORMAL)
+ imask |= ETHR_RWMTX_R_FLG__;
+ else
+ imask |= ETHR_RWMTX_RS_MASK__;
+ }
+ }
act = ethr_atomic_read(&rwmtx->mtxb.flgs);
ETHR_ASSERT((exp & ~imask) == (act & ~imask));
@@ -1894,41 +2008,83 @@ dbg_unlock_wake(ethr_rwmutex *rwmtx,
#endif
static void
-rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial)
+rwmutex_transfer_read_lock(ethr_rwmutex *rwmtx, long initial, int q_locked)
+{
+ long act = initial;
+
+ if (!q_locked) {
+ ethr_ts_event *tse;
+ ETHR_ASSERT(initial & ETHR_RWMTX_R_WAIT_FLG__);
+ ETHR_ASSERT((initial & ETHR_RWMTX_W_FLG__) == 0);
+ ETHR_MTX_Q_LOCK(&rwmtx->mtxb.qlck);
+
+ act = ethr_atomic_read(&rwmtx->mtxb.flgs);
+ tse = rwmtx->mtxb.q;
+ if ((act & ETHR_RWMTX_W_FLG__) || !tse || is_w_waiter(tse)) {
+ /* Someone else woke the readers up... */
+ ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
+ return;
+ }
+ }
+
+ rwmutex_unlock_wake(rwmtx, 0, initial, 1);
+}
+
+static void
+rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial,
+ int transfer_read_lock)
{
long new, act = initial;
ethr_ts_event *tse;
- if ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
- if (!have_w)
- return;
- else {
- while ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
- long exp = act;
- new = exp & ~ETHR_RWMTX_W_FLG__;
- act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
- if (act == exp)
- return;
+ if (transfer_read_lock) {
+ /*
+ * - Q already locked
+ * - Got R waiters first in Q
+ * - Not W locked
+ */
+ tse = rwmtx->mtxb.q;
+
+ ETHR_ASSERT(act & ETHR_RWMTX_R_WAIT_FLG__);
+ ETHR_ASSERT((act & (ETHR_RWMTX_W_FLG__)) == 0);
+ ETHR_ASSERT(tse && !is_w_waiter(tse));
+ }
+ else {
+
+ if ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
+ if (!have_w)
+ return;
+ else {
+ while ((act & ETHR_RWMTX_WAIT_FLGS__) == 0) {
+ long exp = act;
+ new = exp & ~ETHR_RWMTX_W_FLG__;
+ act = ethr_atomic_cmpxchg(&rwmtx->mtxb.flgs, new, exp);
+ if (act == exp)
+ return;
+ }
}
}
- }
- ETHR_MTX_Q_LOCK(&rwmtx->mtxb.qlck);
- tse = rwmtx->mtxb.q;
+ ETHR_MTX_Q_LOCK(&rwmtx->mtxb.qlck);
+ tse = rwmtx->mtxb.q;
- if (!have_w) {
- if (!tse) {
+ if (!have_w) {
+ if (!tse) {
#ifdef ETHR_DEBUG
- act = ethr_atomic_read(&rwmtx->mtxb.flgs);
- ETHR_ASSERT((act & ETHR_RWMTX_WAIT_FLGS__) == 0);
+ act = ethr_atomic_read(&rwmtx->mtxb.flgs);
+ ETHR_ASSERT((act & ETHR_RWMTX_WAIT_FLGS__) == 0);
#endif
- goto already_served;
- }
- act = ethr_atomic_read(&rwmtx->mtxb.flgs);
- if (act & ~ETHR_RWMTX_WAIT_FLGS__) {
- already_served:
- ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
- return;
+ goto already_served;
+ }
+ act = ethr_atomic_read(&rwmtx->mtxb.flgs);
+ if (act == (ETHR_RWMTX_R_WAIT_FLG__|ETHR_RWMTX_R_FLG__)) {
+ ETHR_ASSERT(tse && !is_w_waiter(tse));
+ }
+ else if (act & ~ETHR_RWMTX_WAIT_FLGS__) {
+ already_served:
+ ETHR_MTX_Q_UNLOCK(&rwmtx->mtxb.qlck);
+ return;
+ }
}
}
@@ -1988,6 +2144,7 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial)
rwmutex_freqread_rdrs_add(rwmtx, type, ix, wrs);
}
}
+
new = ETHR_RWMTX_R_FLG__;
}
@@ -1995,6 +2152,7 @@ rwmutex_unlock_wake(ethr_rwmutex *rwmtx, int have_w, long initial)
new |= ETHR_RWMTX_W_WAIT_FLG__;
rwlock_wake_set_flags(rwmtx, new, act);
+
wake_readers(rwmtx, rs);
}
}
@@ -2225,39 +2383,19 @@ ethr_rwmutex_tryrlock(ethr_rwmutex *rwmtx)
case ETHR_RWMUTEX_TYPE_FREQUENT_READ:
case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ: {
ethr_ts_event *tse = ethr_get_ts_event();
-
- rwmutex_freqread_rdrs_inc(rwmtx, tse);
-
- ETHR_MEMORY_BARRIER;
-
- act = ethr_atomic_read_acqb(&rwmtx->mtxb.flgs);
-
- if (act != ETHR_RWMTX_R_FLG__) {
- while (1) {
- long exp, new;
-
- if (act & ~(ETHR_RWMTX_R_FLG__|ETHR_RWMTX_R_WAIT_FLG__)) {
- rwmutex_freqread_restore_failed_tryrlock(rwmtx, tse);
- res = EBUSY;
- break;
- }
-
- if (act & ETHR_RWMTX_R_FLG__)
- break;
-
- exp = act;
- new = act | ETHR_RWMTX_R_FLG__;
- act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, new, exp);
- if (act == exp)
- break;
- }
- }
-
+ res = rwmutex_freqread_rlock(rwmtx, tse, 1);
ethr_leave_ts_event(tse);
break;
}
}
+#ifdef ETHR_MTX_CHK_EXCL
+ if (res == 0) {
+ ETHR_MTX_CHK_EXCL_SET_NON_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(&rwmtx->mtxb);
+ }
+#endif
+
ETHR_MTX_HARD_DEBUG_LFS_TRYRLOCK(&rwmtx->mtxb, res);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(rwmtx);
@@ -2286,9 +2424,8 @@ ethr_rwmutex_rlock(ethr_rwmutex *rwmtx)
while (1) {
act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, exp+1, exp);
- if (act == exp) {
+ if (act == exp)
break;
- }
if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_W_WAIT_FLG__)) {
rwmutex_normal_rlock_wait(rwmtx, act);
@@ -2303,38 +2440,14 @@ ethr_rwmutex_rlock(ethr_rwmutex *rwmtx)
case ETHR_RWMUTEX_TYPE_FREQUENT_READ:
case ETHR_RWMUTEX_TYPE_EXTREMELY_FREQUENT_READ: {
ethr_ts_event *tse = ethr_get_ts_event();
-
- rwmutex_freqread_rdrs_inc(rwmtx, tse);
-
- ETHR_MEMORY_BARRIER;
-
- act = ethr_atomic_read_acqb(&rwmtx->mtxb.flgs);
-
- if (act != ETHR_RWMTX_R_FLG__) {
- while (1) {
- long exp, new;
-
- if (act & ~(ETHR_RWMTX_R_FLG__|ETHR_RWMTX_R_WAIT_FLG__)) {
- rwmutex_freqread_rlock_wait(rwmtx, tse, act);
- break;
- }
-
- if (act & ETHR_RWMTX_R_FLG__)
- break;
-
- exp = act;
- new = act | ETHR_RWMTX_R_FLG__;
- act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs, new, exp);
- if (act == exp)
- break;
- }
- }
-
+ rwmutex_freqread_rlock(rwmtx, tse, 0);
ethr_leave_ts_event(tse);
break;
}
}
+ ETHR_MTX_CHK_EXCL_SET_NON_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(&rwmtx->mtxb);
ETHR_MTX_HARD_DEBUG_LFS_RLOCK(&rwmtx->mtxb);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(rwmtx);
}
@@ -2344,6 +2457,8 @@ ethr_rwmutex_runlock(ethr_rwmutex *rwmtx)
{
long act;
+ ETHR_MTX_CHK_EXCL_IS_NOT_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_UNSET_NON_EXCL(&rwmtx->mtxb);
ETHR_ASSERT(!ethr_not_inited__);
ETHR_ASSERT(rwmtx);
ETHR_ASSERT(rwmtx->initialized == ETHR_RWMUTEX_INITIALIZED);
@@ -2357,7 +2472,7 @@ ethr_rwmutex_runlock(ethr_rwmutex *rwmtx)
if ((act & ETHR_RWMTX_WAIT_FLGS__)
&& (act & ~ETHR_RWMTX_WAIT_FLGS__) == 0) {
ETHR_ASSERT((act & ETHR_RWMTX_W_FLG__) == 0);
- rwmutex_unlock_wake(rwmtx, 0, act);
+ rwmutex_unlock_wake(rwmtx, 0, act, 0);
}
break;
@@ -2369,21 +2484,12 @@ ethr_rwmutex_runlock(ethr_rwmutex *rwmtx)
ETHR_ASSERT(act >= 0);
- ETHR_WRITE_MEMORY_BARRIER;
+ ETHR_MEMORY_BARRIER;
if (act == 0) {
-
-#ifndef ETHR_WRITE_MEMORY_BARRIER_IS_FULL
- ETHR_READ_MEMORY_BARRIER;
-#endif
act = ethr_atomic_read(&rwmtx->mtxb.flgs);
-
- if ((act & ETHR_RWMTX_W_FLG__) == 0
- && (act & (ETHR_RWMTX_WAIT_FLGS__
- | ETHR_RWMTX_R_PEND_UNLCK_MASK__))) {
- rwmutex_try_complete_runlock(rwmtx, act, tse, 1, 0, 0);
- }
-
+ if (act != ETHR_RWMTX_R_FLG__)
+ rwmutex_freqread_rdrs_dec_chk_wakeup(rwmtx, tse, act);
}
ethr_leave_ts_event(tse);
@@ -2422,25 +2528,31 @@ ethr_rwmutex_tryrwlock(ethr_rwmutex *rwmtx)
do {
- if (act & (ETHR_RWMTX_W_FLG__|ETHR_RWMTX_WAIT_FLGS__)) {
- res = EBUSY;
- break;
- }
-
- if (act & ETHR_RWMTX_R_MASK__) {
+ if (act == 0)
+ act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs,
+ ETHR_RWMTX_W_FLG__, 0);
+ else if (act == ETHR_RWMTX_R_FLG__) {
res = rwmutex_try_complete_runlock(rwmtx, act, NULL,
0, 1, 1);
break;
}
-
- act = ethr_atomic_cmpxchg_acqb(&rwmtx->mtxb.flgs,
- ETHR_RWMTX_W_FLG__, 0);
+ else {
+ res = EBUSY;
+ break;
+ }
} while (act != 0);
break;
}
+#ifdef ETHR_MTX_CHK_EXCL
+ if (res == 0) {
+ ETHR_MTX_CHK_EXCL_SET_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(&rwmtx->mtxb);
+ }
+#endif
+
ETHR_MTX_HARD_DEBUG_LFS_TRYRWLOCK(&rwmtx->mtxb, res);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(rwmtx);
@@ -2485,6 +2597,8 @@ ethr_rwmutex_rwlock(ethr_rwmutex *rwmtx)
break;
}
+ ETHR_MTX_CHK_EXCL_SET_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(&rwmtx->mtxb);
ETHR_MTX_HARD_DEBUG_LFS_RWLOCK(&rwmtx->mtxb);
ETHR_MTX_HARD_DEBUG_FENCE_CHK(rwmtx);
@@ -2501,12 +2615,15 @@ ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx)
ETHR_MTX_HARD_DEBUG_FENCE_CHK(rwmtx);
ETHR_MTX_HARD_DEBUG_LFS_RWUNLOCK(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_IS_NOT_NON_EXCL(&rwmtx->mtxb);
+ ETHR_MTX_CHK_EXCL_UNSET_EXCL(&rwmtx->mtxb);
+
switch (rwmtx->type) {
case ETHR_RWMUTEX_TYPE_NORMAL:
act = ethr_atomic_cmpxchg_relb(&rwmtx->mtxb.flgs,
0, ETHR_RWMTX_W_FLG__);
if (act != ETHR_RWMTX_W_FLG__)
- rwmutex_unlock_wake(rwmtx, 1, act);
+ rwmutex_unlock_wake(rwmtx, 1, act, 0);
break;
case ETHR_RWMUTEX_TYPE_FREQUENT_READ:
@@ -2514,7 +2631,7 @@ ethr_rwmutex_rwunlock(ethr_rwmutex *rwmtx)
act = ethr_atomic_cmpxchg_relb(&rwmtx->mtxb.flgs, 0,
ETHR_RWMTX_W_FLG__);
if (act != ETHR_RWMTX_W_FLG__)
- rwmutex_unlock_wake(rwmtx, 1, act);
+ rwmutex_unlock_wake(rwmtx, 1, act, 0);
break;
}
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
index 19b006e750..1628ca69b1 100644
--- a/lib/debugger/test/int_eval_SUITE.erl
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -65,10 +65,7 @@ bifs_outside_erlang(Config) when is_list(Config) ->
Self = self(),
ok = io:format("Self: ~p", [Self]),
Info = ets:info(Id),
- {owner,Self} = lists:nth(2, Info),
- %% Was
- %% {owner,Self} = element(2, Info),
- %% in R10B.
+ Self = proplists:get_value(owner, Info),
?IM:ets_delete(Id),
ok
end,
diff --git a/lib/erl_docgen/Makefile b/lib/erl_docgen/Makefile
index c5bed632a5..93a6353cac 100644
--- a/lib/erl_docgen/Makefile
+++ b/lib/erl_docgen/Makefile
@@ -1,19 +1,20 @@
-# ``The contents of this file are subject to the Erlang Public License,
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
-# retrieved via the world wide web at http://www.erlang.org/.
+# retrieved online at http://www.erlang.org/.
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
#
-# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-# AB. All Rights Reserved.''
-#
-# $Id$
+# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -22,9 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Macros
#
-SUB_DIRECTORIES = priv
+SUB_DIRECTORIES = src priv
#doc/src
+include vsn.mk
+VSN = $(ERL_DOCGEN_VSN)
SPECIAL_TARGETS =
diff --git a/lib/erl_docgen/ebin/.gitignore b/lib/erl_docgen/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/erl_docgen/ebin/.gitignore
diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript
new file mode 100644
index 0000000000..840fed6dd5
--- /dev/null
+++ b/lib/erl_docgen/priv/bin/specs_gen.escript
@@ -0,0 +1,129 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%% <script> [-I<dir>]... [-o<dir>] [-module Module] [File]
+%%%
+%%% Use EDoc and the layout module 'otp_specs' to create an XML file
+%%% containing Dialyzer types and specifications (-type, -spec).
+%%%
+%%% Options:
+%%%
+%%% "-o<dir>" The output directory for the created file.
+%%% Default is ".".
+%%% "-I<dir>" Directory to be searched when including a file.
+%%% "-module Module"
+%%% Module name to use when there is no File argument.
+%%% A temporary file will be created.
+%%% Exactly one of -module Module and File must be given.
+%%%
+%%% The name of the generated file is "specs_<module>.xml". Its exact
+%%% format is not further described here.
+
+main(Args) ->
+ case catch parse(Args, [], ".", no_module) of
+ {ok, FileSpec, InclFs, Dir} ->
+ call_edoc(FileSpec, InclFs, Dir);
+ {error, Msg} ->
+ io:format("~s\n", [Msg]),
+ usage()
+ end.
+
+parse(["-o"++Dir | Opts], InclFs, _, Module) ->
+ parse(Opts, InclFs, Dir, Module);
+parse(["-I"++I | Opts], InclFs, Dir, Module) ->
+ parse(Opts, [I | InclFs], Dir, Module);
+parse(["-module", Module | Opts], InclFs, Dir, _) ->
+ parse(Opts, InclFs, Dir, Module);
+parse([File], InclFs, Dir, no_module) ->
+ {ok, {file, File}, lists:reverse(InclFs), Dir};
+parse([_], _, _, _) ->
+ {error, io_lib:format("Cannot have both -module option and file", [])};
+parse([], _, _, no_module) ->
+ {error, io_lib:format("Missing -module option or file", [])};
+parse([], InclFs, Dir, Module) ->
+ {ok, {module, Module}, lists:reverse(InclFs), Dir};
+parse(Args, _, _, _) ->
+ {error, io_lib:format("Bad arguments: ~p", [Args])}.
+
+usage() ->
+ io:format("usage: ~s [-I<include_dir>]... [-o<out_dir>] "
+ "[-module <module>] [file]\n", [escript:script_name()]),
+ halt(1).
+
+call_edoc(FileSpec, InclFs, Dir) ->
+ Incl = [{includes, InclFs}],
+ Pre = [{preprocess, true}],
+ Choice = [{dialyzer_specs, all}],
+ DirOpt = [{dir, Dir}],
+ Pretty = [{pretty_print, erl_pp}],
+ Layout = [{layout, otp_specs},
+ {file_suffix, ".specs"},
+ {stylesheet, ""}],
+ Warn = [{report_missing_type, false},
+ {report_type_mismatch, false}],
+ OptionList = (DirOpt ++ Choice ++ Pre ++ Warn ++ Pretty ++ Layout ++ Incl),
+ {File, TmpFile} = case FileSpec of
+ {file, File0} ->
+ {File0, false};
+ {module, Module} ->
+ {create_tmp_file(Dir, Module), true}
+ end,
+ try edoc:files([File], OptionList) of
+ ok ->
+ clean_up(Dir, File, TmpFile),
+ rename(Dir, File)
+ catch
+ _:_ ->
+ io:format("EDoc could not process file '~s'\n", [File]),
+ clean_up(Dir, File, TmpFile),
+ halt(3)
+ end.
+
+rename(Dir, F) ->
+ Mod = filename:basename(F, ".erl"),
+ Old = filename:join(Dir, Mod ++ ".specs"),
+ New = filename:join(Dir, "specs_" ++ Mod ++ ".xml"),
+ case file:rename(Old, New) of
+ ok ->
+ ok;
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not rename file '~s': ~s\n", [New, R1]),
+ halt(2)
+ end.
+
+clean_up(Dir, File, TmpFile) ->
+ [file:delete(File) || TmpFile],
+ _ = [file:delete(filename:join(Dir, F)) ||
+ F <- ["packages-frame.html",
+ "overview-summary.html",
+ "modules-frame.html",
+ "index.html", "erlang.png", "edoc-info"]],
+ ok.
+
+create_tmp_file(Dir, Module) ->
+ TmpFile = filename:join(Dir, Module++".erl"),
+ case file:write_file(TmpFile, "-module(" ++ Module ++ ").\n") of
+ ok ->
+ TmpFile;
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not write file '~s': ~s\n", [TmpFile, R1]),
+ halt(2)
+ end.
diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript
new file mode 100755
index 0000000000..fcc3a96ada
--- /dev/null
+++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -0,0 +1,107 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%% Find all applications and all modules given a root directory.
+%%% Output an XML file that can be used for finding which application
+%%% a given module belongs to.
+%%%
+%%% Options:
+%%%
+%%% "-topdir <D>" Applications are found under D/lib/.
+%%% The default value is $ERL_TOP.
+%%%
+%%% "-outfile <F>" Output is written onto F.
+%%% The default value is "mod2app.xml".
+%%%
+%%% The output file has the following format:
+%%%
+%%% <?xml version="1.0"?>
+%%% <mod2app>
+%%% <module name="ModName1">AppName1</module>
+%%% ...
+%%% <mod2app>
+%%%
+%%% meaning that module ModName1 resides in application AppName1.
+
+main(Args) ->
+ case catch parse(Args, os:getenv("ERL_TOP"), "mod2app.xml") of
+ {ok, TopDir, OutFile} ->
+ case modapp(TopDir) of
+ [] ->
+ io:format("no applications found\n"),
+ halt(3);
+ MA ->
+ Layout = layout(MA),
+ XML = xmerl:export_simple(Layout, xmerl_xml),
+ write_file(XML, OutFile)
+ end;
+ {error, Msg} ->
+ io:format("~s\n", [Msg]),
+ usage()
+ end.
+
+parse(["-topdir", TopDir | Opts], _, OutFile) ->
+ parse(Opts, TopDir, OutFile);
+parse(["-outfile", OutFile | Opts], TopDir, _) ->
+ parse(Opts, TopDir, OutFile);
+parse([], TopDir, OutFile) ->
+ {ok, TopDir, OutFile};
+parse([Opt | _], _, _) ->
+ {error, io_lib:format("Bad option: ~p", [Opt])}.
+
+usage() ->
+ io:format("usage: ~s [-topdir <dir>] [-outfile <file>]\n",
+ [escript:script_name()]),
+ halt(1).
+
+modapp(TopDir) ->
+ AppDirs = filelib:wildcard(filename:join([TopDir,"lib","*"])),
+ AM = [appmods(D) || D <- AppDirs],
+ lists:keysort(1, [{M,A} || {A,Ms} <- AM, M <- Ms]).
+
+%% It's OK if too much data is generated as long as all applications
+%% and all modules are mentioned.
+appmods(D) ->
+ ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])),
+ AppV = filename:basename(D),
+ App = case string:rstr(AppV, "-") of
+ 0 -> AppV;
+ P -> string:sub_string(AppV, 1, P-1)
+ end,
+ {App, [filename:basename(EF, ".erl") || EF <- ErlFiles]}.
+
+-include_lib("xmerl/include/xmerl.hrl").
+
+-define(IND(N), lists:duplicate(N, $\s)).
+-define(NL, "\n").
+
+layout(MAL) ->
+ ML = lists:append([[?IND(2),{module,[{name,M}],[A]},?NL] || {M,A} <- MAL]),
+ [?NL,{mod2app,[?NL|ML]},?NL].
+
+write_file(Text, File) ->
+ case file:open(File, [write]) of
+ {ok, FD} ->
+ io:put_chars(FD, Text),
+ ok = file:close(FD);
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not write file '~s': ~s\n", [File, R1]),
+ halt(2)
+ end.
diff --git a/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd b/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
index 7b9974fbda..c1237766e1 100644
--- a/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
+++ b/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
@@ -26,15 +26,18 @@
<!ELEMENT description (%block;|quote|br|marker|warning|note)* >
<!ELEMENT funcs (func)+ >
-<!ELEMENT func (name+,fsummary,type?,desc?) >
+<!ELEMENT func (name+,type_desc+,fsummary,type?,desc?) >
<!-- ELEMENT name is defined in each ref dtd -->
<!ELEMENT fsummary (#PCDATA|c|em)* >
<!ELEMENT type (v,d?)+ >
<!ELEMENT v (#PCDATA) >
<!ELEMENT d (#PCDATA|c|em)* >
-<!ELEMENT desc (%block;|quote|br|marker|warning|note)* >
+<!ELEMENT desc (%block;|quote|br|marker|warning|note|anno)* >
<!ELEMENT authors (aname,email)+ >
<!ELEMENT aname (#PCDATA) >
<!ELEMENT email (#PCDATA) >
<!ELEMENT section (marker*,title,(%block;|quote|br|marker|
warning|note)*) >
+<!ELEMENT datatypes (datatype)+ >
+<!ELEMENT datatype (name+,desc?) >
+<!ELEMENT type_desc (#PCDATA) >
diff --git a/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd b/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
index 21656a1446..9905086ff4 100644
--- a/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
+++ b/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
@@ -22,7 +22,7 @@
%common.refs;
<!ELEMENT erlref (header,module,modulesummary,description,
- (section|funcs)*,authors?) >
+ (section|funcs|datatypes)*,authors?) >
<!ELEMENT module (#PCDATA) >
<!ELEMENT modulesummary (#PCDATA) >
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index bba0f97645..732560e303 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
@@ -17,15 +17,315 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
+ xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<xsl:include href="db_html_params.xsl"/>
+ <!-- Start of Dialyzer type/spec tags.
+ See also the template matching "name" and the template "menu.funcs"
+ -->
+
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:param name="mod2app_file" select="''"/>
+ <xsl:variable name="m2a" select="document($mod2app_file)"></xsl:variable>
+ <xsl:key name="mod2app" match="module" use="@name"/>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="arity_clause">
+ <xsl:choose>
+ <xsl:when test="string-length(@clause) > 0">
+ <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <a name="{$name}-{$arity_clause}"></a>
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span>
+ <br/>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <xsl:param name="guard"/>
+ <xsl:param name="type_desc"/>
+ <div class="REFBODY"><p>Types:</p>
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </div>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <div class="REFTYPES">
+ <span class="bold_code">
+ <xsl:apply-templates select="string"/>
+ </span>
+ </div>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <div class="REFBODY">
+ <xsl:apply-templates/>
+ </div>
+ </xsl:template>
+
+ <!-- This is for debugging. All modules! -->
+ <xsl:template match="all_etypes">
+ <xsl:for-each select= "$i//type">
+ <pre>
+ <span class="bold_code">
+ <xsl:apply-templates select="typedecl"/>
+ </span><xsl:text>
+</xsl:text>
+ </pre>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <h3>
+ <xsl:text>DATA TYPES</xsl:text>
+ </h3>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <p><xsl:apply-templates select="name"/></p>
+ <xsl:apply-templates select="desc"/>
+ </xsl:template>
+
+ <xsl:template match="typehead">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span><br/>
+ </xsl:template>
+
+ <!-- local_defs -->
+ <xsl:template match="local_defs">
+ <div class="REFBODY">
+ <xsl:apply-templates>
+ </xsl:apply-templates>
+ </div>
+ </xsl:template>
+
+ <xsl:template match="local_def">
+ <div class="REFTYPES">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span>
+ </div>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <span class="bold_code">
+ <xsl:value-of select="."/>
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text>&#160;</xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
+
<!-- Page layout -->
<xsl:template name="pagelayout">
<xsl:param name="chapnum"/>
@@ -36,19 +336,19 @@
<title>Erlang -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<div id="container">
<script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/>
<script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script>
<script language="JavaScript" type="text/javascript">
<xsl:text disable-output-escaping="yes"><![CDATA[
- <!--
+ <!--
function getWinHeight() {
var myHeight = 0;
if( typeof( window.innerHeight ) == 'number' ) {
//Non-IE
myHeight = window.innerHeight;
- } else if( document.documentElement && ( document.documentElement.clientWidth ||
+ } else if( document.documentElement && ( document.documentElement.clientWidth ||
document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myHeight = document.documentElement.clientHeight;
@@ -56,7 +356,7 @@
//IE 4 compatible
myHeight = document.body.clientHeight;
}
- return myHeight;
+ return myHeight;
}
function setscrollpos() {
@@ -64,16 +364,16 @@
document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2;
}
- function addEvent(obj, evType, fn){
- if (obj.addEventListener){
- obj.addEventListener(evType, fn, true);
- return true;
- } else if (obj.attachEvent){
- var r = obj.attachEvent("on"+evType, fn);
- return r;
- } else {
- return false;
- }
+ function addEvent(obj, evType, fn){
+ if (obj.addEventListener){
+ obj.addEventListener(evType, fn, true);
+ return true;
+ } else if (obj.attachEvent){
+ var r = obj.attachEvent("on"+evType, fn);
+ return r;
+ } else {
+ return false;
+ }
}
addEvent(window, 'load', setscrollpos);
@@ -85,7 +385,7 @@
<xsl:with-param name="chapnum" select="$chapnum"/>
<xsl:with-param name="curModule" select="$curModule"/>
</xsl:call-template>
-
+
<div id="content">
<div class="innertube">
@@ -124,17 +424,17 @@
<xsl:if test="$lname = 'releasenotes'">
<!-- .../part -->
<xsl:call-template name="releasenotes.content" />
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'part'">
<!-- .../part -->
<xsl:call-template name="part.content" />
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'chapter'">
<!-- .../part/chapter -->
<xsl:call-template name="chapter.content">
<xsl:with-param name="chapnum" select="$chapnum"/>
</xsl:call-template>
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'application'">
<!-- .../application -->
<xsl:call-template name="app.content" />
@@ -178,37 +478,37 @@
<small>
<xsl:if test="boolean(/book/parts/part)">
<a href="users_guide.html">User's Guide</a><br/>
- </xsl:if>
+ </xsl:if>
<xsl:if test="boolean(/book/applications)">
<a href="index.html">Reference Manual</a><br/>
- </xsl:if>
+ </xsl:if>
<xsl:if test="boolean(/book/releasenotes)">
<a href="release_notes.html">Release Notes</a><br/>
- </xsl:if>
+ </xsl:if>
<a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a><br/>
<a href="{$topdocdir}/index.html">Top</a>
</small>
</xsl:template>
-
+
<xsl:template name="menu_middle">
<!-- small>
<xsl:choose>
<xsl:when test="ancestor::parts">
<a href="users_guide_bibliography.html">Bibliography</a><br/>
<a href="users_guide_glossary.html">Glossary</a><br/>
- </xsl:when>
- <xsl:when test="ancestor::applications">
+ </xsl:when>
+ <xsl:when test="ancestor::applications">
<a href="ref_man_bibliography.html">Bibliography</a><br/>
<a href="ref_man_glossary.html">Glossary</a><br/>
- </xsl:when>
+ </xsl:when>
</xsl:choose>
</small -->
<br/>
<a href="javascript:openAllFlips()">Expand All</a><br/>
<a href="javascript:closeAllFlips()">Contract All</a>
- </xsl:template>
-
+ </xsl:template>
+
<!-- Book -->
<xsl:template match="/book">
@@ -243,7 +543,7 @@
<!-- Chapter/Section -->
<xsl:template match="chapter/section">
- <xsl:param name="chapnum"/>
+ <xsl:param name="chapnum"/>
<h3>
<a name="{generate-id(title)}">
<xsl:value-of select="$chapnum"/>.<xsl:number/>&#160;
@@ -302,7 +602,7 @@
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:param name="chapnum"/>
<ul>
@@ -330,7 +630,7 @@
</xsl:apply-templates>
</dl>
</xsl:template>
-
+
<xsl:template match="taglist/tag">
<xsl:param name="chapnum"/>
<dt>
@@ -377,7 +677,7 @@
</xsl:apply-templates>
</p>
</div>
- </div>
+ </div>
</xsl:template>
<!-- Paragraph -->
@@ -402,7 +702,7 @@
</xsl:template>
<xsl:template match="em">
- <strong><xsl:apply-templates/></strong>
+ <strong><xsl:apply-templates/></strong>
</xsl:template>
<!-- Code -->
@@ -507,7 +807,7 @@
<!-- Part -->
<xsl:template match="part">
<!-- Generate Glossary for Users Guide -->
- <!--xsl:call-template name="glossary">
+ <!--xsl:call-template name="glossary">
<xsl:with-param name="type">users_guide</xsl:with-param>
</xsl:call-template-->
@@ -530,9 +830,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="chapter"/>
-
+
</xsl:template>
<!-- Menu.ug -->
@@ -565,10 +865,10 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
-
-
+
+
<xsl:template name="menu.chapter">
<xsl:param name="entries"/>
<xsl:param name="chapnum"/>
@@ -596,7 +896,7 @@
<a href="{$chapter_file}.html">
Top of chapter
</a>
- </li>
+ </li>
<xsl:call-template name="menu.section">
<xsl:with-param name="entries"
select="section[title]"/>
@@ -623,7 +923,7 @@
<!-- Chapter (if top tag)-->
<xsl:template match="/chapter">
- <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
+ <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
<xsl:call-template name="pagelayout">
@@ -635,7 +935,7 @@
<!-- Chapter -->
<xsl:template match="chapter">
- <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
+ <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
<xsl:call-template name="pagelayout">
@@ -670,7 +970,7 @@
<xsl:template match="application">
<!-- Generate Glossary for Ref. Manual -->
- <!--xsl:call-template name="glossary">
+ <!--xsl:call-template name="glossary">
<xsl:with-param name="type">ref_man</xsl:with-param>
</xsl:call-template-->
@@ -678,7 +978,7 @@
<!--xsl:call-template name="bibliography">
<xsl:with-param name="type">ref_man</xsl:with-param>
</xsl:call-template-->
-
+
<xsl:document href="{$outdir}/index.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
@@ -695,9 +995,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="erlref|cref|comref|fileref|appref"/>
-
+
</xsl:template>
<!-- Menu.ref -->
@@ -730,16 +1030,16 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
-
-
+
+
<xsl:template name="menu.ref2">
<xsl:param name="entries"/>
<!--xsl:param name="genFuncMenu"/-->
<xsl:param name="curModule"/>
<xsl:for-each select="$entries">
-
+
<xsl:variable name="cval">
<xsl:choose>
<xsl:when test="local-name() = 'erlref'">
@@ -767,9 +1067,9 @@
<xsl:when test="local-name() = 'fileref'">false</xsl:when>
<xsl:when test="descendant::funcs">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:variable>
-
+
<xsl:variable name="expanded">
<xsl:choose>
<xsl:when test="$curModule = $cval">true</xsl:when>
@@ -796,7 +1096,7 @@
<a href="{$link_cval}.html">
Top of manual page
</a>
- </li>
+ </li>
<xsl:call-template name="menu.funcs">
<xsl:with-param name="entries"
select="funcs/func/name"/>
@@ -823,7 +1123,7 @@
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:for-each>
</xsl:template>
@@ -831,7 +1131,7 @@
<xsl:template name="menu.funcs">
<xsl:param name="entries"/>
<xsl:param name="basename"/>
-
+
<xsl:for-each select="$entries">
<xsl:choose>
@@ -840,74 +1140,97 @@
<xsl:choose>
<xsl:when test="string-length($fname) > 0">
<li title="{$fname}">
- <a href="{$basename}.html#{$fname}">
+ <a href="{$basename}.html#{$fname}">
<xsl:value-of select="$fname"/>()
</a>
- </li>
+ </li>
</xsl:when>
<xsl:otherwise>
<li title="{name/nametext}">
- <a href="{$basename}.html#{name/nametext}">
+ <a href="{$basename}.html#{name/nametext}">
<xsl:value-of select="nametext"/>()
- </a>
- </li>
+ </a>
+ </li>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:when>
-
+
<xsl:when test="ancestor::erlref">
-
+
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="arity">
- <xsl:call-template name="calc-arity">
- <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
- </xsl:call-template>
- </xsl:variable>
-
+ <xsl:choose>
+ <xsl:when test="string-length(@arity) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:choose>
+ <xsl:when test="string-length(@clause) > 0">
+ <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="calc-arity">
+ <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
<xsl:variable name="fname">
- <xsl:variable name="fname1">
- <xsl:value-of select="substring-before(., '(')"/>
- </xsl:variable>
- <xsl:variable name="fname2">
- <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
- </xsl:variable>
<xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
- <xsl:value-of select="$fname2"/>
+ <xsl:when test="string-length(@name) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@name"/>
</xsl:when>
<xsl:otherwise>
- <xsl:value-of select="$fname1"/>
+ <xsl:variable name="fname1">
+ <xsl:value-of select="substring-before(., '(')"/>
+ </xsl:variable>
+ <xsl:variable name="fname2">
+ <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="string-length($fname2) > 0">
+ <xsl:value-of select="$fname2"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fname1"/>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
-
+
<li title="{$fname}-{$arity}">
- <a href="{$basename}.html#{$fname}-{$arity}">
+ <a href="{$basename}.html#{$fname}-{$arity}">
<xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/>
</a>
- </li>
+ </li>
</xsl:when>
</xsl:choose>
-
+
</xsl:for-each>
</xsl:template>
@@ -1148,7 +1471,7 @@
<!-- Func -->
<xsl:template match="func">
<xsl:param name="partnum"/>
-
+
<p><xsl:apply-templates select="name"/></p>
<xsl:apply-templates select="fsummary|type|desc">
@@ -1159,33 +1482,48 @@
<xsl:template match="name">
+ <xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="arity">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
</xsl:call-template>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:choose>
<xsl:when test="ancestor::cref">
<a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/>
@@ -1199,7 +1537,7 @@
<xsl:value-of select="substring-after($fname1, 'erlang:')"/>
</xsl:variable>
<xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
+ <xsl:when test="string-length($fname2) > 0">
<xsl:value-of select="$fname2"/>
</xsl:when>
<xsl:otherwise>
@@ -1213,21 +1551,20 @@
<span class="bold_code"><xsl:value-of select="."/></span>
</xsl:otherwise>
</xsl:choose>
-
- </xsl:template>
+ </xsl:template>
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
- <div class="REFBODY"><p>Types:</p>
+ <div class="REFBODY"><p>Types:</p>
<xsl:apply-templates>
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
</div>
-
+
</xsl:template>
@@ -1286,16 +1623,37 @@
<xsl:variable name="modulepart"><xsl:value-of select="substring-before($filepart, ':')"/></xsl:variable>
<xsl:choose>
<xsl:when test="string-length($modulepart) > 0">
- <xsl:variable name="filepart1"><xsl:value-of select="substring-after($filepart, ':')"/></xsl:variable>
+ <xsl:variable name="filepart1"><xsl:value-of select="substring-after($filepart, ':')"/></xsl:variable>
<span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$modulepart}','{$filepart1}.html#{$linkpart}');"><xsl:apply-templates/></a></span>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
+ <!-- Dialyzer seealso (the application is unknown) -->
+ <xsl:when test="string-length($specs_file) > 0
+ and count($i/specs/module[@name=$filepart]) = 0">
+ <!-- Deemed to slow; use key() instead
+ <xsl:variable name="app"
+ select="$m2a/mod2app/module[@name=$filepart]"/>
+ -->
+ <xsl:variable name="reftext" select="text()"/>
+ <xsl:for-each select="$m2a">
+ <xsl:variable name="app" select="key('mod2app', $filepart)"/>
+ <xsl:choose>
+ <xsl:when test="string-length($app) > 0">
+ <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html');"><xsl:value-of select="$reftext"/></a></span>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Unknown application; no link -->
+ <xsl:value-of select="$reftext"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </xsl:when>
<xsl:when test="string-length($linkpart) > 0">
<span class="bold_code"><a href="{$filepart}.html#{$linkpart}"><xsl:apply-templates/></a></span>
</xsl:when>
- <xsl:otherwise>
- <span class="bold_code"><a href="{$filepart}.html"><xsl:apply-templates/></a></span>
+ <xsl:otherwise>
+ <span class="bold_code"><a href="{$filepart}.html"><xsl:apply-templates/></a></span>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
@@ -1308,16 +1666,16 @@
</xsl:when>
<xsl:otherwise>
<xsl:variable name="modulepart"><xsl:value-of select="substring-before(@marker, ':')"/></xsl:variable>
-
+
<xsl:choose>
<xsl:when test="string-length($modulepart) > 0">
- <xsl:variable name="filepart1"><xsl:value-of select="substring-after(@marker, ':')"/></xsl:variable>
+ <xsl:variable name="filepart1"><xsl:value-of select="substring-after(@marker, ':')"/></xsl:variable>
<span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$modulepart}','{$filepart1}.html');"><xsl:apply-templates/></a></span>
</xsl:when>
<xsl:otherwise>
- <span class="bold_code"><a href="{@marker}.html"><xsl:apply-templates/></a></span>
+ <span class="bold_code"><a href="{@marker}.html"><xsl:apply-templates/></a></span>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
@@ -1342,16 +1700,16 @@
<xsl:choose>
<xsl:when test="ancestor::parts">
<a href="users_guide_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
- <xsl:when test="ancestor::applications">
+ </xsl:when>
+ <xsl:when test="ancestor::applications">
<a href="ref_man_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
+ </xsl:when>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<a href="{$topdocdir}/glossary.html#{@id}"><xsl:value-of select="@id"/></a>
</xsl:otherwise>
- </xsl:choose -->
+ </xsl:choose -->
</xsl:template>
<xsl:template match="cite">
@@ -1375,9 +1733,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="chapter"/>
-
+
</xsl:template>
<!-- Menu.rn -->
@@ -1410,7 +1768,7 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
<!-- Glossary -->
@@ -1423,14 +1781,14 @@
<title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<div id="container">
<script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/>
<script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script>
<!-- Generate menu -->
<xsl:call-template name="menu"/>
-
+
<div id="content">
<div class="innertube">
<h1>Glossary</h1>
@@ -1478,14 +1836,14 @@
<title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<div id="container">
<script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/>
<script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script>
<!-- Generate menu -->
<xsl:call-template name="menu"/>
-
+
<div id="content">
<div class="innertube">
<h1>Bibliography</h1>
@@ -1498,8 +1856,8 @@
<tr>
<td><xsl:value-of select="@id"/></td>
<td><xsl:value-of select="citedef"/></td>
- </tr>
- </xsl:if>
+ </tr>
+ </xsl:if>
</xsl:for-each>
</table>
@@ -1529,7 +1887,7 @@
<xsl:template name="calc-arity">
<xsl:param name="string"/>
<xsl:param name="no-of-pars"/>
-
+
<xsl:variable name="length">
<xsl:value-of select="string-length($string)"/>
</xsl:variable>
@@ -1538,8 +1896,8 @@
<xsl:when test="$length > 0">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-after($string, ',')"/>
- <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
- </xsl:call-template>
+ <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$no-of-pars"/>
@@ -1554,9 +1912,9 @@
<xsl:variable name="str1">
<xsl:call-template name="remove-paren-1">
<xsl:with-param name="string" select="$string"/>
- <xsl:with-param name="start">(</xsl:with-param>
- <xsl:with-param name="end">)</xsl:with-param>
- </xsl:call-template>
+ <xsl:with-param name="start">(</xsl:with-param>
+ <xsl:with-param name="end">)</xsl:with-param>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str2">
@@ -1564,7 +1922,7 @@
<xsl:with-param name="string" select="$str1"/>
<xsl:with-param name="start">{</xsl:with-param>
<xsl:with-param name="end">}</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str3">
@@ -1572,7 +1930,7 @@
<xsl:with-param name="string" select="$str2"/>
<xsl:with-param name="start">[</xsl:with-param>
<xsl:with-param name="end">]</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="$str3"/>
@@ -1584,7 +1942,7 @@
<xsl:param name="string"/>
<xsl:param name="start"/>
<xsl:param name="end"/>
-
+
<xsl:variable name="tmp1">
<xsl:value-of select="substring-before($string, $start)"/>
</xsl:variable>
@@ -1597,7 +1955,7 @@
<xsl:variable name="retstring">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmp2"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(concat($tmp1, 'x'), $retstring)"/>
</xsl:when>
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index 71c4a66707..2ed0d95c6d 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
@@ -17,24 +17,294 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:preserve-space elements="code pre"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text" encoding="UTF-8" indent="no"/>
+ <!-- Start of Dialyzer type/spec tags. See also the template matching "name"
+ -->
+
+ <!-- Note: specs data for *one* module (as opposed to html and pdf) -->
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <xsl:param name="guard"/>
+ <xsl:param name="type_desc"/>
+ <xsl:text>&#10;.RS</xsl:text>
+ <xsl:text>&#10;.TP</xsl:text>
+ <xsl:text>&#10;Types</xsl:text>
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ <xsl:text>&#10;.RE</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="string"/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <xsl:text>&#10;</xsl:text><xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <xsl:text>&#10;.SH DATA TYPES</xsl:text>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="typehead">
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="local_defs">
+ <xsl:text>&#10;.RS</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.RE</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="local_def">
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text> </xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
+
<!-- Header -->
<xsl:template match="header">
</xsl:template>
-
+
<!-- Section/Title -->
<xsl:template match="section/title">
</xsl:template>
-
+
<!-- *ref/Section -->
<xsl:template match="erlref/section|comref/section|cref/section|fileref/section|appref/section">
<xsl:text>&#10;.SH "</xsl:text><xsl:value-of select="translate(title, 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:text>"&#10;</xsl:text>
@@ -49,7 +319,7 @@
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:text>&#10;.RS 2</xsl:text>
<xsl:apply-templates/>
@@ -68,7 +338,7 @@
<xsl:apply-templates select="tag|item"/>
<xsl:text>&#10;.RE</xsl:text>
</xsl:template>
-
+
<xsl:template match="taglist/tag">
<xsl:text>&#10;.TP 2&#10;</xsl:text>
<xsl:text>.B&#10;</xsl:text>
@@ -76,7 +346,7 @@
</xsl:template>
<xsl:template match="taglist/item">
- <xsl:apply-templates/>
+ <xsl:apply-templates/>
</xsl:template>
<xsl:template match="item/p">
@@ -88,10 +358,10 @@
<xsl:value-of select="$content"/>
</xsl:when>
<xsl:otherwise>
- <xsl:text>&#10;.RS 2</xsl:text>
- <xsl:text>&#10;.LP&#10;&#10;.LP&#10;</xsl:text>
+ <xsl:text>&#10;.RS 2</xsl:text>
+ <xsl:text>&#10;.LP&#10;&#10;.LP&#10;</xsl:text>
<xsl:value-of select="$content"/>
- <xsl:text>&#10;.RE</xsl:text>
+ <xsl:text>&#10;.RE</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
@@ -171,7 +441,7 @@
<xsl:template match="application">
<xsl:apply-templates/>
</xsl:template>
-
+
<!-- Erlref -->
<xsl:template match="/erlref">
<xsl:variable name="companyname">
@@ -184,7 +454,7 @@
<xsl:text>.TH </xsl:text><xsl:value-of select="module"/><xsl:text> 3 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Erlang Module Definition"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="module"/><xsl:text> \- </xsl:text><xsl:value-of select="modulesummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="module"/><xsl:text> \- </xsl:text><xsl:value-of select="modulesummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -199,7 +469,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="com"/><xsl:text> 1 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "User Commands"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="com"/><xsl:text> \- </xsl:text><xsl:value-of select="comsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="com"/><xsl:text> \- </xsl:text><xsl:value-of select="comsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -214,7 +484,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="lib"/><xsl:text> 3 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "C Library Functions"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="lib"/><xsl:text> \- </xsl:text><xsl:value-of select="libsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="lib"/><xsl:text> \- </xsl:text><xsl:value-of select="libsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -229,7 +499,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="file"/><xsl:text> 5 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Files"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="file"/><xsl:text> \- </xsl:text><xsl:value-of select="filesummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="file"/><xsl:text> \- </xsl:text><xsl:value-of select="filesummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -244,7 +514,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="app"/><xsl:text> 7 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Erlang Application Definition"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="app"/><xsl:text> \- </xsl:text><xsl:value-of select="appsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="app"/><xsl:text> \- </xsl:text><xsl:value-of select="appsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -271,10 +541,26 @@
<!-- Func -->
<xsl:template match="func">
<xsl:text>&#10;.LP</xsl:text>
- <xsl:apply-templates/>
+ <xsl:apply-templates select="name"/>
+ <xsl:apply-templates select="fsummary|type|desc"/>
</xsl:template>
<xsl:template match="name">
+ <xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
<xsl:text>&#10;.B&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;.br</xsl:text>
@@ -296,7 +582,7 @@
<xsl:text>&#10;</xsl:text><xsl:value-of select="normalize-space(text())"/>
<xsl:text>&#10;.br</xsl:text>
</xsl:template>
-
+
<!-- D -->
<xsl:template match="d">
<xsl:text>&#10;</xsl:text><xsl:apply-templates/>
@@ -316,7 +602,7 @@
<!-- This tag is skipped for now. -->
</xsl:template>
-
+
<!-- Authors -->
<xsl:template match="authors">
<xsl:text>&#10;.SH AUTHORS</xsl:text>
@@ -336,21 +622,11 @@
<xsl:text>&gt;</xsl:text>
</xsl:template>
- <!-- Do not noramlize any text within pre and code tags. -->
- <xsl:template match="pre/text()">
- <xsl:value-of select="."/>
- </xsl:template>
-
- <xsl:template match="code/text()">
- <xsl:value-of select="."/>
- </xsl:template>
-
-
<!-- Replace ' by \&' ans . by \&. -->
<xsl:template match="text()">
<xsl:variable name="startstring">
<xsl:value-of select="normalize-space()"/><xsl:text> </xsl:text>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="rep1">
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$startstring" />
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index e12b4d219a..1e80c360b8 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
@@ -17,7 +17,7 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
@@ -27,16 +27,310 @@
<xsl:include href="db_pdf_params.xsl"/>
+ <!-- Start of Dialyzer type/spec tags.
+ See also the template matching "name" and the template "bookmarks6"
+ -->
+
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <fo:block id="{generate-id()}">
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </fo:block>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates/>
+ </fo:block>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <fo:block>
+ <xsl:text>Types:</xsl:text>
+ </fo:block>
+ <fo:list-block xsl:use-attribute-sets="type-listblock">
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </fo:list-block>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>
+ </fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block font-weight="bold">
+ <xsl:apply-templates select="string"/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()"><fo:block></fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block>
+ <xsl:apply-templates/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <fo:block xsl:use-attribute-sets="h3">
+ <xsl:text>Data Types</xsl:text>
+ </fo:block>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates select="name"/>
+ </fo:block>
+ <xsl:apply-templates select="desc"/>
+ </xsl:template>
+
+ <!-- Like <head>... -->
+ <xsl:template match="typehead">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates/>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Like <guard>, except "Types:"... -->
+ <xsl:template match="local_defs">
+ <fo:list-block xsl:use-attribute-sets="type-listblock">
+ <xsl:apply-templates/>
+ </fo:list-block>
+ </xsl:template>
+
+ <!-- Like <subtype>... -->
+ <xsl:template match="local_def">
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>
+ </fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block font-weight="bold">
+ <xsl:apply-templates/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <fo:inline font-weight="bold" xsl:use-attribute-sets="type-listitem">
+ <xsl:value-of select="."/>
+ </fo:inline>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text>&#160;</xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
<xsl:template match="/">
<xsl:apply-templates select="book"/>
</xsl:template>
-
+
<xsl:template match="book">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
- <!-- Master pages -->
+ <!-- Master pages -->
<fo:layout-master-set>
<fo:simple-page-master
master-name="cover"
@@ -47,7 +341,7 @@
<xsl:attribute name="page-width">
<xsl:value-of select="$page-width"/>
</xsl:attribute>
- <fo:region-body
+ <fo:region-body
margin="0mm"/>
</fo:simple-page-master>
@@ -63,7 +357,7 @@
<xsl:attribute name="page-width">
<xsl:value-of select="$page-width"/>
</xsl:attribute>
- <fo:region-body
+ <fo:region-body
margin-top="15mm"
margin-bottom="20mm"/>
<fo:region-before
@@ -100,10 +394,10 @@
<fo:page-sequence-master master-name="document">
<fo:repeatable-page-master-alternatives>
- <fo:conditional-page-master-reference
+ <fo:conditional-page-master-reference
master-reference="left-page"
odd-or-even="even"/>
- <fo:conditional-page-master-reference
+ <fo:conditional-page-master-reference
master-reference="right-page"
odd-or-even="odd"/>
</fo:repeatable-page-master-alternatives>
@@ -166,7 +460,7 @@
<fo:flow flow-name="xsl-region-body">
<fo:block>
-
+
</fo:block>
<xsl:apply-templates select="parts"/>
@@ -189,7 +483,7 @@
<!-- Cover page -->
<xsl:template match="header/title">
- <fo:page-sequence
+ <fo:page-sequence
font-family="sans-serif"
force-page-count="even"
master-reference="cover">
@@ -242,7 +536,7 @@
the License for the specific language governing rights and limitations
under the License.
- The Initial Developer of the Original Code is
+ The Initial Developer of the Original Code is
-->
<xsl:value-of select="$companyname"/>.
</fo:block>
@@ -281,22 +575,22 @@
<xsl:template name="bookmarks1">
<xsl:param name="entries"/>
<xsl:if test="$entries != ''">
-
+
<fo:bookmark internal-destination="{generate-id(/book/parts/part)}"
starting-state="hide">
<fo:bookmark-title>User's Guide</fo:bookmark-title>
-
+
<xsl:for-each select="$entries">
<xsl:call-template name="bookmarks2">
<xsl:with-param name="entries"
select="chapter[header/title]"/>
</xsl:call-template>
</xsl:for-each>
-
+
</fo:bookmark>
</xsl:if>
</xsl:template>
-
+
<xsl:template name="bookmarks2">
<xsl:param name="entries"/>
<xsl:for-each select="$entries">
@@ -341,7 +635,7 @@
starting-state="hide">
<fo:bookmark-title>Reference Manual</fo:bookmark-title>
<xsl:for-each select="$entries">
-
+
<xsl:call-template name="bookmarks5">
<xsl:with-param name="entries"
select="erlref[module]|comref[com]|cref[lib]|fileref[file]|appref[app]"/>
@@ -387,7 +681,7 @@
<fo:bookmark internal-destination="{generate-id(nametext)}" starting-state="hide">
<xsl:variable name="fname">
<xsl:value-of select="substring-before(nametext, '(')"/>
- </xsl:variable>
+ </xsl:variable>
<fo:bookmark-title>
<xsl:choose>
<xsl:when test="string-length($fname) > 0">
@@ -396,7 +690,7 @@
<xsl:otherwise>
<xsl:value-of select="nametext"/>()
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</fo:bookmark-title>
</fo:bookmark>
</xsl:when>
@@ -404,60 +698,76 @@
<fo:bookmark internal-destination="{generate-id(.)}" starting-state="hide">
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="arity">
- <xsl:call-template name="calc-arity">
- <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
- </xsl:call-template>
- </xsl:variable>
-
- <xsl:variable name="fname">
- <xsl:variable name="fname1">
- <xsl:value-of select="substring-before(., '(')"/>
- </xsl:variable>
- <xsl:variable name="fname2">
- <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
- </xsl:variable>
- <xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
- <xsl:value-of select="$fname2"/>
- </xsl:when>
+ <xsl:choose>
+ <xsl:when test="string-length(@arity) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@arity"/>
+ </xsl:when>
<xsl:otherwise>
- <xsl:value-of select="$fname1"/>
+ <xsl:call-template name="calc-arity">
+ <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
+ <xsl:variable name="fname">
+ <xsl:choose>
+ <xsl:when test="string-length(@name) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="fname1">
+ <xsl:value-of select="substring-before(., '(')"/>
+ </xsl:variable>
+ <xsl:variable name="fname2">
+ <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="string-length($fname2) > 0">
+ <xsl:value-of select="$fname2"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fname1"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
<fo:bookmark-title>
<xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/>
</fo:bookmark-title>
</fo:bookmark>
</xsl:when>
</xsl:choose>
-
+
</xsl:for-each>
</xsl:template>
<!-- UG part -->
-
+
<!-- Parts -->
<xsl:template match="parts">
<xsl:apply-templates select="part"/>
@@ -491,7 +801,7 @@
<xsl:value-of select="$partnum"/>.<xsl:number/>&#160;&#160;<xsl:value-of select="header/title"/>
</fo:marker>
<xsl:value-of select="$partnum"/>.<xsl:number/>&#160;&#160;<xsl:value-of select="header/title"/>
-
+
</fo:block>
<xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude|erleval">
@@ -567,7 +877,7 @@
</xsl:template>
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:param name="partnum"/>
<fo:list-block xsl:use-attribute-sets="listblock">
@@ -692,7 +1002,7 @@
</xsl:variable>
<fo:block xsl:use-attribute-sets="code">
- <xsl:apply-templates select="text()"/>
+ <xsl:apply-templates select="text()"/>
</fo:block>
<xsl:if test="@caption">
@@ -711,7 +1021,7 @@
</xsl:variable>
<fo:block xsl:use-attribute-sets="code">
- <xsl:apply-templates/>
+ <xsl:apply-templates/>
</fo:block>
<xsl:if test="@caption">
@@ -734,23 +1044,23 @@
<xsl:variable name="partnum">
<xsl:number level="any" from="book" count="part|application"/>
</xsl:variable>
-
- <fo:block xsl:use-attribute-sets="h1" id="{generate-id()}">
+
+ <fo:block xsl:use-attribute-sets="h1" id="{generate-id()}">
<xsl:if test="/book/header/title">
<xsl:value-of select="$partnum"/>&#160;&#160;&#160;
<xsl:text>Reference Manual</xsl:text>
- </xsl:if>
+ </xsl:if>
</fo:block>
-
-
+
+
<xsl:apply-templates select="description">
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
-
+
<xsl:apply-templates select="erlref|comref|cref|fileref|appref">
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
-
+
</xsl:template>
<!-- Erlref -->
@@ -763,7 +1073,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="module"/>
</fo:marker>
- <xsl:value-of select="module"/>
+ <xsl:value-of select="module"/>
</fo:block>
<xsl:text>Erlang module</xsl:text>
</fo:block>
@@ -784,7 +1094,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="com"/>
</fo:marker>
- <xsl:value-of select="com"/>
+ <xsl:value-of select="com"/>
</fo:block>
<xsl:text>Command</xsl:text>
</fo:block>
@@ -805,7 +1115,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="lib"/>
</fo:marker>
- <xsl:value-of select="lib"/>
+ <xsl:value-of select="lib"/>
</fo:block>
<xsl:text>C Library</xsl:text>
</fo:block>
@@ -826,7 +1136,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="file"/>
</fo:marker>
- <xsl:value-of select="file"/>
+ <xsl:value-of select="file"/>
</fo:block>
<xsl:text>Name</xsl:text>
</fo:block>
@@ -847,7 +1157,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="app"/>
</fo:marker>
- <xsl:value-of select="app"/>
+ <xsl:value-of select="app"/>
</fo:block>
<xsl:text>Application</xsl:text>
</fo:block>
@@ -900,9 +1210,7 @@
<xsl:template match="func">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="function-name">
- <xsl:apply-templates select="name"/>
- </fo:block>
+ <xsl:apply-templates select="name"/>
<xsl:apply-templates select="fsummary|type|desc">
<xsl:with-param name="partnum" select="$partnum"/>
@@ -914,15 +1222,35 @@
<xsl:template match="name">
<xsl:param name="partnum"/>
<xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:call-template name="name">
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:call-template>
+ </fo:block>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
+ <xsl:param name="partnum"/>
+ <xsl:choose>
<xsl:when test="ancestor::cref">
<fo:block id="{generate-id(nametext)}">
- <xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/>
- </fo:block>
+ <xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/>
+ </fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block id="{generate-id(.)}">
- <xsl:value-of select="."/>
- </fo:block>
+ <xsl:value-of select="."/>
+ </fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
@@ -931,9 +1259,9 @@
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
-
+
<fo:block>
- <xsl:text>Types:</xsl:text>
+ <xsl:text>Types:</xsl:text>
</fo:block>
<fo:list-block xsl:use-attribute-sets="type-listblock">
@@ -1001,9 +1329,9 @@
<xsl:param name="chapnum"/>
<xsl:variable name="tabnum">
<xsl:number level="any" from="chapter" count="table"/>
- </xsl:variable>
+ </xsl:variable>
<fo:table xsl:use-attribute-sets="table">
- <fo:table-body>
+ <fo:table-body>
<xsl:apply-templates select="row">
<xsl:with-param name="chapnum" select="$chapnum"/>
<xsl:with-param name="tabnum" select="$tabnum"/>
@@ -1107,7 +1435,7 @@
<xsl:template name="calc-arity">
<xsl:param name="string"/>
<xsl:param name="no-of-pars"/>
-
+
<xsl:variable name="length">
<xsl:value-of select="string-length($string)"/>
</xsl:variable>
@@ -1116,8 +1444,8 @@
<xsl:when test="$length > 0">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-after($string, ',')"/>
- <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
- </xsl:call-template>
+ <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$no-of-pars"/>
@@ -1131,9 +1459,9 @@
<xsl:variable name="str1">
<xsl:call-template name="remove-paren-1">
<xsl:with-param name="string" select="$string"/>
- <xsl:with-param name="start">(</xsl:with-param>
- <xsl:with-param name="end">)</xsl:with-param>
- </xsl:call-template>
+ <xsl:with-param name="start">(</xsl:with-param>
+ <xsl:with-param name="end">)</xsl:with-param>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str2">
@@ -1141,7 +1469,7 @@
<xsl:with-param name="string" select="$str1"/>
<xsl:with-param name="start">{</xsl:with-param>
<xsl:with-param name="end">}</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str3">
@@ -1149,7 +1477,7 @@
<xsl:with-param name="string" select="$str2"/>
<xsl:with-param name="start">[</xsl:with-param>
<xsl:with-param name="end">]</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="$str3"/>
@@ -1161,7 +1489,7 @@
<xsl:param name="string"/>
<xsl:param name="start"/>
<xsl:param name="end"/>
-
+
<xsl:variable name="tmp1">
<xsl:value-of select="substring-before($string, $start)"/>
</xsl:variable>
@@ -1174,7 +1502,7 @@
<xsl:variable name="retstring">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmp2"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(concat($tmp1, 'x'), $retstring)"/>
</xsl:when>
diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile
new file mode 100644
index 0000000000..8e81bccd59
--- /dev/null
+++ b/lib/erl_docgen/src/Makefile
@@ -0,0 +1,96 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(ERL_DOCGEN_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/erl_docgen-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES = \
+ otp_specs
+
+HRL_FILES =
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+APP_FILE = erl_docgen.app
+
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_FILE = erl_docgen.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -I../../xmerl/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src
new file mode 100644
index 0000000000..1720464b6d
--- /dev/null
+++ b/lib/erl_docgen/src/erl_docgen.app.src
@@ -0,0 +1,12 @@
+{application, erl_docgen,
+ [{description, "Misc tools for building documentation"},
+ {vsn, "%VSN%"},
+ {modules, [otp_specs
+ ]
+ },
+ {registered,[]},
+ {applications, [kernel,stdlib]},
+ {env, []
+ }
+ ]
+}.
diff --git a/lib/erl_docgen/src/erl_docgen.appup.src b/lib/erl_docgen/src/erl_docgen.appup.src
new file mode 100644
index 0000000000..54a63833e6
--- /dev/null
+++ b/lib/erl_docgen/src/erl_docgen.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/erl_docgen/src/otp_specs.erl b/lib/erl_docgen/src/otp_specs.erl
new file mode 100644
index 0000000000..728ddb2e6e
--- /dev/null
+++ b/lib/erl_docgen/src/otp_specs.erl
@@ -0,0 +1,701 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(otp_specs).
+
+-export([module/2, package/2, overview/2, type/1]).
+
+-include("xmerl.hrl").
+
+-define(XML_EXPORT, xmerl_xml).
+-define(DEFAULT_XML_EXPORT, ?XML_EXPORT).
+-define(DEFAULT_PP, erl_pp).
+-define(IND(N), #xmlText{value="\n" ++ lists:duplicate(N, $\s)}).
+-define(NL, "\n").
+
+module(Element, Options) ->
+ XML = layout_module(Element, init_opts(Options)),
+ Export = proplists:get_value(xml_export, Options,
+ ?DEFAULT_XML_EXPORT),
+ xmerl:export_simple(XML, Export, [#xmlAttribute{name=prolog,
+ value=""}]).
+
+-record(opts, {pretty_print, file_suffix}).
+
+init_opts(Options) ->
+ #opts{pretty_print = proplists:get_value(pretty_print,
+ Options, ?DEFAULT_PP),
+ %% It *is* depending on edoc.hrl!
+ file_suffix = proplists:get_value(file_suffix, Options, ".html")}.
+
+layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
+ Name = get_attrval(name, E),
+ Functions = [{function_name(Elem), Elem} ||
+ Elem <- get_content(functions, Es)],
+ Types = [{type_name(Elem), Elem} || Elem <- get_content(typedecls, Es)],
+ Body = [{module,
+ [{name,[Name]}],
+ ([?NL] ++ types(lists:sort(Types), Opts)
+ ++ functions(lists:sort(Functions), Opts)
+ ++ timestamp())}],
+ Body.
+
+timestamp() ->
+ [{timestamp, [io_lib:fwrite("Generated by EDoc, ~s, ~s.",
+ [edoc_lib:datestr(date()),
+ edoc_lib:timestr(time())])]},?NL].
+
+functions(Fs, Opts) ->
+ lists:flatmap(fun ({Name, E}) -> function(Name, E, Opts) end, Fs).
+
+function(Name, #xmlElement{content = Es}, Opts) ->
+ TS = get_content(typespec, Es),
+ Spec = typespec(TS, Opts),
+ [{spec,(Name
+ ++ [?IND(2),{contract,Spec}]
+ ++ typespec_annos(TS))},
+ ?NL].
+
+function_name(E) ->
+ [] = get_attrval(module, E),
+ [?IND(2),{name,[atom(get_attrval(name, E))]},
+ ?IND(2),{arity,[get_attrval(arity, E)]}].
+
+label_anchor(Content, E) ->
+ case get_attrval(label, E) of
+ "" -> Content;
+ Ref -> [{marker, [{id, Ref}], Content}]
+ end.
+
+typespec([], _Opts) -> [];
+typespec(Es, Opts) ->
+ {Head, LDefs} = collect_clause(Es, Opts),
+ clause(Head, LDefs) ++ [?IND(2)].
+
+collect_clause(Es, Opts) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ Defs = get_elem(localdef, Es),
+ [Type] = get_elem(type, Es),
+ {format_spec(Name, Type, Opts), collect_local_defs(Defs, Opts)}.
+
+clause(Head, LDefs) ->
+ FC = [?IND(6),{head,Head}] ++ local_clause_defs(LDefs),
+ [?IND(4),{clause,FC}].
+
+local_clause_defs([]) -> [];
+local_clause_defs(LDefs) ->
+ LocalDefs = [{subtype,T} || T <- coalesce_local_defs(LDefs, [])],
+ [?IND(6),{guard,margin(8, LocalDefs)}].
+
+types(Ts, Opts) ->
+ lists:flatmap(fun ({Name, E}) -> typedecl(Name, E, Opts) end, Ts).
+
+typedecl(Name, E=#xmlElement{content = Es}, Opts) ->
+ TD = get_content(typedef, Es),
+ TypeDef = typedef(E, TD, Opts),
+ [{type,(Name
+ ++ [?IND(2),{typedecl, TypeDef}]
+ ++ typedef_annos(TD))},
+ ?NL].
+
+type_name(#xmlElement{content = Es}) ->
+ Typedef = get_content(typedef, Es),
+ [E] = get_elem(erlangName, Typedef),
+ Args = get_content(argtypes, Typedef),
+ [] = get_attrval(module, E),
+ [?IND(2),{name,[atom(get_attrval(name, E))]},
+ ?IND(2),{n_vars,[integer_to_list(length(Args))]}].
+
+typedef(E, Es, Opts) ->
+ Ns = get_elem(erlangName, Es),
+ Name =
+ ([t_name(Ns), "("]
+ ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
+ LDefs = collect_local_defs(get_elem(localdef, Es), Opts),
+ TypeHead = case get_elem(type, Es) of
+ [] -> label_anchor(Name, E);
+ Type -> (label_anchor(Name, E)
+ ++ format_type(Name, Type, Opts))
+ end,
+ ([?IND(6),{typehead,TypeHead}]
+ ++ local_type_defs(LDefs, [])).
+
+local_type_defs([], _) -> [];
+local_type_defs(LDefs, Last) ->
+ LocalDefs = [{local_def,T} || T <- coalesce_local_defs(LDefs, Last)],
+ [?IND(6),{local_defs,margin(8, LocalDefs)}].
+
+collect_local_defs(Es, Opts) ->
+ [collect_localdef(E, Opts) || E <- Es].
+
+collect_localdef(E = #xmlElement{content = Es}, Opts) ->
+ Name = case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(N0 = t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ N0 = t_var(V)
+ end,
+ {Name,N0,format_type(N0, get_elem(type, Es), Opts)}.
+
+%% "A = t(), B = t()" is coalesced into "A = B = t()".
+%% Names as B above are kept, but the formated string is empty.
+coalesce_local_defs([], _Last) ->
+ [];
+coalesce_local_defs([{Name,N0,TypeS} | L], Last) when Name =:= N0 ->
+ cld(L, [{Name,N0}], TypeS, Last);
+coalesce_local_defs([{Name,N0,TypeS} | L], Last) ->
+ [local_def(N0, Name, TypeS, Last, L) | coalesce_local_defs(L, Last)].
+
+cld([{Name,N0,TypeS} | L], Names, TypeS, Last) when Name =:= N0 ->
+ cld(L, [{Name,N0} | Names], TypeS, Last);
+cld(L, Names0, TypeS, Last) ->
+ Names = [{_,Name0} | Names1] = lists:reverse(Names0),
+ NS = join([N || {N,_} <- Names], [" = "]),
+ ([local_def(Name0, NS, TypeS, Last, L) |
+ [local_def(N0, "", "", [], L) || {_,N0} <- Names1]]
+ ++ coalesce_local_defs(L, Last)).
+
+local_def(Name, NS, TypeS, Last, L) ->
+ [{typename,Name},{string,NS ++ TypeS ++ [Last || L =:= []]}].
+
+%% join([], Sep) when is_list(Sep) ->
+%% [];
+join([H|T], Sep) ->
+ H ++ lists:append([Sep ++ X || X <- T]).
+
+%% Use the default formatting of EDoc, which creates references, and
+%% then insert newlines and indentation according to erl_pp (the
+%% (fast) Erlang pretty printer).
+format_spec(Name, Type, #opts{pretty_print = erl_pp}=Opts) ->
+ try
+ L = t_clause(Name, Type),
+ O = pp_clause(Name, Type),
+ {R, ".\n"} = diaf(L, O, Opts),
+ R
+ catch _:_ ->
+ %% Example: "@spec ... -> record(a)"
+ format_spec(Name, Type, Opts#opts{pretty_print=default})
+ end;
+format_spec(Sep, Type, _Opts) ->
+ t_clause(Sep, Type).
+
+t_clause(Name, Type) ->
+ #xmlElement{content = [#xmlElement{name = 'fun', content = C}]} = Type,
+ [Name] ++ t_fun(C).
+
+pp_clause(Pre, Type) ->
+ Types = ot_utype([Type]),
+ Atom = lists:duplicate(iolist_size(Pre), $a),
+ L1 = erl_pp:attribute({attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}),
+ "-spec " ++ L2 = lists:flatten(L1),
+ L3 = Pre ++ lists:nthtail(length(Atom), L2),
+ re:replace(L3, "\n ", "\n", [{return,list},global]).
+
+format_type(Name, Type, #opts{pretty_print = erl_pp}=Opts) ->
+ try
+ L = t_utype(Type),
+ O = pp_type(Name, Type),
+ {R, ".\n"} = diaf(L, O, Opts),
+ [" = "] ++ R
+ catch _:_ ->
+ %% Example: "t() = record(a)."
+ format_type(Name, Type, Opts#opts{pretty_print=default})
+ end;
+format_type(_Name, Type, _Opts) ->
+ [" = "] ++ t_utype(Type).
+
+pp_type(Prefix, Type) ->
+ Atom = list_to_atom(lists:duplicate(iolist_size(Prefix), $a)),
+ L1 = erl_pp:attribute({attribute,0,type,{Atom,ot_utype(Type),[]}}),
+ {L2,N} = case lists:dropwhile(fun(C) -> C =/= $: end, lists:flatten(L1)) of
+ ":: " ++ L3 -> {L3,9}; % compensation for extra "()" and ":"
+ "::\n" ++ L3 -> {"\n"++L3,6}
+ end,
+ Ss = lists:duplicate(N, $\s),
+ re:replace(L2, "\n"++Ss, "\n", [{return,list},global]).
+
+diaf(L, O0, Opts) ->
+ {R0, O} = diaf(L, [], O0, [], Opts),
+ R1 = rewrite_some_predefs(lists:reverse(R0)),
+ R = indentation(lists:flatten(R1)),
+ {R, O}.
+
+diaf([C | L], St, [C | O], R, Opts) ->
+ diaf(L, St, O, [[C] | R], Opts);
+diaf(" "++L, St, O, R, Opts) ->
+ diaf(L, St, O, R, Opts);
+diaf("", [Cs | St], O, R, Opts) ->
+ diaf(Cs, St, O, R, Opts);
+diaf("", [], O, R, _Opts) ->
+ {R, O};
+diaf(L, St, " "++O, R, Opts) ->
+ diaf(L, St, O, [" " | R], Opts);
+diaf(L, St, "\n"++O, R, Opts) ->
+ Ss = lists:takewhile(fun(C) -> C =:= $\s end, O),
+ diaf(L, St, lists:nthtail(length(Ss), O), ["\n"++Ss | R], Opts);
+diaf([{seealso, HRef0, S0} | L], St, O0, R, Opts) ->
+ {S, O} = diaf(S0, app_fix(O0), Opts),
+ HRef = fix_mod_ref(HRef0, Opts),
+ diaf(L, St, O, [{seealso, HRef, S} | R], Opts);
+diaf("="++L, St, "::"++O, R, Opts) ->
+ %% EDoc uses "=" for record field types; Dialyzer uses "::". Maybe
+ %% there should be an option for this, possibly affecting other
+ %% similar discrepancies.
+ diaf(L, St, O, ["=" | R], Opts);
+diaf([Cs | L], St, O, R, Opts) ->
+ diaf(Cs, [L | St], O, R, Opts).
+
+rewrite_some_predefs(S) ->
+ xpredef(lists:flatten(S)).
+
+xpredef([]) ->
+ [];
+xpredef("neg_integer()"++L) ->
+ ["integer() =< -1"] ++ xpredef(L);
+xpredef("non_neg_integer()"++L) ->
+ ["integer() >= 0"] ++ xpredef(L);
+xpredef("pos_integer()"++L) ->
+ ["integer() >= 1"] ++ xpredef(L);
+xpredef([T | Es]) when is_tuple(T) ->
+ [T | xpredef(Es)];
+xpredef([E | Es]) ->
+ [[E] | xpredef(Es)].
+
+indentation([]) ->
+ [];
+indentation([$\n|L]) ->
+ [{br,[]}|indent(L)];
+indentation([T | Es]) when is_tuple(T) ->
+ [T | indentation(Es)];
+indentation([E|L]) ->
+ [[E]|indentation(L)].
+
+indent([$\s|L]) ->
+ [{nbsp,[]}|indent(L)];
+indent(L) ->
+ indentation(L).
+
+app_fix(L) ->
+ try
+ {"//" ++ R1,L2} = app_fix(L, 1),
+ [App, Mod] = string:tokens(R1, "/"),
+ "//" ++ atom(App) ++ "/" ++ atom(Mod) ++ L2
+ catch _:_ -> L
+ end.
+
+app_fix(L, I) -> % a bit slow
+ {L1, L2} = lists:split(I, L),
+ case erl_scan:tokens([], L1 ++ ". ", 1) of
+ {done, {ok,[{atom,_,Atom}|_],_}, _} -> {atom_to_list(Atom), L2};
+ _ -> app_fix(L, I+1)
+ end.
+
+%% Remove the file suffix from module references.
+fix_mod_ref(HRef, #opts{file_suffix = ""}) ->
+ HRef;
+fix_mod_ref([{marker, S}]=HRef0, #opts{file_suffix = FS}) ->
+ {A, B} = lists:splitwith(fun(C) -> C =/= $# end, S),
+ case lists:member($:, A) of
+ true ->
+ HRef0; % should "save" most application references "http:"
+ false ->
+ case {lists:suffix(FS, A), B} of
+ {true, "#"++_} ->
+ [{marker, lists:sublist(A, length(A)-length(FS)) ++ B}];
+ _ ->
+ HRef0
+ end
+ end.
+
+see(E, Es) ->
+ case href(E) of
+ [] -> Es;
+ Ref ->
+ [{seealso, Ref, Es}]
+ end.
+
+href(E) ->
+ case get_attrval(href, E) of
+ "" -> [];
+ URI ->
+ [{marker, URI}]
+ end.
+
+atom(String) ->
+ io_lib:write_atom(list_to_atom(String)).
+
+t_name([E]) ->
+ N = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> atom(N);
+ M ->
+ S = atom(M) ++ ":" ++ atom(N),
+ case get_attrval(app, E) of
+ "" -> S;
+ A -> "//" ++ atom(A) ++ "/" ++ S
+ end
+ end.
+
+t_utype([E]) ->
+ t_utype_elem(E).
+
+t_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> t_type(Es);
+ Name ->
+ T = t_type(Es),
+ case T of
+ [Name] -> T; % avoid generating "Foo::Foo"
+ T -> [Name] ++ ["::"] ++ T
+ end
+ end.
+
+t_type([E=#xmlElement{name = typevar}]) ->
+ t_var(E);
+t_type([E=#xmlElement{name = atom}]) ->
+ t_atom(E);
+t_type([E=#xmlElement{name = integer}]) ->
+ t_integer(E);
+t_type([E=#xmlElement{name = range}]) ->
+ t_range(E);
+t_type([E=#xmlElement{name = binary}]) ->
+ t_binary(E);
+t_type([E=#xmlElement{name = float}]) ->
+ t_float(E);
+t_type([#xmlElement{name = nil}]) ->
+ t_nil();
+t_type([#xmlElement{name = list, content = Es}]) ->
+ t_list(Es);
+t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ t_nonempty_list(Es);
+t_type([#xmlElement{name = tuple, content = Es}]) ->
+ t_tuple(Es);
+t_type([#xmlElement{name = 'fun', content = Es}]) ->
+ ["fun("] ++ t_fun(Es) ++ [")"];
+t_type([E = #xmlElement{name = record, content = Es}]) ->
+ t_record(E, Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ t_abstype(E, Es);
+t_type([#xmlElement{name = union, content = Es}]) ->
+ t_union(Es).
+
+t_var(E) ->
+ [get_attrval(name, E)].
+
+t_atom(E) ->
+ [get_attrval(value, E)].
+
+t_integer(E) ->
+ [get_attrval(value, E)].
+
+t_range(E) ->
+ [get_attrval(value, E)].
+
+t_binary(E) ->
+ [get_attrval(value, E)].
+
+t_float(E) ->
+ [get_attrval(value, E)].
+
+t_nil() ->
+ ["[]"].
+
+t_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+
+t_nonempty_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"].
+
+t_tuple(Es) ->
+ ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_fun(Es) ->
+ ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
+ [") -> "] ++ t_utype(get_elem(type, Es))).
+
+t_record(E, Es) ->
+ Name = ["#"] ++ t_type(get_elem(atom, Es)),
+ case get_elem(field, Es) of
+ [] ->
+ see(E, [Name, "{}"]);
+ Fs ->
+ see(E, Name) ++ ["{"] ++ seq(fun t_field/1, Fs, ["}"])
+ end.
+
+t_field(#xmlElement{content = Es}) ->
+ t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)).
+
+t_abstype(E, Es) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ case get_elem(type, Es) of
+ [] ->
+ see(E, [Name, "()"]);
+ Ts ->
+ see(E, [Name]) ++ ["("] ++ seq(fun t_utype_elem/1, Ts, [")"])
+ end.
+
+t_abstype(Es) ->
+ ([t_name(get_elem(erlangName, Es)), "("]
+ ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])).
+
+t_union(Es) ->
+ seq(fun t_utype_elem/1, Es, " | ", []).
+
+seq(F, Es, Tail) ->
+ seq(F, Es, ", ", Tail).
+
+seq(F, [E], _Sep, Tail) ->
+ F(E) ++ Tail;
+seq(F, [E | Es], Sep, Tail) ->
+ F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
+seq(_F, [], _Sep, Tail) ->
+ Tail.
+
+get_elem(Name, [#xmlElement{name = Name} = E | Es]) ->
+ [E | get_elem(Name, Es)];
+get_elem(Name, [_ | Es]) ->
+ get_elem(Name, Es);
+get_elem(_, []) ->
+ [].
+
+get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_content(Name, Es) ->
+ case get_elem(Name, Es) of
+ [#xmlElement{content = Es1}] ->
+ Es1;
+ [] -> []
+ end.
+
+overview(_, _Options) -> [].
+
+package(_, _Options) -> [].
+
+type(_) -> [].
+
+%% ---------------------------------------------------------------------
+
+ot_utype([E]) ->
+ ot_utype_elem(E).
+
+ot_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> ot_type(Es);
+ N ->
+ Name = {var,0,list_to_atom(N)},
+ T = ot_type(Es),
+ case T of
+ Name -> T;
+ T -> {ann_type,0,[Name, T]}
+ end
+ end.
+
+ot_type([E=#xmlElement{name = typevar}]) ->
+ ot_var(E);
+ot_type([E=#xmlElement{name = atom}]) ->
+ ot_atom(E);
+ot_type([E=#xmlElement{name = integer}]) ->
+ ot_integer(E);
+ot_type([E=#xmlElement{name = range}]) ->
+ ot_range(E);
+ot_type([E=#xmlElement{name = binary}]) ->
+ ot_binary(E);
+ot_type([E=#xmlElement{name = float}]) ->
+ ot_float(E);
+ot_type([#xmlElement{name = nil}]) ->
+ ot_nil();
+ot_type([#xmlElement{name = list, content = Es}]) ->
+ ot_list(Es);
+ot_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ ot_nonempty_list(Es);
+ot_type([#xmlElement{name = tuple, content = Es}]) ->
+ ot_tuple(Es);
+ot_type([#xmlElement{name = 'fun', content = Es}]) ->
+ ot_fun(Es);
+ot_type([#xmlElement{name = record, content = Es}]) ->
+ ot_record(Es);
+ot_type([#xmlElement{name = abstype, content = Es}]) ->
+ ot_abstype(Es);
+ot_type([#xmlElement{name = union, content = Es}]) ->
+ ot_union(Es).
+
+ot_var(E) ->
+ {var,0,list_to_atom(get_attrval(name, E))}.
+
+ot_atom(E) ->
+ {ok, [Atom], _} = erl_scan:string(get_attrval(value, E), 0),
+ Atom.
+
+ot_integer(E) ->
+ {integer,0,list_to_integer(get_attrval(value, E))}.
+
+ot_range(E) ->
+ [I1, I2] = string:tokens(get_attrval(value, E), "."),
+ {type,0,range,[{integer,0,list_to_integer(I1)},
+ {integer,0,list_to_integer(I2)}]}.
+
+ot_binary(E) ->
+ {Base, Unit} =
+ case string:tokens(get_attrval(value, E), ",:*><") of
+ [] ->
+ {0, 0};
+ ["_",B] ->
+ {list_to_integer(B), 0};
+ ["_","_",U] ->
+ {0, list_to_integer(U)};
+ ["_",B,_,"_",U] ->
+ {list_to_integer(B), list_to_integer(U)}
+ end,
+ {type,0,binary,[{integer,0,Base},{integer,0,Unit}]}.
+
+ot_float(E) ->
+ {float,0,list_to_float(get_attrval(value, E))}.
+
+ot_nil() ->
+ {nil,0}.
+
+ot_list(Es) ->
+ {type,0,list,[ot_utype(get_elem(type, Es))]}.
+
+ot_nonempty_list(Es) ->
+ {type,0,nonempty_list,[ot_utype(get_elem(type, Es))]}.
+
+ot_tuple(Es) ->
+ {type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
+
+ot_fun(Es) ->
+ Range = ot_utype(get_elem(type, Es)),
+ Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)],
+ {type,0,'fun',[{type,0,product,Args},Range]}.
+
+ot_record(Es) ->
+ {type,0,record,[ot_type(get_elem(atom, Es)) |
+ [ot_field(F) || F <- get_elem(field, Es)]]}.
+
+ot_field(#xmlElement{content = Es}) ->
+ {type,0,field_type,
+ [ot_type(get_elem(atom, Es)), ot_utype(get_elem(type, Es))]}.
+
+ot_abstype(Es) ->
+ ot_name(get_elem(erlangName, Es),
+ [ot_utype_elem(Elem) || Elem <- get_elem(type, Es)]).
+
+ot_union(Es) ->
+ {type,0,union,[ot_utype_elem(E) || E <- Es]}.
+
+ot_name(Es, T) ->
+ case ot_name(Es) of
+ [Mod, ":", Atom] ->
+ {remote_type,0,[{atom,0,list_to_atom(Mod)},
+ {atom,0,list_to_atom(Atom)},T]};
+ "tuple" when T =:= [] ->
+ {type,0,tuple,any};
+ Atom ->
+ {type,0,list_to_atom(Atom),T}
+ end.
+
+ot_name([E]) ->
+ Atom = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> Atom;
+ M ->
+ case get_attrval(app, E) of
+ "" ->
+ [M, ":", Atom];
+ A ->
+ ["//"++A++"/" ++ M, ":", Atom] % EDoc only!
+ end
+ end.
+
+%% Returns exactly those annotations that can be referred to. Note
+%% that a Dialyzer type/spec (currently) can have more annotations
+%% than can be represented by EDoc types. Note also that edoc_dia
+%% has annotated all type variables with themselves.
+typespec_annos([]) -> [?NL];
+typespec_annos([_|Es]) ->
+ annotations(clause_annos(Es)).
+
+clause_annos(Es) ->
+ [annos(get_elem(type, Es)), local_defs_annos(get_elem(localdef, Es))].
+
+typedef_annos(Es) ->
+ annotations([(case get_elem(type, Es) of
+ [] -> [];
+ T -> annos(T)
+ end
+ ++ lists:flatmap(fun annos_elem/1,
+ get_content(argtypes, Es))),
+ local_defs_annos(get_elem(localdef, Es))]).
+
+local_defs_annos(Es) ->
+ lists:flatmap(fun localdef_annos/1, Es).
+
+localdef_annos(#xmlElement{content = Es}) ->
+ annos(get_elem(type, Es)).
+
+annotations(AnnoL) ->
+ Annos = lists:usort(lists:flatten(AnnoL)),
+ margin(2, Annos).
+
+margin(N, L) ->
+ lists:append([[?IND(N),E] || E <- L]) ++ [?IND(N-2)].
+
+annos([E]) ->
+ annos_elem(E).
+
+annos_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> annos_type(Es);
+ "..." -> annos_type(Es); % compensate for a kludge in edoc_dia.erl
+ N ->
+ [{anno,[N]} | annos_type(Es)]
+ end.
+
+annos_type([#xmlElement{name = list, content = Es}]) ->
+ annos(get_elem(type, Es));
+annos_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ annos(get_elem(type, Es));
+annos_type([#xmlElement{name = tuple, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, Es);
+annos_type([#xmlElement{name = 'fun', content = Es}]) ->
+ (annos(get_elem(type, Es))
+ ++ lists:flatmap(fun annos_elem/1, get_content(argtypes, Es)));
+annos_type([#xmlElement{name = record, content = Es}]) ->
+ lists:append([annos(get_elem(type, Es1)) ||
+ #xmlElement{content = Es1} <- get_elem(field, Es)]);
+annos_type([#xmlElement{name = abstype, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, get_elem(type, Es));
+annos_type([#xmlElement{name = union, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, Es);
+annos_type([E=#xmlElement{name = typevar}]) ->
+ annos_elem(E);
+annos_type(_) ->
+ [].
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index f39f21aa70..c8e9c907b2 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -115,13 +115,13 @@ change_pattern({Mod, Pattern}) when is_atom(Mod) ->
old_ctp({Mod, _Fun, Args}) ->
case Mod of
- et -> ignore;
+ et -> {ok, ignore};
_ -> dbg:ctp({Mod, report_event, Args})
end.
old_tp({Mod, _Fun, Args}, Pattern) ->
case Mod of
- et -> ignore;
+ et -> {ok, ignore};
_ -> dbg:tp({Mod, report_event, Args}, Pattern)
end.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 696414eb7a..1cba89c621 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -4678,16 +4678,16 @@ t_HttpRequest() ->
t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]).
t_HttpResponse() ->
- t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_string()]).
+ t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]).
t_HttpHeader() ->
- t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_string()]).
+ t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]).
t_HttpError() ->
- t_tuple([t_atom('http_error'), t_string()]).
+ t_tuple([t_atom('http_error'), t_HttpString()]).
t_HttpMethod() ->
- t_sup(t_HttpMethodAtom(), t_string()).
+ t_sup(t_HttpMethodAtom(), t_HttpString()).
t_HttpMethodAtom() ->
t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']).
@@ -4696,18 +4696,18 @@ t_HttpUri() ->
t_sup([t_atom('*'),
t_tuple([t_atom('absoluteURI'),
t_sup(t_atom('http'), t_atom('https')),
- t_string(),
+ t_HttpString(),
t_sup(t_non_neg_integer(), t_atom('undefined')),
- t_string()]),
- t_tuple([t_atom('scheme'), t_string(), t_string()]),
- t_tuple([t_atom('abs_path'), t_string()]),
- t_string()]).
+ t_HttpString()]),
+ t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]),
+ t_tuple([t_atom('abs_path'), t_HttpString()]),
+ t_HttpString()]).
t_HttpVersion() ->
t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
t_HttpField() ->
- t_sup(t_HttpFieldAtom(), t_string()).
+ t_sup(t_HttpFieldAtom(), t_HttpString()).
t_HttpFieldAtom() ->
t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding',
@@ -4724,6 +4724,9 @@ t_HttpFieldAtom() ->
'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie',
'Keep-Alive', 'Proxy-Connection']).
+t_HttpString() ->
+ t_sup(t_string(),t_binary()).
+
%% =====================================================================
%% These are used for the built-in functions of 'code'
%% =====================================================================
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index ea8053cafa..672ea3fa98 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
@@ -57,7 +57,7 @@
[{inets, [{services, [{httpc, PropertyList}]}]}]
</pre>
<p>For valid properties see
- <seealso marker="http">httpc(3)</seealso>. </p>
+ <seealso marker="httpc">httpc(3)</seealso>. </p>
</section>
<section>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 9c8df28fec..c20358178b 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
@@ -172,7 +172,8 @@ ssl_options() = {verify, code()} |
{autoredirect, boolean()} |
{proxy_auth, {userstring(), passwordstring()}} |
{version, http_version()} |
- {relaxed, boolean()}</v>
+ {relaxed, boolean()} |
+ {url_encode, boolean()}</v>
<v>timeout() = integer() >= 0 | infinity</v>
<v>Options = options()</v>
<v>options() = [option()]</v>
@@ -276,6 +277,11 @@ ssl_options() = {verify, code()} |
<p>Defaults to <c>false</c>. </p>
</item>
+ <tag><c><![CDATA[url_encode]]></c></tag>
+ <item>
+ <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p>
+ <p>Defaults to <c>false</c>. </p>
+ </item>
</taglist>
<p>Option (<c>option()</c>) details: </p>
@@ -342,7 +348,7 @@ ssl_options() = {verify, code()} |
<p>Socket options to be used for this and subsequent
request(s). </p>
<p>Overrides any value set by the
- <seealso marker="set_options">set_options</seealso>
+ <seealso marker="#set_options">set_options</seealso>
function. </p>
<p>Note that the validity of the options are <em>not</em>
checked in any way. </p>
@@ -632,4 +638,3 @@ apply(Module, Function, [ReplyInfo | Args])
</section>
</erlref>
-
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 847605fe93..62f4e18f82 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
@@ -421,7 +421,7 @@ bytes
Beware of trailing space in Replacement that will be used.
If you must have a space in Re use e.g the character encoding
- <code>\040</code> see <seealso marker="re">re(3)</seealso>.
+ <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>.
</item>
<tag>{directory_index, [string()]}</tag>
@@ -931,6 +931,10 @@ bytes
connection
}).
</code>
+
+ <p>To acess the record in your callback-module use </p>
+ <code> -include_lib("inets/include/httpd.hrl"). </code>
+
<p>The fields of the <c>mod</c> record has the following meaning:
</p>
<taglist>
@@ -978,10 +982,10 @@ bytes
<c>parsed_header</c> contains all HTTP header fields from the
HTTP-request stored in a list as key-value tuples. See RFC 2616
for a listing of all header fields. For example the date field
- would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"}.
+ would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"} </c>.
RFC 2616 defines that HTTP is a case insensitive protocol and
the header fields may be in lower case or upper case. Httpd will
- ensure that all header field names are in lower case. </c>.
+ ensure that all header field names are in lower case.
</item>
<tag><c>entity_body</c></tag>
<item>The <c>Entity-Body</c> as defined
diff --git a/lib/inets/include/httpd.hrl b/lib/inets/include/httpd.hrl
new file mode 100644
index 0000000000..a7e63ca670
--- /dev/null
+++ b/lib/inets/include/httpd.hrl
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-ifndef(httpd_hrl).
+-define(httpd_hrl, true).
+
+-include_lib("kernel/include/file.hrl").
+
+-record(init_data,{peername,resolve}).
+
+-record(mod,{init_data,
+ data=[],
+ socket_type=ip_comm,
+ socket,
+ config_db,
+ method,
+ absolute_uri=[],
+ request_uri,
+ http_version,
+ request_line,
+ parsed_header=[],
+ entity_body,
+ connection}).
+-endif. % -ifdef(httpd_hrl).
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index 575c6efaec..0397b48ab2 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -51,7 +51,6 @@ MODULES = \
httpc_profile_sup \
httpc_response \
httpc_request \
- http_uri \
HRL_FILES = httpc_internal.hrl
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 851364001c..ae754fab94 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -442,18 +442,23 @@ handle_request(Method, Url,
HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions),
Receiver = proplists:get_value(receiver, Options),
SocketOpts = proplists:get_value(socket_opts, Options),
+ UrlEncodeBool = HTTPOptions#http_options.url_encode,
+ MaybeEscPath = url_encode(Path, UrlEncodeBool),
+ MaybeEscQuery = url_encode(Query, UrlEncodeBool),
+ AbsUri = url_encode(Url, UrlEncodeBool),
+
Request = #request{from = Receiver,
scheme = Scheme,
address = {Host, Port},
- path = Path,
- pquery = Query,
+ path = MaybeEscPath,
+ pquery = MaybeEscQuery,
method = Method,
headers = HeadersRecord,
content = {ContentType, Body},
settings = HTTPOptions,
- abs_uri = Url,
+ abs_uri = AbsUri,
userinfo = UserInfo,
- stream = Stream,
+ stream = Stream,
headers_as_is = headers_as_is(Headers, Options),
socket_opts = SocketOpts,
started = Started},
@@ -471,6 +476,10 @@ handle_request(Method, Url,
Error
end.
+url_encode(URI, true) ->
+ http_uri:encode(URI);
+url_encode(URI, false) ->
+ URI.
handle_answer(RequestId, false, _) ->
{ok, RequestId};
@@ -578,12 +587,8 @@ http_options_default() ->
(_) ->
error
end,
- AutoRedirectPost = fun(Value) when (Value =:= true) orelse
- (Value =:= false) ->
- {ok, Value};
- (_) ->
- error
- end,
+ AutoRedirectPost = boolfun(),
+
SslPost = fun(Value) when is_list(Value) ->
{ok, {?HTTP_DEFAULT_SSL_KIND, Value}};
({ssl, SslOptions}) when is_list(SslOptions) ->
@@ -601,12 +606,8 @@ http_options_default() ->
(_) ->
error
end,
- RelaxedPost = fun(Value) when (Value =:= true) orelse
- (Value =:= false) ->
- {ok, Value};
- (_) ->
- error
- end,
+ RelaxedPost = boolfun(),
+
ConnTimeoutPost =
fun(Value) when is_integer(Value) andalso (Value >= 0) ->
{ok, Value};
@@ -615,6 +616,8 @@ http_options_default() ->
(_) ->
error
end,
+
+ UrlDecodePost = boolfun(),
[
{version, {value, "HTTP/1.1"}, #http_options.version, VersionPost},
{timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost},
@@ -622,18 +625,21 @@ http_options_default() ->
{ssl, {value, {?HTTP_DEFAULT_SSL_KIND, []}}, #http_options.ssl, SslPost},
{proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost},
{relaxed, {value, false}, #http_options.relaxed, RelaxedPost},
+ {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost},
%% this field has to be *after* the timeout option (as that field is used for the default value)
{connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}
].
+boolfun() ->
+ fun(Value) when (Value =:= true) orelse
+ (Value =:= false) ->
+ {ok, Value};
+ (_) ->
+ error
+ end.
request_options_defaults() ->
- VerifyBoolean =
- fun(Value) when ((Value =:= true) orelse (Value =:= false)) ->
- ok;
- (_) ->
- error
- end,
+ VerifyBoolean = boolfun(),
VerifySync = VerifyBoolean,
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 8af6613fa2..cb6f3e2841 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -713,33 +713,38 @@ terminate(normal,
profile_name = ProfileName,
request = Request,
timers = Timers,
- pipeline = Pipeline}) ->
+ pipeline = Pipeline,
+ keep_alive = KeepAlive} = State) ->
?hcrt("terminate(normal) - remote close",
[{id, Id}, {profile, ProfileName}]),
%% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
+ maybe_retry_queue(Pipeline, State),
+ maybe_retry_queue(KeepAlive, State),
+
%% Cancel timers
- #timers{request_timers = ReqTmrs, queue_timer = QTmr} = Timers,
- cancel_timer(QTmr, timeout_queue),
- lists:foreach(fun({_, Timer}) -> cancel_timer(Timer, timeout) end,
- ReqTmrs),
+ cancel_timers(Timers),
%% Maybe deliver answers to requests
- deliver_answers([Request | queue:to_list(Pipeline)]),
+ deliver_answer(Request),
%% And, just in case, close our side (**really** overkill)
http_transport:close(SocketType, Socket);
-terminate(_, #state{session = #session{id = Id,
- socket = Socket,
- socket_type = SocketType},
+terminate(Reason, #state{session = #session{id = Id,
+ socket = Socket,
+ socket_type = SocketType},
request = undefined,
profile_name = ProfileName,
timers = Timers,
pipeline = Pipeline,
keep_alive = KeepAlive} = State) ->
+ ?hcrt("terminate",
+ [{id, Id}, {profile, ProfileName}, {reason, Reason}]),
+
+ %% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
maybe_retry_queue(Pipeline, State),
@@ -772,59 +777,55 @@ maybe_send_answer(#request{from = answer_sent}, _Reason, State) ->
maybe_send_answer(Request, Answer, State) ->
answer_request(Request, Answer, State).
-deliver_answers([]) ->
- ?hcrd("deliver answer done", []),
- ok;
-deliver_answers([#request{id = Id, from = From} = Request | Requests])
+deliver_answer(#request{id = Id, from = From} = Request)
when is_pid(From) ->
Response = httpc_response:error(Request, socket_closed_remotely),
?hcrd("deliver answer", [{id, Id}, {from, From}, {response, Response}]),
- httpc_response:send(From, Response),
- deliver_answers(Requests);
-deliver_answers([Request|Requests]) ->
+ httpc_response:send(From, Response);
+deliver_answer(Request) ->
?hcrd("skip deliver answer", [{request, Request}]),
- deliver_answers(Requests).
+ ok.
%%--------------------------------------------------------------------
%% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState}
%% Purpose: Convert process state when code is changed
%%--------------------------------------------------------------------
-code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.1'}, {to, '5.0.2'}]) ->
- Settings = new_http_options(Request#request.settings),
- NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun new_http_options/1),
- {ok, State#state{request = NewRequest, pipeline = NewQueue}};
-
-code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.2'}, {to, '5.0.1'}]) ->
- Settings = old_http_options(Request#request.settings),
- NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun old_http_options/1),
- {ok, State#state{request = NewRequest, pipeline = NewQueue}};
+%% code_change(_, #state{request = Request, pipeline = Queue} = State,
+%% [{from, '5.0.1'}, {to, '5.0.2'}]) ->
+%% Settings = new_http_options(Request#request.settings),
+%% NewRequest = Request#request{settings = Settings},
+%% NewQueue = new_queue(Queue, fun new_http_options/1),
+%% {ok, State#state{request = NewRequest, pipeline = NewQueue}};
+
+%% code_change(_, #state{request = Request, pipeline = Queue} = State,
+%% [{from, '5.0.2'}, {to, '5.0.1'}]) ->
+%% Settings = old_http_options(Request#request.settings),
+%% NewRequest = Request#request{settings = Settings},
+%% NewQueue = new_queue(Queue, fun old_http_options/1),
+%% {ok, State#state{request = NewRequest, pipeline = NewQueue}};
code_change(_, State, _) ->
{ok, State}.
-new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}) ->
- {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}.
-
-old_http_options({http_options, _, TimeOut, AutoRedirect,
- SslOpts, Auth, Relaxed}) ->
- {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
-
-new_queue(Queue, Fun) ->
- List = queue:to_list(Queue),
- NewList =
- lists:map(fun(Request) ->
- Settings =
- Fun(Request#request.settings),
- Request#request{settings = Settings}
- end, List),
- queue:from_list(NewList).
+%% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
+%% Auth, Relaxed}) ->
+%% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
+%% Auth, Relaxed}.
+
+%% old_http_options({http_options, _, TimeOut, AutoRedirect,
+%% SslOpts, Auth, Relaxed}) ->
+%% {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
+
+%% new_queue(Queue, Fun) ->
+%% List = queue:to_list(Queue),
+%% NewList =
+%% lists:map(fun(Request) ->
+%% Settings =
+%% Fun(Request#request.settings),
+%% Request#request{settings = Settings}
+%% end, List),
+%% queue:from_list(NewList).
%%%--------------------------------------------------------------------
@@ -854,12 +855,18 @@ connect(SocketType, ToAddress,
inet6fb4 ->
Opts3 = [inet6 | Opts2],
case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
+ {error, _Reason} = Error ->
Opts4 = [inet | Opts2],
- http_transport:connect(SocketType, ToAddress, Opts4, Timeout);
- Other ->
- Other
+ case http_transport:connect(SocketType,
+ ToAddress, Opts4, Timeout) of
+ {error, _} ->
+ %% Reply with the "original" error
+ Error;
+ OK ->
+ OK
+ end;
+ OK ->
+ OK
end;
_ ->
Opts3 = [IpFamily | Opts2],
@@ -1440,6 +1447,12 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg,
timers =
Timers#timers{request_timers =
lists:delete(Timer, RequestTimers)}}.
+
+cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) ->
+ cancel_timer(QTmr, timeout_queue),
+ CancelTimer = fun({_, Timer}) -> cancel_timer(Timer, timeout) end,
+ lists:foreach(CancelTimer, ReqTmrs).
+
cancel_timer(undefined, _) ->
ok;
cancel_timer(Timer, TimeoutMsg) ->
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 3cdd95a02b..1d8a5b6a92 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -60,7 +60,11 @@
relaxed = false,
%% integer() - ms before a connect times out
- connect_timeout = ?HTTP_REQUEST_CTIMEOUT
+ connect_timeout = ?HTTP_REQUEST_CTIMEOUT,
+
+ %% bool() - Use %-encoding rfc 2396
+ url_encode
+
}
).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 1e1bde220b..591cb78c29 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -734,10 +734,11 @@ handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result,
ok;
[] ->
- error_report(Profile,
- "handler (~p) successfully started "
- "for unknown request ~p => canceling",
- [HandlerPid, ReqId]),
+ ?hcri("handler successfully started "
+ "for unknown request => canceling",
+ [{profile, Profile},
+ {handler, HandlerPid},
+ {request, ReqId}]),
httpc_handler:cancel(ReqId, HandlerPid)
end.
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 5dac3b0c00..aaf3cfb995 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -45,7 +45,8 @@ MODULES = \
http_transport\
http_util \
http_request \
- http_response
+ http_response \
+ http_uri
HRL_FILES = http_internal.hrl
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index b8121852b8..0024d19fc1 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -192,24 +192,31 @@ listen_ip_comm(Addr, Port) ->
case IpFamily of
inet6fb4 ->
Opts2 = [inet6 | Opts],
+ ?hlrt("try ipv6 listen", [{port, NewPort}, {opts, Opts2}]),
case (catch gen_tcp:listen(NewPort, Opts2)) of
{error, Reason} when ((Reason =:= nxdomain) orelse
(Reason =:= eafnosupport)) ->
Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen failed - try ipv4 instead",
+ [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
gen_tcp:listen(NewPort, Opts3);
%% This is when a given hostname has resolved to a
%% IPv4-address. The inet6-option together with a
%% {ip, IPv4} option results in badarg
- {'EXIT', _} ->
+ {'EXIT', Reason} ->
Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen exit - try ipv4 instead",
+ [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
gen_tcp:listen(NewPort, Opts3);
Other ->
+ ?hlrt("ipv6 listen done", [{other, Other}]),
Other
end;
_ ->
Opts2 = [IpFamily | Opts],
+ ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]),
gen_tcp:listen(NewPort, Opts2)
end.
diff --git a/lib/inets/src/http_client/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 615a0d8ec4..44b9face0b 100644
--- a/lib/inets/src/http_client/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,26 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(http_uri).
--export([parse/1]).
+-export([parse/1, encode/1, decode/1]).
%%%=========================================================================
%%% API
@@ -34,10 +34,25 @@ parse(AbsURI) ->
{UserInfo, Host, Port, Path, Query} ->
{Scheme, UserInfo, Host, Port, Path, Query};
_ ->
- {error, {malformed_url, AbsURI}}
+ {error, {malformed_url, AbsURI}}
end
end.
+encode(URI) ->
+ Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
+ $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $\\, $', $^, $%, $ ]),
+ lists:append(lists:map(fun(Char) ->
+ uri_encode(Char, Reserved)
+ end, URI)).
+
+decode([$%,Hex1,Hex2|Rest]) ->
+ [hex2dec(Hex1)*16+hex2dec(Hex2)|decode(Rest)];
+decode([First|Rest]) ->
+ [First|decode(Rest)];
+decode([]) ->
+ [].
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
@@ -56,7 +71,7 @@ parse_scheme(AbsURI) ->
parse_uri_rest(Scheme, "//" ++ URIPart) ->
- {Authority, PathQuery} =
+ {Authority, PathQuery} =
case split_uri(URIPart, "/", URIPart, 1, 0) of
Split = {_, _} ->
Split;
@@ -68,7 +83,7 @@ parse_uri_rest(Scheme, "//" ++ URIPart) ->
{URIPart,""}
end
end,
-
+
{UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1),
{Host, Port} = parse_host_port(Scheme, HostPort),
{Path, Query} = parse_path_query(PathQuery),
@@ -78,7 +93,6 @@ parse_uri_rest(Scheme, "//" ++ URIPart) ->
parse_path_query(PathQuery) ->
{Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0),
{path(Path), Query}.
-
parse_host_port(Scheme,"[" ++ HostPort) -> %ipv6
DefaultPort = default_port(Scheme),
@@ -90,12 +104,12 @@ parse_host_port(Scheme, HostPort) ->
DefaultPort = default_port(Scheme),
{Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1),
{Host, int_port(Port)}.
-
+
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
case inets_regexp:first_match(UriPart, SplitChar) of
{match, Match, _} ->
{string:substr(UriPart, 1, Match - SkipLeft),
- string:substr(UriPart, Match + SkipRight, length(UriPart))};
+ string:substr(UriPart, Match + SkipRight, length(UriPart))};
nomatch ->
NoMatchResult
end.
@@ -114,3 +128,15 @@ path("") ->
"/";
path(Path) ->
Path.
+
+uri_encode(Char, Reserved) ->
+ case sets:is_element(Char, Reserved) of
+ true ->
+ [ $% | http_util:integer_to_hexlist(Char)];
+ false ->
+ [Char]
+ end.
+
+hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
+hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
+hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 879e605217..bdd8c5ee3c 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -82,7 +82,9 @@ MODULES = \
mod_security \
mod_security_server
-HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl
+INCLUDE = ../../include
+
+HRL_FILES = $(INCLUDE)/httpd.hrl httpd_internal.hrl mod_auth.hrl
ERL_FILES = $(MODULES:%=%.erl)
@@ -98,9 +100,9 @@ include ../inets_app/inets.mk
ERL_COMPILE_FLAGS += \
$(INETS_FLAGS) \
$(INETS_ERL_COMPILE_FLAGS) \
- -I../../include \
+ -I$(INCLUDE) \
-I../inets_app \
- -I../http_lib
+ -I../http_lib \
# ----------------------------------------------------
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index fb5fa1c758..93608dbf96 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -24,7 +24,6 @@
-include("httpd.hrl").
-
%% Behavior callbacks
-export([
start_standalone/1,
@@ -271,8 +270,8 @@ foreach([KeyValue|Rest]) ->
{ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "),
case inets_regexp:split(Plus2Space,"=") of
{ok,[Key|Value]} ->
- [{httpd_util:decode_hex(Key),
- httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)];
+ [{http_uri:decode(Key),
+ http_uri:decode(lists:flatten(Value))}|foreach(Rest)];
{ok,_} ->
foreach(Rest)
end.
diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl
deleted file mode 100644
index 0db8a029bb..0000000000
--- a/lib/inets/src/http_server/httpd.hrl
+++ /dev/null
@@ -1,82 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--include_lib("kernel/include/file.hrl").
-
--ifndef(SERVER_SOFTWARE).
--define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile!
--endif.
--define(SERVER_PROTOCOL,"HTTP/1.1").
--define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi,
- mod_dir, mod_get, mod_head, mod_log, mod_disk_log]).
--define(SOCKET_CHUNK_SIZE,8192).
--define(SOCKET_MAX_POLL,25).
--define(FILE_CHUNK_SIZE,64*1024).
--define(GATEWAY_INTERFACE,"CGI/1.1").
--define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
--define(DEFAULT_CONTEXT,
- [{errmsg,"[an error occurred while processing this directive]"},
- {timefmt,"%A, %d-%b-%y %T %Z"},
- {sizefmt,"abbrev"}]).
-
-
--ifdef(inets_error).
--define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(ERROR(F,A),[]).
--endif.
-
--ifdef(inets_log).
--define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(LOG(F,A),[]).
--endif.
-
--ifdef(inets_debug).
--define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(DEBUG(F,A),[]).
--endif.
-
--ifdef(inets_cdebug).
--define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(CDEBUG(F,A),[]).
--endif.
-
-
--record(init_data,{peername,resolve}).
--record(mod,{init_data,
- data=[],
- socket_type=ip_comm,
- socket,
- config_db,
- method,
- absolute_uri=[],
- request_uri,
- http_version,
- request_line,
- parsed_header=[],
- entity_body,
- connection}).
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
index c261eff6b2..bcebb6a9e3 100644
--- a/lib/inets/src/http_server/httpd_acceptor.erl
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -21,6 +21,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
%% Internal application API
-export([start_link/5, start_link/6]).
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 8438c4037e..f4d8a6c09f 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -31,8 +31,8 @@
validate_properties/1]).
-define(VMODULE,"CONF").
--include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("httpd.hrl").
-include_lib("inets/src/http_lib/http_internal.hrl").
diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl
index 5fd529100e..7e21d9e158 100644
--- a/lib/inets/src/http_server/httpd_file.erl
+++ b/lib/inets/src/http_server/httpd_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,11 +22,13 @@
-export([handle_error/4]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
+
handle_error(eacces, Op, ModData, Path) ->
- handle_error(403, Op, ModData, Path,"");
+ handle_error(403, Op, ModData, Path,"Forbidden");
handle_error(enoent, Op, ModData, Path) ->
- handle_error(404, Op, ModData, Path,"");
+ handle_error(404, Op, ModData, Path,"File not found");
handle_error(enotdir, Op, ModData, Path) ->
handle_error(404, Op, ModData, Path,
": A component of the file name is not a directory");
@@ -34,8 +36,8 @@ handle_error(emfile, Op, _ModData, Path) ->
handle_error(500, Op, none, Path, ": To many open files");
handle_error({enfile,_}, Op, _ModData, Path) ->
handle_error(500, Op, none, Path, ": File table overflow");
-handle_error(_Reason, Op, _ModData, Path) ->
- handle_error(500, Op, none, Path, "").
+handle_error(_Reason, Op, ModData, Path) ->
+ handle_error(404, Op, ModData, Path, "File not found").
handle_error(StatusCode, Op, none, Path, Reason) ->
{StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)};
diff --git a/lib/inets/src/http_server/httpd_internal.hrl b/lib/inets/src/http_server/httpd_internal.hrl
index 38b0ddefd3..108469ea0a 100644
--- a/lib/inets/src/http_server/httpd_internal.hrl
+++ b/lib/inets/src/http_server/httpd_internal.hrl
@@ -21,7 +21,50 @@
-ifndef(httpd_internal_hrl).
-define(httpd_internal_hrl, true).
--include_lib("inets/src/inets_app/inets_internal.hrl").
+-ifndef(SERVER_SOFTWARE).
+-define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile!
+-endif.
+-define(SERVER_PROTOCOL,"HTTP/1.1").
+-define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi,
+ mod_dir, mod_get, mod_head, mod_log, mod_disk_log]).
+-define(SOCKET_CHUNK_SIZE,8192).
+-define(SOCKET_MAX_POLL,25).
+-define(FILE_CHUNK_SIZE,64*1024).
+-define(GATEWAY_INTERFACE,"CGI/1.1").
+-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
+-define(DEFAULT_CONTEXT,
+ [{errmsg,"[an error occurred while processing this directive]"},
+ {timefmt,"%A, %d-%b-%y %T %Z"},
+ {sizefmt,"abbrev"}]).
+
+
+-ifdef(inets_error).
+-define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(ERROR(F,A),[]).
+-endif.
+
+-ifdef(inets_log).
+-define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(LOG(F,A),[]).
+-endif.
+
+-ifdef(inets_debug).
+-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(DEBUG(F,A),[]).
+-endif.
+
+-ifdef(inets_cdebug).
+-define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(CDEBUG(F,A),[]).
+-endif.
-define(SERVICE, httpd).
-define(hdri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 883acbf585..7084d9824a 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -304,9 +304,9 @@ validate_uri(RequestURI) ->
UriNoQueryNoHex =
case string:str(RequestURI, "?") of
0 ->
- (catch httpd_util:decode_hex(RequestURI));
+ (catch http_uri:decode(RequestURI));
Ndx ->
- (catch httpd_util:decode_hex(string:left(RequestURI, Ndx)))
+ (catch http_uri:decode(string:left(RequestURI, Ndx)))
end,
case UriNoQueryNoHex of
{'EXIT',_Reason} ->
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index a742cbef76..d3115150b0 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,7 @@
-export([create_env/3]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%%%=========================================================================
%%% Internal application API
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 1507c6852a..f94e5459c1 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -37,7 +37,7 @@
-define(TIMEOUT, 15000).
-include("httpd_internal.hrl").
-
+-include("inets_internal.hrl").
%%%=========================================================================
%%% API
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index cfad79638f..789f12652b 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -21,7 +21,7 @@
-export([ip_address/2, lookup/2, lookup/3, multi_lookup/2,
lookup_mime/2, lookup_mime/3, lookup_mime_default/2,
lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0,
- rfc1123_date/1, day/1, month/1, decode_hex/1,
+ rfc1123_date/1, day/1, month/1,
flatlength/1, split_path/1, split_script_path/1,
suffix/1, split/3, uniq/1,
make_name/2,make_name/3,make_name/4,strip/1,
@@ -32,7 +32,7 @@
dir_validate/2, file_validate/2, mime_type_validate/1,
mime_types_validate/1, custom_date/0]).
--export([encode_hex/1]).
+-export([encode_hex/1, decode_hex/1]).
-include_lib("kernel/include/file.hrl").
ip_address({_,_,_,_} = Address, _IpFamily) ->
@@ -175,13 +175,13 @@ reason_phrase(_) -> "Internal Server Error".
%% message
message(301,URL,_) ->
- "The document has moved <A HREF=\""++URL++"\">here</A>.";
+ "The document has moved <A HREF=\""++ maybe_encode(URL) ++"\">here</A>.";
message(304, _URL,_) ->
"The document has not been changed.";
message(400,none,_) ->
"Your browser sent a query that this server could not understand.";
message(400,Msg,_) ->
- "Your browser sent a query that this server could not understand. "++Msg;
+ "Your browser sent a query that this server could not understand. "++ maybe_encode(Msg);
message(401,none,_) ->
"This server could not verify that you
are authorized to access the document you
@@ -190,9 +190,9 @@ credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.";
message(403,RequestURI,_) ->
- "You don't have permission to access "++RequestURI++" on this server.";
+ "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server.";
message(404,RequestURI,_) ->
- "The requested URL "++RequestURI++" was not found on this server.";
+ "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server.";
message(408, Timeout, _) ->
Timeout;
message(412,none,_) ->
@@ -200,7 +200,7 @@ message(412,none,_) ->
message(413, Reason,_) ->
"Entity: " ++ Reason;
message(414,ReasonPhrase,_) ->
- "Message "++ReasonPhrase++".";
+ "Message "++ ReasonPhrase ++".";
message(416,ReasonPhrase,_) ->
ReasonPhrase;
@@ -216,15 +216,23 @@ message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
if
is_atom(Method) ->
atom_to_list(Method)++
- " to "++RequestURI++" ("++HTTPVersion++") not supported.";
+ " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported.";
is_list(Method) ->
Method++
- " to "++RequestURI++" ("++HTTPVersion++") not supported."
+ " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."
end;
message(503, String, _ConfigDB) ->
"This service in unavailable due to: "++String.
+maybe_encode(URI) ->
+ case lists:member($%, URI) of
+ true ->
+ URI;
+ false ->
+ http_uri:encode(URI)
+ end.
+
%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
convert_request_date([D,A,Y,DateType| Rest])->
@@ -381,16 +389,11 @@ month(12) -> "Dec".
%% decode_hex
-decode_hex([$%,Hex1,Hex2|Rest]) ->
- [hex2dec(Hex1)*16+hex2dec(Hex2)|decode_hex(Rest)];
-decode_hex([First|Rest]) ->
- [First|decode_hex(Rest)];
-decode_hex([]) ->
- [].
+decode_hex(URI) ->
+ http_uri:decode(URI).
-hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
-hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
-hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
+encode_hex(URI) ->
+ http_uri:encode(URI).
%% flatlength
flatlength(List) ->
@@ -411,7 +414,7 @@ split_path(Path) ->
case inets_regexp:match(Path,"[\?].*\$") of
%% A QUERY_STRING exists!
{match,Start,Length} ->
- {httpd_util:decode_hex(string:substr(Path,1,Start-1)),
+ {http_uri:decode(string:substr(Path,1,Start-1)),
string:substr(Path,Start,Length)};
%% A possible PATH_INFO exists!
nomatch ->
@@ -419,9 +422,9 @@ split_path(Path) ->
end.
split_path([],SoFar) ->
- {httpd_util:decode_hex(lists:reverse(SoFar)),[]};
+ {http_uri:decode(lists:reverse(SoFar)),[]};
split_path([$/|Rest],SoFar) ->
- Path=httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path=http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path,[$/|Rest]};
@@ -454,7 +457,7 @@ pathinfo_querystring([C|Rest], SoFar) ->
pathinfo_querystring(Rest, [C|SoFar]).
split_script_path([$?|QueryString], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$?|QueryString]};
@@ -464,7 +467,7 @@ split_script_path([$?|QueryString], SoFar) ->
not_a_script
end;
split_script_path([], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, []};
@@ -474,7 +477,7 @@ split_script_path([], SoFar) ->
not_a_script
end;
split_script_path([$/|Rest], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok, FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$/|Rest]};
@@ -608,9 +611,6 @@ hexlist_to_integer(List)->
%%----------------------------------------------------------------------
%%Converts an integer to an hexlist
%%----------------------------------------------------------------------
-encode_hex(Num)->
- integer_to_hexlist(Num).
-
integer_to_hexlist(Num) when is_integer(Num) ->
http_util:integer_to_hexlist(Num).
@@ -735,7 +735,6 @@ valid_accept_timeout(A) ->
valid_config(_) ->
ok.
-
%%----------------------------------------------------------------------
%% Enable debugging,
%%----------------------------------------------------------------------
diff --git a/lib/inets/src/http_server/mod_actions.erl b/lib/inets/src/http_server/mod_actions.erl
index d50ed4b16c..c3946ff9b4 100644
--- a/lib/inets/src/http_server/mod_actions.erl
+++ b/lib/inets/src/http_server/mod_actions.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-export([do/1,load/2, store/2]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%% do
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index 9c5a8cc1c6..0b9fe4cfe0 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -29,6 +29,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ALIAS").
diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl
index 07cafb4726..85a87ab884 100644
--- a/lib/inets/src/http_server/mod_auth.erl
+++ b/lib/inets/src/http_server/mod_auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,7 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"AUTH").
diff --git a/lib/inets/src/http_server/mod_auth_dets.erl b/lib/inets/src/http_server/mod_auth_dets.erl
index bc6c2b70a0..a48725d5d9 100644
--- a/lib/inets/src/http_server/mod_auth_dets.erl
+++ b/lib/inets/src/http_server/mod_auth_dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,7 @@
-export([store_directory_data/3]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-include("mod_auth.hrl").
store_directory_data(_Directory, DirData, Server_root) ->
diff --git a/lib/inets/src/http_server/mod_auth_plain.erl b/lib/inets/src/http_server/mod_auth_plain.erl
index d88859d28a..c0a83711ba 100644
--- a/lib/inets/src/http_server/mod_auth_plain.erl
+++ b/lib/inets/src/http_server/mod_auth_plain.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,8 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
+
-define(VMODULE,"AUTH_PLAIN").
diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl
index 5f9e59be9d..947273bd9e 100644
--- a/lib/inets/src/http_server/mod_auth_server.erl
+++ b/lib/inets/src/http_server/mod_auth_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl
index 33605b9698..c854166c29 100644
--- a/lib/inets/src/http_server/mod_cgi.erl
+++ b/lib/inets/src/http_server/mod_cgi.erl
@@ -27,6 +27,7 @@
-export([do/1, load/2, store/2]).
-include("http_internal.hrl").
+-include("httpd_internal.hrl").
-include("httpd.hrl").
-define(VMODULE,"CGI").
diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl
index cdc7cc01e4..d791ee28e9 100644
--- a/lib/inets/src/http_server/mod_dir.erl
+++ b/lib/inets/src/http_server/mod_dir.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,11 @@
%%
%%
-module(mod_dir).
--export([do/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
+
+-export([do/1]).
%% do
@@ -57,7 +59,7 @@ do_dir(Info) ->
case file:read_file_info(DefaultPath) of
{ok,FileInfo} when FileInfo#file_info.type == directory ->
DecodedRequestURI =
- httpd_util:decode_hex(Info#mod.request_uri),
+ http_uri:decode(Info#mod.request_uri),
?DEBUG("do_dir -> ~n"
" Path: ~p~n"
" DefaultPath: ~p~n"
diff --git a/lib/inets/src/http_server/mod_disk_log.erl b/lib/inets/src/http_server/mod_disk_log.erl
index 95e0d00c70..5a3766de66 100644
--- a/lib/inets/src/http_server/mod_disk_log.erl
+++ b/lib/inets/src/http_server/mod_disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(VMODULE,"DISK_LOG").
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%%%=========================================================================
%%% API
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index f7877aa9e2..929185a67a 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -30,6 +30,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ESI").
-define(DEFAULT_ERL_TIMEOUT,15000).
diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl
index 9fd1fcec47..5cb30e3d97 100644
--- a/lib/inets/src/http_server/mod_get.erl
+++ b/lib/inets/src/http_server/mod_get.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(mod_get).
-export([do/1]).
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%% do
do(Info) ->
diff --git a/lib/inets/src/http_server/mod_head.erl b/lib/inets/src/http_server/mod_head.erl
index 8b08d61651..c346fd4d23 100644
--- a/lib/inets/src/http_server/mod_head.erl
+++ b/lib/inets/src/http_server/mod_head.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl
index d8835198f5..e1f66d01c8 100644
--- a/lib/inets/src/http_server/mod_htaccess.erl
+++ b/lib/inets/src/http_server/mod_htaccess.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,7 @@
-export([do/1, load/2, store/2]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Public methods that interface the eswapi %%
diff --git a/lib/inets/src/http_server/mod_include.erl b/lib/inets/src/http_server/mod_include.erl
index 534eba8a36..35f45bdd33 100644
--- a/lib/inets/src/http_server/mod_include.erl
+++ b/lib/inets/src/http_server/mod_include.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-export([do/1,parse/2,config/6,include/6,echo/6,fsize/6,flastmod/6,exec/6]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-define(VMODULE,"INCLUDE").
@@ -186,9 +187,9 @@ document_uri(ConfigDB, RequestURI) ->
FileName = string:substr(Path,Start,Length),
case inets_regexp:match(VirtualPath, FileName++"\$") of
{match, _, _} ->
- httpd_util:decode_hex(VirtualPath)++AfterPath;
+ http_uri:decode(VirtualPath)++AfterPath;
nomatch ->
- string:strip(httpd_util:decode_hex(VirtualPath),right,$/)++
+ string:strip(http_uri:decode(VirtualPath),right,$/)++
"/"++FileName++AfterPath
end.
diff --git a/lib/inets/src/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl
index de24d5a569..c8a2ec0dc4 100644
--- a/lib/inets/src/http_server/mod_log.erl
+++ b/lib/inets/src/http_server/mod_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@
-export([do/1, load/2, store/2, remove/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-define(VMODULE,"LOG").
%%%=========================================================================
diff --git a/lib/inets/src/http_server/mod_range.erl b/lib/inets/src/http_server/mod_range.erl
index 0698fb9099..a0408cba79 100644
--- a/lib/inets/src/http_server/mod_range.erl
+++ b/lib/inets/src/http_server/mod_range.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(mod_range).
-export([do/1]).
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%% do
do(Info) ->
diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl
index 79e2e1bdba..5d5b60cdbd 100644
--- a/lib/inets/src/http_server/mod_responsecontrol.erl
+++ b/lib/inets/src/http_server/mod_responsecontrol.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-export([do/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
do(Info) ->
?DEBUG("do -> response_control",[]),
diff --git a/lib/inets/src/http_server/mod_security.erl b/lib/inets/src/http_server/mod_security.erl
index 95793e1cfb..41988732ad 100644
--- a/lib/inets/src/http_server/mod_security.erl
+++ b/lib/inets/src/http_server/mod_security.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,6 +32,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"SEC").
diff --git a/lib/inets/src/http_server/mod_security_server.erl b/lib/inets/src/http_server/mod_security_server.erl
index 58060686b3..784b3eba70 100644
--- a/lib/inets/src/http_server/mod_security_server.erl
+++ b/lib/inets/src/http_server/mod_security_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/http_server/mod_trace.erl b/lib/inets/src/http_server/mod_trace.erl
index df482228d8..7233925783 100644
--- a/lib/inets/src/http_server/mod_trace.erl
+++ b/lib/inets/src/http_server/mod_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 84d8c9278d..0194c65db9 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -18,6 +18,11 @@
{"%VSN%",
[
+ {"5.5",
+ [
+ {restart_application, inets}
+ ]
+ },
{"5.4",
[
{restart_application, inets}
@@ -29,6 +34,11 @@
[
{restart_application, inets}
]
+ },
+ {"5.4",
+ [
+ {restart_application, inets}
+ ]
}
]
}.
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index bb7f2186af..0492d33eab 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -288,16 +288,20 @@ release_spec: opt
$(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test
@for d in $(DATADIRS); do \
echo "installing data dir $$d"; \
- echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
- cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
- find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
- find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
- find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
- find $$d -name '*~' >> $$d/TAR.exclude2; \
- find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
- find $$d -name 'core' >> $$d/TAR.exclude2; \
- find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
- tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ if test -f $$d/TAR.exclude; then \
+ echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
+ cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
+ find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
+ find $$d -name '*~' >> $$d/TAR.exclude2; \
+ find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
+ find $$d -name 'core' >> $$d/TAR.exclude2; \
+ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
+ tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ else \
+ tar cf - $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ fi; \
done
release_tests_spec: opt
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 902e440c80..94d5a48ef6 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -254,9 +254,14 @@ init_per_testcase(Case, Timeout, Config) ->
[{watchdog, Dog}, {local_server, Server} | TmpConfig2]
end,
+ %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
+ %% ["localhost", ?IPV6_LOCAL_HOST]}}]),
+
httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
- ["localhost", ?IPV6_LOCAL_HOST]}}]),
- %% snmp:set_trace([gen_tcp, inet_tcp, prim_inet]),
+ ["localhost", ?IPV6_LOCAL_HOST]}},
+ {ipfamily, inet6fb4}]),
+
+ %% snmp:set_trace([gen_tcp]),
NewConfig.
@@ -471,7 +476,7 @@ http_relaxed(Config) when is_list(Config) ->
DummyServerPid ! stop,
ok = httpc:set_options([{ipv6, enabled}]),
- %% ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ %% ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -489,7 +494,7 @@ http_dummy_pipe(Config) when is_list(Config) ->
test_pipeline(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
http_inets_pipe(doc) ->
@@ -851,7 +856,7 @@ http_headers_dummy(Config) when is_list(Config) ->
], "text/plain", FooBar},
[], []),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -875,7 +880,7 @@ http_bad_response(Config) when is_list(Config) ->
test_server:format("Wrong Statusline: ~p~n", [Reason]),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1157,7 +1162,7 @@ http_redirect(Config) when is_list(Config) ->
tsp("http_redirect -> stop dummy server"),
DummyServerPid ! stop,
tsp("http_redirect -> reset ipfamily option (to inet6fb4)"),
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
tsp("http_redirect -> done"),
ok;
@@ -1181,7 +1186,7 @@ http_redirect_loop(Config) when is_list(Config) ->
{ok, {{_,300,_}, [_ | _], _}}
= httpc:request(get, {URL, []}, [], []),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
@@ -1215,7 +1220,7 @@ http_internal_server_error(Config) when is_list(Config) ->
ets:delete(unavailable),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1242,7 +1247,7 @@ http_userinfo(Config) when is_list(Config) ->
httpc:request(get, {URLUnAuth, []}, [], []),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1271,9 +1276,9 @@ http_cookie(Config) when is_list(Config) ->
ets:delete(cookie),
- ok = httpc:set_options([{cookies, disabled}, {ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{cookies, disabled}]),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
@@ -1643,7 +1648,7 @@ http_stream_once(Config) when is_list(Config) ->
p("http_stream_once -> stop dummy server", []),
DummyServerPid ! stop,
p("http_stream_once -> set ipfamily to inet6fb4", []),
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
p("http_stream_once -> done", []),
ok.
@@ -1847,7 +1852,7 @@ http_invalid_http(Config) when is_list(Config) ->
test_server:format("Parse error: ~p ~n", [Reason]),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1901,7 +1906,7 @@ transfer_encoding_otp_6807(Config) when is_list(Config) ->
"/capital_transfer_encoding.html",
{ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1933,7 +1938,7 @@ empty_response_header_otp_6830(Config) when is_list(Config) ->
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html",
{ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1950,7 +1955,7 @@ no_content_204_otp_6982(Config) when is_list(Config) ->
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html",
{ok, {{_,204,_}, [], []}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1968,7 +1973,7 @@ missing_CR_otp_7304(Config) when is_list(Config) ->
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html",
{ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1990,7 +1995,7 @@ otp_7883_1(Config) when is_list(Config) ->
{error, socket_closed_remotely} = httpc:request(URL),
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
otp_7883_2(doc) ->
@@ -2017,7 +2022,7 @@ otp_7883_2(Config) when is_list(Config) ->
end,
DummyServerPid ! stop,
- ok = httpc:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index f86c1fcb49..9ba2e73942 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,13 +25,16 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
+-define(URL_START, "http://localhost:").
+
all(doc) ->
["Basic test of httpd."];
all(suite) ->
[
uri_too_long_414,
- header_too_long_413
+ header_too_long_413,
+ escaped_url_in_error_body
].
%%--------------------------------------------------------------------
@@ -131,6 +134,31 @@ header_too_long_413(Config) when is_list(Config) ->
{version, "HTTP/1.1"}]),
inets:stop(httpd, Pid).
+escaped_url_in_error_body(doc) ->
+ ["Test Url-encoding see OTP-8940"];
+escaped_url_in_error_body(suite) ->
+ [];
+escaped_url_in_error_body(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+ Path = "/<b>this_is_bold<b>",
+ URL = ?URL_START ++ integer_to_list(Port) ++ Path,
+ EscapedPath = http_uri:encode(Path),
+ {ok, {404, Body}} = httpc:request(get, {URL, []},
+ [{url_encode, true}],
+ [{version, "HTTP/1.0"}, {full_result, false}]),
+ EscapedPath = find_URL_path(string:tokens(Body, " ")),
+ {ok, {404, Body1}} = httpc:request(get, {URL, []}, [],
+ [{version, "HTTP/1.0"}, {full_result, false}]),
+ EscapedPath = find_URL_path(string:tokens(Body1, " ")),
+ inets:stop(httpd, Pid).
-
-
+find_URL_path([]) ->
+ "";
+find_URL_path(["URL", URL | _]) ->
+ URL;
+find_URL_path([_ | Rest]) ->
+ find_URL_path(Rest).
diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl
index d580c6c4c5..2c9c687c91 100644
--- a/lib/inets/test/inets_appup_test.erl
+++ b/lib/inets/test/inets_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,11 +18,12 @@
%%
%%
%%----------------------------------------------------------------------
-%% Purpose: Verify the application specifics of the Megaco application
+%% Purpose: Verify the application specifics of the Inets application
%%----------------------------------------------------------------------
-module(inets_appup_test).
-compile(export_all).
+-compile({no_auto_import,[error/1]}).
-include("inets_test_lib.hrl").
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 5eff9e4e3f..f462290a99 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -1,5 +1,24 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
APPLICATION = inets
-INETS_VSN = 5.5
+INETS_VSN = 5.5.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index c8953c6818..fadb993ed9 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -164,7 +164,7 @@ validate_signature(OtpCert, DerCert, Key, KeyParams,
verify_fun(OtpCert, {bad_cert, invalid_signature}, UserState, VerifyFun)
end.
%%--------------------------------------------------------------------
--spec validate_names(#'OTPCertificate'{}, list(), list(),
+-spec validate_names(#'OTPCertificate'{}, no_constraints | list(), list(),
term(), term(), fun())-> term().
%%
%% Description: Validate Subject Alternative Name.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 095a6ff0e0..30398df9cc 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -213,10 +213,13 @@ decrypt_private(CipherText,
crypto:mpint(D)], Padding).
%%--------------------------------------------------------------------
--spec decrypt_public(CipherText :: binary(), rsa_public_key()) ->
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) ->
PlainText :: binary().
--spec decrypt_public(CipherText :: binary(), rsa_public_key(),
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key(),
public_crypt_options()) -> PlainText :: binary().
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
%% Description: Public key decryption using the public key.
%%--------------------------------------------------------------------
@@ -232,10 +235,14 @@ decrypt_public(CipherText,#'RSAPrivateKey'{modulus = N, publicExponent = E},
decrypt_public(CipherText, N,E, Options).
%%--------------------------------------------------------------------
--spec encrypt_public(PlainText :: binary(), rsa_public_key()) ->
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key()) ->
CipherText :: binary().
--spec encrypt_public(PlainText :: binary(), rsa_public_key(),
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key(),
public_crypt_options()) -> CipherText :: binary().
+
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
%% Description: Public key encryption using the public key.
%%--------------------------------------------------------------------
@@ -280,8 +287,8 @@ encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N,
sign(PlainText, DigestType, #'RSAPrivateKey'{modulus = N, publicExponent = E,
privateExponent = D})
when is_binary(PlainText),
- DigestType == md5;
- DigestType == sha ->
+ (DigestType == md5 orelse
+ DigestType == sha) ->
crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E),
crypto:mpint(N),
@@ -571,11 +578,9 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
pubkey_cert:prepare_for_next_cert(OtpCert, ValidationState).
-sized_binary(Binary) when is_binary(Binary) ->
+sized_binary(Binary) ->
Size = size(Binary),
- <<?UINT32(Size), Binary/binary>>;
-sized_binary(List) ->
- sized_binary(list_to_binary(List)).
+ <<?UINT32(Size), Binary/binary>>.
%%--------------------------------------------------------------------
%%% Deprecated functions
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 81e01f3a02..88cfbcf2b6 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -249,10 +249,8 @@ sign_verify(Config) when is_list(Config) ->
true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}),
%% RSA sign
- Msg0 = lists:duplicate(5, "Foo bar 100"),
- Msg = list_to_binary(Msg0),
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
- RSASign = public_key:sign(Msg0, sha, PrivateRSA),
RSASign = public_key:sign(Msg, sha, PrivateRSA),
true = public_key:verify(Msg, sha, RSASign, PublicRSA),
false = public_key:verify(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA),
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 511f1e0bb2..ec272379bb 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -218,12 +218,12 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
application is encountered. Additionally it will be called
when a certificate is considered valid by the path validation
to allow access to each certificate in the path to the user
- application. Note that the it will differentiate between
- the peer certificate and CA certificates by using valid_peer
- or valid as the second argument to the verify fun.
- See
- <seealso marker="public_key:application">public_key(3)</seealso>
- for definition of #'OTPCertificate'{} and #'Extension'{}.</p>
+ application. Note that the it will differentiate between the
+ peer certificate and CA certificates by using valid_peer or
+ valid as the second argument to the verify fun. See <seealso
+ marker="public_key:cert_records">the public_key User's
+ Guide</seealso> for definition of #'OTPCertificate'{} and
+ #'Extension'{}.</p>
<p>If the verify callback fun returns {fail, Reason}, the
verification process is immediately stopped and an alert is
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 5571fb01f6..8c0c2bfa5d 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -28,7 +28,6 @@
-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
--include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
-export([trusted_cert_and_path/2,
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 1c8bbbaf06..72f02a4362 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -29,7 +29,6 @@
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
-include("ssl_alert.hrl").
--include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, suite_definition/1,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index ce90d22c09..6c9ac65b64 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -29,7 +29,6 @@
-behaviour(gen_fsm).
--include("ssl_debug.hrl").
-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include("ssl_record.hrl").
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 7082a23fd0..c7a1c4965d 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -28,7 +28,6 @@
-include("ssl_cipher.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
--include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
-export([master_secret/4, client_hello/5, server_hello/4, hello/4,
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 74fba3786c..68a7802ef2 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -26,6 +26,13 @@
-ifndef(ssl_handshake).
-define(ssl_handshake, true).
+-include_lib("public_key/include/public_key.hrl").
+
+-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
+-type public_key() :: #'RSAPublicKey'{} | integer().
+-type public_key_params() :: #'Dss-Parms'{} | term().
+-type public_key_info() :: {algo_oid(), public_key(), public_key_params()}.
+
-record(session, {
session_id,
peer_certificate,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 4148032cb7..43a85c2d9d 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -25,6 +25,24 @@
-include_lib("public_key/include/public_key.hrl").
+-type reason() :: term().
+-type reply() :: term().
+-type msg() :: term().
+-type from() :: term().
+-type host() :: string() | tuple().
+-type port_num() :: integer().
+-type session_id() :: 0 | binary().
+-type tls_version() :: {integer(), integer()}.
+-type tls_atom_version() :: sslv3 | tlsv1.
+-type cache_ref() :: term().
+-type certdb_ref() :: term().
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
+-type der_cert() :: binary().
+-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}.
+-type issuer() :: tuple().
+-type serialnumber() :: integer().
+-type cert_key() :: {reference(), integer(), issuer()}.
+
%% basic binary constructors
-define(BOOLEAN(X), X:8/unsigned-big-integer).
-define(BYTE(X), X:8/unsigned-big-integer).
@@ -93,28 +111,6 @@
active = true
}).
--type reason() :: term().
--type reply() :: term().
--type msg() :: term().
--type from() :: term().
--type host() :: string() | tuple().
--type port_num() :: integer().
--type session_id() :: 0 | binary().
--type tls_version() :: {integer(), integer()}.
--type tls_atom_version() :: sslv3 | tlsv1.
--type cache_ref() :: term().
--type certdb_ref() :: term().
--type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
--type oid() :: tuple().
--type public_key() :: #'RSAPublicKey'{} | integer().
--type public_key_params() :: #'Dss-Parms'{} | term().
--type public_key_info() :: {oid(), public_key(), public_key_params()}.
--type der_cert() :: binary().
--type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}.
--type issuer() :: tuple().
--type serialnumber() :: integer().
--type cert_key() :: {reference(), integer(), issuer()}.
-
-endif. % -ifdef(ssl_internal).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index e7a2d8ecf1..f1c0073965 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -30,7 +30,6 @@
-include("ssl_alert.hrl").
-include("ssl_handshake.hrl").
-include("ssl_cipher.hrl").
--include("ssl_debug.hrl").
%% Connection state handling
-export([init_connection_states/1,
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index 18c3f4ed3d..c49f9f1e6d 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -25,7 +25,6 @@
-module(ssl_ssl3).
-include("ssl_cipher.hrl").
--include("ssl_debug.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl"). % MD5 and SHA
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index 88394b23ba..3784483e9c 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -27,7 +27,6 @@
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
--include("ssl_debug.hrl").
-export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
setup_keys/6, suites/0]).
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ea84b3c9d1..8f9554f3ce 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -31,6 +31,7 @@
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
+-define(LONG_TIMEOUT, 600000).
-define(EXPIRE, 10).
-define(SLEEP, 500).
@@ -45,7 +46,7 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?TIMEOUT *2),
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
crypto:start(),
application:start(public_key),
ssl:start(),
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 7a441e4599..0f39759d97 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -28,6 +28,7 @@
-define(SLEEP, 500).
-define(TIMEOUT, 60000).
+-define(LONG_TIMEOUT, 600000).
-behaviour(ssl_session_cache_api).
%% For the session cache tests
@@ -45,7 +46,7 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?TIMEOUT *2),
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
crypto:start(),
application:start(public_key),
ssl:start(),
@@ -119,7 +120,7 @@ end_per_testcase(session_cache_process_list, Config) ->
end_per_testcase(session_cache_process_mnesia, Config) ->
application:unset_env(ssl, session_cb),
application:unset_env(ssl, session_cb_init_args),
- mnesia:stop(),
+ mnesia:kill(),
ssl:stop(),
ssl:start(),
end_per_testcase(default_action, Config);
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 55a0100b1e..afedeaf099 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -27,6 +27,7 @@
-include("test_server.hrl").
-define(TIMEOUT, 120000).
+-define(LONG_TIMEOUT, 600000).
-define(SLEEP, 1000).
-define(OPENSSL_RENEGOTIATE, "r\n").
-define(OPENSSL_QUIT, "Q\n").
@@ -44,7 +45,7 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- Dog = ssl_test_lib:timetrap(?TIMEOUT *2),
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
case os:find_executable("openssl") of
false ->
{skip, "Openssl not found"};
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 2d8eb08105..b621b17441 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -5231,8 +5231,6 @@ smp_select_delete(Config) when is_list(Config) ->
types(doc) -> ["Test different types"];
types(Config) when is_list(Config) ->
init_externals(),
- io:format("ets:i() before test:\n",[]),
- ets:i(), % SVERK: trouble shooting
repeat_for_opts(types_do,[[set,ordered_set],compressed]).
types_do(Opts) ->
@@ -5328,6 +5326,10 @@ my_tab_to_list(Ts,Key, Acc) ->
my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]).
etsmem() ->
+ AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size),
+ ets:info(T,memory),ets:info(T,type)}
+ end, ets:all()),
+ Mem =
{try erlang:memory(ets) catch error:notsup -> notsup end,
case erlang:system_info({allocator,ets_alloc}) of
false -> undefined;
@@ -5346,12 +5348,13 @@ etsmem() ->
{value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
{Bl0+Bl,BlSz0+BlSz}
end, {0,0}, MSBCS)
- end}.
+ end},
+ {Mem,AllTabs}.
-verify_etsmem(MemInfo) ->
+verify_etsmem({MemInfo,AllTabs}) ->
wait_for_test_procs(),
case etsmem() of
- MemInfo ->
+ {MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
case MemInfo of
{ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
@@ -5360,13 +5363,15 @@ verify_etsmem(MemInfo) ->
_ ->
ok
end;
- Other ->
+ {MemInfo2, AllTabs2} ->
io:format("Expected: ~p", [MemInfo]),
- io:format("Actual: ~p", [Other]),
- ets:i(),
+ io:format("Actual: ~p", [MemInfo2]),
+ io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
+ io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
?t:fail()
end.
+
start_loopers(N, Prio, Fun, State) ->
lists:map(fun (_) ->
my_spawn_opt(fun () -> looper(Fun, State) end,
diff --git a/make/otp.mk.in b/make/otp.mk.in
index 6ae7c5b456..bf287be416 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -209,6 +209,8 @@ MAN9DIR = $(DOCDIR)/man9
TEXDIR = .
+SPECDIR = $(DOCDIR)/specs
+
# HTML & GIF files that always are generated and must be delivered
SGML_COLL_FILES = $(SGML_APPLICATION_FILES) $(SGML_PART_FILES)
XML_COLL_FILES = $(XML_APPLICATION_FILES) $(XML_PART_FILES)
@@ -237,41 +239,52 @@ FOP = @FOP@
DOCGEN=$(ERL_TOP)/lib/erl_docgen
+SPECS_ESRC = ../../src
+# Extract specifications and types from Erlang source files (-spec, -type)
+$(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl
+ escript $(DOCGEN)/priv/bin/specs_gen.escript $(SPECS_FLAGS) -o$(dir $@) $<
-$(MAN1DIR)/%.1:: %.xml
+$(MAN1DIR)/%.1: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-$(MAN2DIR)/%.2:: %.xml
+$(MAN2DIR)/%.2: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-$(MAN3DIR)/%.3:: %.xml
+ifneq ($(wildcard $(SPECDIR)),)
+$(MAN3DIR)/%.3: %.xml $(SPECDIR)/specs_%.xml
+ date=`date +"%B %e %Y"`; \
+ specs_file=`pwd`/$(SPECDIR)/specs_$*.xml; \
+ xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --stringparam specs_file "$$specs_file" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
+else
+$(MAN3DIR)/%.3: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
+endif
# left for compatability
-$(MAN4DIR)/%.4:: %.xml
+$(MAN4DIR)/%.4: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-$(MAN4DIR)/%.5:: %.xml
+$(MAN4DIR)/%.5: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
# left for compatability
-$(MAN6DIR)/%.6:: %_app.xml
+$(MAN6DIR)/%.6: %_app.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-$(MAN6DIR)/%.7:: %_app.xml
+$(MAN6DIR)/%.7: %_app.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
-$(MAN9DIR)/%.9:: %.xml
+$(MAN9DIR)/%.9: %.xml
date=`date +"%B %e %Y"`; \
xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk
index 7d433e738c..8058e634d4 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -21,13 +21,30 @@
# Targets for the new documentation support
# ----------------------------------------------------
+ifneq ($(TOP_SPECS_FILE),)
+TOP_SPECS_PARAM = --stringparam specs_file "`pwd`/$(TOP_SPECS_FILE)"
+endif
+
+MOD2APP = $(ERL_TOP)/make/$(TARGET)/mod2app.xml
+ifneq ($(wildcard $(MOD2APP)),)
+MOD2APP_PARAM = --stringparam mod2app_file "$(MOD2APP)"
+endif
+
ifeq ($(TOPDOC),)
-$(HTMLDIR)/index.html: $(XML_FILES)
+$(HTMLDIR)/index.html: $(XML_FILES) $(SPECS_FILES)
date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --noout --stringparam outdir $(HTMLDIR) --stringparam docgen "$(DOCGEN)" --stringparam topdocdir "$(TOPDOCDIR)" \
- --stringparam pdfdir "$(PDFDIR)" \
- --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_html.xsl book.xml
+ $(XSLTPROC) --noout \
+ --stringparam outdir $(HTMLDIR) \
+ --stringparam docgen "$(DOCGEN)" \
+ --stringparam topdocdir "$(TOPDOCDIR)" \
+ --stringparam pdfdir "$(PDFDIR)" \
+ --xinclude $(TOP_SPECS_PARAM) $(MOD2APP_PARAM) \
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ -path $(DOCGEN)/priv/docbuilder_dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
+ $(DOCGEN)/priv/xsl/db_html.xsl book.xml
endif
$(HTMLDIR)/users_guide.html: $(XML_FILES)
@@ -37,14 +54,17 @@ $(HTMLDIR)/users_guide.html: $(XML_FILES)
--stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude \
-path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_html.xsl book.xml
-
-%.fo: $(XML_FILES)
+%.fo: $(XML_FILES) $(SPECS_FILES)
date=`date +"%B %e %Y"`; \
- $(XSLTPROC) --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" \
- --stringparam appver "$(VSN)" --xinclude \
- -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_html_entities $(DOCGEN)/priv/xsl/db_pdf.xsl book.xml > $@
-
-
+ $(XSLTPROC) \
+ --stringparam docgen "$(DOCGEN)" \
+ --stringparam gendate "$$date" \
+ --stringparam appname "$(APPLICATION)" \
+ --stringparam appver "$(VSN)" \
+ --xinclude $(TOP_SPECS_PARAM) \
+ -path $(DOCGEN)/priv/docbuilder_dtd \
+ -path $(DOCGEN)/priv/dtd_html_entities \
+ $(DOCGEN)/priv/xsl/db_pdf.xsl book.xml > $@
# ------------------------------------------------------------------------
# The following targets just exist in the documentation directory