aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl_nif.xml7
-rw-r--r--erts/emulator/beam/break.c23
-rw-r--r--erts/emulator/beam/erl_bif_port.c21
-rw-r--r--erts/emulator/beam/erl_gc.c48
-rw-r--r--erts/emulator/beam/erl_port.h1
-rw-r--r--erts/emulator/beam/erl_process.c77
-rw-r--r--erts/emulator/beam/erl_process.h2
-rw-r--r--erts/emulator/beam/erl_process_dump.c72
-rw-r--r--erts/emulator/beam/erl_trace.c30
-rw-r--r--erts/emulator/beam/io.c101
-rw-r--r--erts/test/z_SUITE.erl2
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl18
-rw-r--r--lib/crypto/test/engine_SUITE.erl7
-rw-r--r--lib/diameter/doc/src/diameter.xml14
-rw-r--r--lib/diameter/src/base/diameter_reg.erl17
-rw-r--r--lib/diameter/src/base/diameter_service.erl22
-rw-r--r--lib/diameter/src/diameter.appup.src15
-rw-r--r--lib/diameter/vsn.mk4
-rw-r--r--lib/kernel/src/os.erl8
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl8
-rw-r--r--lib/kernel/test/inet_SUITE.erl8
-rw-r--r--lib/kernel/test/os_SUITE.erl4
-rw-r--r--lib/observer/src/cdv_port_cb.erl17
-rw-r--r--lib/observer/src/cdv_proc_cb.erl4
-rw-r--r--lib/observer/src/cdv_sched_cb.erl18
-rw-r--r--lib/observer/src/crashdump_viewer.erl188
-rw-r--r--lib/observer/src/crashdump_viewer.hrl16
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl4
-rw-r--r--lib/snmp/doc/src/snmp_impl_example_agent.xml10
-rw-r--r--lib/ssh/doc/src/notes.xml34
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml7
-rw-r--r--lib/ssh/src/ssh.erl3
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_cli.erl17
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl46
-rw-r--r--lib/ssh/src/ssh_options.erl2
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl44
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl1
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl95
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl99
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl25
-rw-r--r--lib/ssh/test/ssh_test_lib.erl6
-rw-r--r--lib/ssh/test/ssh_test_lib.hrl7
-rw-r--r--lib/ssh/vsn.mk1
-rw-r--r--lib/ssl/doc/src/notes.xml15
-rw-r--r--lib/ssl/src/ssl_cipher.erl11
-rw-r--r--lib/ssl/test/ssl_engine_SUITE.erl39
-rw-r--r--lib/stdlib/doc/src/timer.xml2
-rw-r--r--lib/stdlib/src/string.erl8
-rw-r--r--lib/stdlib/test/re_SUITE.erl15
-rw-r--r--lib/stdlib/test/string_SUITE.erl4
-rw-r--r--lib/tools/emacs/Makefile20
-rw-r--r--lib/tools/emacs/erlang-skels.el248
-rw-r--r--lib/tools/emacs/erlang.el127
-rw-r--r--lib/tools/emacs/test.erl.indented784
-rw-r--r--lib/tools/emacs/test.erl.orig784
-rw-r--r--lib/tools/test/emacs_SUITE.erl73
-rw-r--r--lib/tools/test/emacs_SUITE_data/comments25
-rw-r--r--lib/tools/test/emacs_SUITE_data/comprehensions47
-rw-r--r--lib/tools/test/emacs_SUITE_data/funcs174
-rw-r--r--lib/tools/test/emacs_SUITE_data/highlight78
-rw-r--r--lib/tools/test/emacs_SUITE_data/icr157
-rw-r--r--lib/tools/test/emacs_SUITE_data/macros31
-rw-r--r--lib/tools/test/emacs_SUITE_data/records35
-rw-r--r--lib/tools/test/emacs_SUITE_data/terms174
-rw-r--r--lib/tools/test/emacs_SUITE_data/try_catch166
-rw-r--r--lib/tools/test/emacs_SUITE_data/type_specs110
-rw-r--r--otp_versions.table2
-rw-r--r--system/doc/efficiency_guide/advanced.xml2
69 files changed, 2200 insertions, 2086 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index ef3cdb89e9..b9c2e70b57 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -3044,9 +3044,10 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) {
</name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc>
- <p>Initializes the pid variable <c>*pid</c> to represent the
- calling process.</p>
- <p>Returns <c>pid</c>.</p>
+ <p>Initializes the <seealso marker="#ErlNifPid"><c>ErlNifPid</c></seealso>
+ variable at <c>*pid</c> to represent the calling process.</p>
+ <p>Returns <c>pid</c> if successful, or NULL if <c>caller_env</c> is not
+ a <seealso marker="#ErlNifEnv">process-bound environment</seealso>.</p>
</desc>
</func>
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index e4c2801ea2..5ace997344 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -336,6 +336,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
erts_print(to, to_arg, "OldHeap unused: %bpu\n",
(OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
+ erts_print(to, to_arg, "BinVHeap: %b64u\n", p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p));
+ erts_print(to, to_arg, "BinVHeap unused: %b64u\n",
+ BIN_VHEAP_SZ(p) - p->off_heap.overhead);
+ erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
+ BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
@@ -905,6 +911,23 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
erts_cbprintf(to, to_arg, "** crashed **\n"));
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+ for (i = 0; i < erts_no_dirty_cpu_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_CPU_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_cpu_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_CPU_RUNQ);
+
+ for (i = 0; i < erts_no_dirty_io_schedulers; i++) {
+ ERTS_SYS_TRY_CATCH(
+ erts_print_scheduler_info(to, to_arg, ERTS_DIRTY_IO_SCHEDULER_IX(i)),
+ erts_cbprintf(to, to_arg, "** crashed **\n"));
+ }
+ erts_cbprintf(to, to_arg, "=dirty_io_run_queue\n");
+ erts_print_run_queue_info(to, to_arg, ERTS_DIRTY_IO_RUNQ);
+#endif /* ERTS_DIRTY_SCHEDULERS */
#endif
#ifdef ERTS_SMP
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index ff03151619..d05507118e 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -647,6 +647,27 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
BIF_RET(res);
}
+Eterm erts_port_data_read(Port* prt)
+{
+ Eterm res;
+ erts_aint_t data;
+
+ data = erts_smp_atomic_read_ddrb(&prt->data);
+ if (data == (erts_aint_t)NULL)
+ return am_undefined; /* Port terminated by racing thread */
+
+ if ((data & 0x3) != 0) {
+ res = (Eterm) (UWord) data;
+ ASSERT(is_immed(res));
+ }
+ else {
+ ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
+ res = pdhp->data;
+ }
+ return res;
+}
+
+
/*
* Open a port. Most of the work is not done here but rather in
* the file io.c.
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 6a87136463..154b9b8dac 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -116,6 +116,7 @@ typedef struct {
static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
static void cleanup_rootset(Rootset *rootset);
static Eterm *full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -142,7 +143,7 @@ static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
char* src, Uint src_size);
static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* heap, Eterm* htop, Eterm* objv, int nobj);
+ Eterm* htop);
static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
@@ -939,6 +940,7 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
htop = heap;
htop = full_sweep_heaps(p,
+ ERTS_INVALID_HFRAG_PTR,
1,
heap,
htop,
@@ -1503,18 +1505,22 @@ do_minor(Process *p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
+ n = setup_rootset(p, objv, nobj, &rootset);
+ roots = rootset.roots;
+
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
/*
* Move heap frags that we know are completely live
* directly into the new young heap generation.
*/
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
}
- n = setup_rootset(p, objv, nobj, &rootset);
- roots = rootset.roots;
-
GENSWEEP_NSTACK(p, old_htop, n_htop);
while (n--) {
Eterm* g_ptr = roots->v;
@@ -1757,16 +1763,8 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
sizeof(Eterm)*new_sz);
- if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
- /*
- * Move heap frags that we know are completely live
- * directly into the heap.
- */
- n_htop = collect_live_heap_frags(p, live_hf_end, n_heap, n_htop,
- objv, nobj);
- }
-
- n_htop = full_sweep_heaps(p, 0, n_heap, n_htop, oh, oh_size, objv, nobj);
+ n_htop = full_sweep_heaps(p, live_hf_end, 0, n_heap, n_htop, oh, oh_size,
+ objv, nobj);
/* Move the stack to the end of the heap */
stk_sz = HEAP_END(p) - p->stop;
@@ -1813,6 +1811,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end,
static Eterm *
full_sweep_heaps(Process *p,
+ ErlHeapFragment *live_hf_end,
int hibernate,
Eterm *n_heap, Eterm* n_htop,
char *oh, Uint oh_size,
@@ -1829,6 +1828,19 @@ full_sweep_heaps(Process *p,
n = setup_rootset(p, objv, nobj, &rootset);
+ /*
+ * All allocations done. Start defile heap with move markers.
+ * A crash dump due to allocation failure above will see a healthy heap.
+ */
+
+ if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
+ /*
+ * Move heap frags that we know are completely live
+ * directly into the heap.
+ */
+ n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
+ }
+
#ifdef HIPE
if (hibernate)
hipe_empty_nstack(p);
@@ -2337,9 +2349,7 @@ move_one_area(Eterm* n_htop, char* src, Uint src_size)
*/
static Eterm*
-collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
- Eterm* n_hstart, Eterm* n_htop,
- Eterm* objv, int nobj)
+collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop)
{
ErlHeapFragment* qb;
char* frag_begin;
diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h
index b64de624dd..c30bbbca06 100644
--- a/erts/emulator/beam/erl_port.h
+++ b/erts/emulator/beam/erl_port.h
@@ -187,6 +187,7 @@ void erts_init_port_data(Port *);
void erts_cleanup_port_data(Port *);
Uint erts_port_data_size(Port *);
ErlOffHeap *erts_port_data_offheap(Port *);
+Eterm erts_port_data_read(Port* prt);
#define ERTS_PORT_GET_CONNECTED(PRT) \
((Eterm) erts_atomic_read_nob(&(PRT)->connected))
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 63e9275ac1..9b22f024b0 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -1609,6 +1609,20 @@ erts_proclist_destroy(ErtsProcList *plp)
proclist_destroy(plp);
}
+void
+erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList *plp)
+{
+ ErtsProcList *first = plp;
+
+ while (plp) {
+ erts_print(to, to_arg, "%T", plp->pid);
+ plp = plp->next;
+ if (plp == first)
+ break;
+ }
+ erts_print(to, to_arg, "\n");
+}
+
void *
erts_psd_set_init(Process *p, int ix, void *data)
{
@@ -14259,16 +14273,35 @@ stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
return yreg;
}
+static void print_current_process_info(fmtfn_t, void *to_arg, ErtsSchedulerData*);
+
/*
* Print scheduler information
*/
void
-erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
+erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp)
+{
int i;
erts_aint32_t flg;
- Process *p;
- erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ switch (esdp->type) {
+ case ERTS_SCHED_NORMAL:
+ erts_print(to, to_arg, "=scheduler:%u\n", esdp->no);
+ break;
+#ifdef ERTS_DIRTY_SCHEDULERS
+ case ERTS_SCHED_DIRTY_CPU:
+ erts_print(to, to_arg, "=dirty_cpu_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers));
+ break;
+ case ERTS_SCHED_DIRTY_IO:
+ erts_print(to, to_arg, "=dirty_io_scheduler:%u\n",
+ (esdp->dirty_no + erts_no_schedulers + erts_no_dirty_cpu_schedulers));
+ break;
+#endif
+ default:
+ erts_print(to, to_arg, "=unknown_scheduler_type:%u\n", esdp->type);
+ break;
+ }
#ifdef ERTS_SMP
flg = erts_smp_atomic32_read_dirty(&esdp->ssi->flags);
@@ -14316,10 +14349,24 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
erts_print(to, to_arg, "\n");
- erts_print(to, to_arg, "Current Port: ");
- if (esdp->current_port)
- erts_print(to, to_arg, "%T", esdp->current_port->common.id);
- erts_print(to, to_arg, "\n");
+ if (esdp->type == ERTS_SCHED_NORMAL) {
+ erts_print(to, to_arg, "Current Port: ");
+ if (esdp->current_port)
+ erts_print(to, to_arg, "%T", esdp->current_port->common.id);
+ erts_print(to, to_arg, "\n");
+
+ erts_print_run_queue_info(to, to_arg, esdp->run_queue);
+ }
+
+ /* This *MUST* to be the last information in scheduler block */
+ print_current_process_info(to, to_arg, esdp);
+}
+
+void erts_print_run_queue_info(fmtfn_t to, void *to_arg,
+ ErtsRunQueue *run_queue)
+{
+ erts_aint32_t flg;
+ int i;
for (i = 0; i < ERTS_NO_PROC_PRIO_LEVELS; i++) {
erts_print(to, to_arg, "Run Queue ");
@@ -14341,12 +14388,12 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
break;
}
erts_print(to, to_arg, "Length: %d\n",
- erts_smp_atomic32_read_dirty(&esdp->run_queue->procs.prio_info[i].len));
+ erts_smp_atomic32_read_dirty(&run_queue->procs.prio_info[i].len));
}
erts_print(to, to_arg, "Run Queue Port Length: %d\n",
- erts_smp_atomic32_read_dirty(&esdp->run_queue->ports.info.len));
+ erts_smp_atomic32_read_dirty(&run_queue->ports.info.len));
- flg = erts_smp_atomic32_read_dirty(&esdp->run_queue->flags);
+ flg = erts_smp_atomic32_read_dirty(&run_queue->flags);
erts_print(to, to_arg, "Run Queue Flags: ");
for (i = 0; i < ERTS_RUNQ_FLG_MAX && flg; i++) {
erts_aint32_t chk = (1 << i);
@@ -14413,9 +14460,15 @@ erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp) {
}
}
erts_print(to, to_arg, "\n");
+}
+
+
+static void print_current_process_info(fmtfn_t to, void *to_arg,
+ ErtsSchedulerData* esdp)
+{
+ Process *p = esdp->current_process;
+ erts_aint32_t flg;
- /* This *MUST* to be the last information in scheduler block */
- p = esdp->current_process;
erts_print(to, to_arg, "Current Process: ");
if (esdp->current_process && !(ERTS_TRACE_FLAGS(p) & F_SENSITIVE)) {
flg = erts_smp_atomic32_read_dirty(&p->state);
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 639818c20c..5cac939771 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1614,6 +1614,7 @@ Uint64 erts_step_proc_interval(void);
ErtsProcList *erts_proclist_create(Process *);
ErtsProcList *erts_proclist_copy(ErtsProcList *);
void erts_proclist_destroy(ErtsProcList *);
+void erts_proclist_dump(fmtfn_t to, void *to_arg, ErtsProcList*);
ERTS_GLB_INLINE int erts_proclist_same(ErtsProcList *, Process *);
ERTS_GLB_INLINE void erts_proclist_store_first(ErtsProcList **, ErtsProcList *);
@@ -1868,6 +1869,7 @@ void erts_stack_dump(fmtfn_t to, void *to_arg, Process *);
void erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *);
void erts_program_counter_info(fmtfn_t to, void *to_arg, Process *);
void erts_print_scheduler_info(fmtfn_t to, void *to_arg, ErtsSchedulerData *esdp);
+void erts_print_run_queue_info(fmtfn_t, void *to_arg, ErtsRunQueue*);
void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg);
diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c
index e0b795fbf3..00fe30dfcb 100644
--- a/erts/emulator/beam/erl_process_dump.c
+++ b/erts/emulator/beam/erl_process_dump.c
@@ -78,8 +78,17 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg)
Process *p = erts_pix2proc(i);
if (p && p->i != ENULL) {
erts_aint32_t state = erts_smp_atomic32_read_acqb(&p->state);
- if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_GC)))
- dump_process_info(to, to_arg, p);
+ if (state & ERTS_PSFLG_EXITING)
+ continue;
+ if (state & ERTS_PSFLG_GC) {
+ ErtsSchedulerData *sdp = erts_get_scheduler_data();
+ if (!sdp || p != sdp->current_process)
+ continue;
+
+ /* We want to dump the garbing process that caused the dump */
+ }
+
+ dump_process_info(to, to_arg, p);
}
}
@@ -135,9 +144,12 @@ dump_process_info(fmtfn_t to, void *to_arg, Process *p)
ErtsMessage* mp;
int yreg = -1;
+ if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE)
+ return;
+
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0 && p->msg.first) {
+ if (p->msg.first) {
erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
for (mp = p->msg.first; mp != NULL; mp = mp->next) {
Eterm mesg = ERL_MESSAGE_TERM(mp);
@@ -152,38 +164,34 @@ dump_process_info(fmtfn_t to, void *to_arg, Process *p)
}
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- if (p->dictionary) {
- erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
- erts_deep_dictionary_dump(to, to_arg,
- p->dictionary, dump_element_nl);
- }
+ if (p->dictionary) {
+ erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
+ erts_deep_dictionary_dump(to, to_arg,
+ p->dictionary, dump_element_nl);
}
- if ((ERTS_TRACE_FLAGS(p) & F_SENSITIVE) == 0) {
- erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- yreg = stack_element_dump(to, to_arg, sp, yreg);
- }
+ erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ yreg = stack_element_dump(to, to_arg, sp, yreg);
+ }
- erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
- for (sp = p->stop; sp < STACK_START(p); sp++) {
- Eterm term = *sp;
-
- if (!is_catch(term) && !is_CP(term)) {
- heap_dump(to, to_arg, term);
- }
- }
- for (mp = p->msg.first; mp != NULL; mp = mp->next) {
- Eterm mesg = ERL_MESSAGE_TERM(mp);
- if (is_value(mesg))
- heap_dump(to, to_arg, mesg);
- mesg = ERL_MESSAGE_TOKEN(mp);
- heap_dump(to, to_arg, mesg);
- }
- if (p->dictionary) {
- erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
- }
+ erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
+ for (sp = p->stop; sp < STACK_START(p); sp++) {
+ Eterm term = *sp;
+
+ if (!is_catch(term) && !is_CP(term)) {
+ heap_dump(to, to_arg, term);
+ }
+ }
+ for (mp = p->msg.first; mp != NULL; mp = mp->next) {
+ Eterm mesg = ERL_MESSAGE_TERM(mp);
+ if (is_value(mesg))
+ heap_dump(to, to_arg, mesg);
+ mesg = ERL_MESSAGE_TOKEN(mp);
+ heap_dump(to, to_arg, mesg);
+ }
+ if (p->dictionary) {
+ erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
}
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index db7d0ac449..9fda4ff6ff 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -3009,24 +3009,28 @@ is_tracer_enabled(Process* c_p, ErtsProcLocks c_p_locks,
ASSERT(0);
}
- /* Only remove tracer on self() and ports */
+ /* Only remove tracer on (self() or ports) AND we are on a normal scheduler */
if (is_internal_port(t_p->id) || (c_p && c_p->common.id == t_p->id)) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
ErtsProcLocks c_p_xlocks = 0;
- if (is_internal_pid(t_p->id)) {
- ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
- if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
- c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
- if (erts_smp_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
- erts_smp_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ if (esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)) {
+ if (is_internal_pid(t_p->id)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN);
+ if (c_p_locks != ERTS_PROC_LOCKS_ALL) {
+ c_p_xlocks = ~c_p_locks & ERTS_PROC_LOCKS_ALL;
+ if (erts_smp_proc_trylock(c_p, c_p_xlocks) == EBUSY) {
+ erts_smp_proc_unlock(c_p, c_p_locks & ~ERTS_PROC_LOCK_MAIN);
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
+ }
}
}
- }
- erts_tracer_replace(t_p, erts_tracer_nil);
- t_p->trace_flags &= ~TRACEE_FLAGS;
- if (c_p_xlocks)
- erts_smp_proc_unlock(c_p, c_p_xlocks);
+ erts_tracer_replace(t_p, erts_tracer_nil);
+ t_p->trace_flags &= ~TRACEE_FLAGS;
+
+ if (c_p_xlocks)
+ erts_smp_proc_unlock(c_p, c_p_xlocks);
+ }
}
return 0;
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 3a5ddde5f4..13b8125a99 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -5151,6 +5151,93 @@ static void prt_one_lnk(ErtsLink *lnk, void *vprtd)
erts_print(prtd->to, prtd->arg, "%T", lnk->pid);
}
+static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state)
+{
+ erts_aint32_t rest;
+ int unknown = 0;
+ char delim = ' ';
+
+ erts_print(to, arg, "State:");
+
+ rest = state;
+ while (rest) {
+ erts_aint32_t chk = (rest ^ (rest-1)) & rest; /* lowest set bit */
+ char* s;
+
+ rest &= ~chk;
+ switch (chk) {
+ case ERTS_PORT_SFLG_CONNECTED: s = "CONNECTED"; break;
+ case ERTS_PORT_SFLG_EXITING: s = "EXITING"; break;
+ case ERTS_PORT_SFLG_DISTRIBUTION: s = "DISTR"; break;
+ case ERTS_PORT_SFLG_BINARY_IO: s = "BINARY_IO"; break;
+ case ERTS_PORT_SFLG_SOFT_EOF: s = "SOFT_EOF"; break;
+ case ERTS_PORT_SFLG_CLOSING: s = "CLOSING"; break;
+ case ERTS_PORT_SFLG_SEND_CLOSED: s = "SEND_CLOSED"; break;
+ case ERTS_PORT_SFLG_LINEBUF_IO: s = "LINEBUF_IO"; break;
+ case ERTS_PORT_SFLG_FREE: s = "FREE"; break;
+ case ERTS_PORT_SFLG_INITIALIZING: s = "INITIALIZING"; break;
+ case ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK: s = "PORT_LOCK"; break;
+ case ERTS_PORT_SFLG_INVALID: s = "INVALID"; break;
+ case ERTS_PORT_SFLG_HALT: s = "HALT"; break;
+#ifdef DEBUG
+ case ERTS_PORT_SFLG_PORT_DEBUG: s = "DEBUG"; break;
+#endif
+ default:
+ unknown = 1;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown || !state)
+ erts_print(to, arg, "%c0x%x\n", delim, state);
+ else
+ erts_print(to, arg, "\n");
+}
+
+static void dump_port_task_flags(fmtfn_t to, void *arg, Port* p)
+{
+ erts_aint32_t flags = erts_smp_atomic32_read_nob(&p->sched.flags);
+ erts_aint32_t unknown = 0;
+ char delim = ' ';
+
+ if (!flags)
+ return;
+
+ erts_print(to, arg, "Task Flags:");
+
+ while (flags) {
+ erts_aint32_t chk = (flags ^ (flags-1)) & flags; /* lowest set bit */
+ char* s;
+
+ flags &= ~chk;
+ switch (chk) {
+ case ERTS_PTS_FLG_IN_RUNQ: s = "IN_RUNQ"; break;
+ case ERTS_PTS_FLG_EXEC: s = "EXEC"; break;
+ case ERTS_PTS_FLG_HAVE_TASKS: s = "HAVE_TASKS"; break;
+ case ERTS_PTS_FLG_EXIT: s = "EXIT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT: s = "BUSY_PORT"; break;
+ case ERTS_PTS_FLG_BUSY_PORT_Q: s = "BUSY_Q"; break;
+ case ERTS_PTS_FLG_CHK_UNSET_BUSY_PORT_Q: s = "CHK_UNSET_BUSY_Q"; break;
+ case ERTS_PTS_FLG_HAVE_BUSY_TASKS: s = "BUSY_TASKS"; break;
+ case ERTS_PTS_FLG_HAVE_NS_TASKS: s = "NS_TASKS"; break;
+ case ERTS_PTS_FLG_PARALLELISM: s = "PARALLELISM"; break;
+ case ERTS_PTS_FLG_FORCE_SCHED: s = "FORCE_SCHED"; break;
+ case ERTS_PTS_FLG_EXITING: s = "EXITING"; break;
+ case ERTS_PTS_FLG_EXEC_IMM: s = "EXEC_IMM"; break;
+ default:
+ unknown |= chk;
+ continue;
+ }
+ erts_print(to, arg, "%c%s", delim, s);
+ delim = '|';
+ }
+ if (unknown)
+ erts_print(to, arg, "%cUNKNOWN(0x%x)\n", delim, unknown);
+ else
+ erts_print(to, arg, "\n");
+}
+
void
print_port_info(Port *p, fmtfn_t to, void *arg)
{
@@ -5160,6 +5247,8 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
return;
erts_print(to, arg, "=port:%T\n", p->common.id);
+ dump_port_state(to, arg, state);
+ dump_port_task_flags(to, arg, p);
erts_print(to, arg, "Slot: %d\n", internal_port_index(p->common.id));
if (state & ERTS_PORT_SFLG_CONNECTED) {
erts_print(to, arg, "Connected: %T", ERTS_PORT_GET_CONNECTED(p));
@@ -5182,6 +5271,10 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd);
erts_print(to, arg, "\n");
}
+ if (p->suspended) {
+ erts_print(to, arg, "Suspended: ");
+ erts_proclist_dump(to, arg, p->suspended);
+ }
if (p->common.u.alive.reg != NULL)
erts_print(to, arg, "Registered as: %T\n", p->common.u.alive.reg->name);
@@ -5199,6 +5292,14 @@ print_port_info(Port *p, fmtfn_t to, void *arg)
} else {
erts_print(to, arg, "Port controls linked-in driver: %s\n",p->name);
}
+ erts_print(to, arg, "Input: %beu\n", p->bytes_in);
+ erts_print(to, arg, "Output: %beu\n", p->bytes_out);
+ erts_print(to, arg, "Queue: %beu\n", erts_ioq_size(&p->ioq));
+ {
+ Eterm port_data = erts_port_data_read(p);
+ if (port_data != am_undefined)
+ erts_print(to, arg, "Port Data: %T\n", port_data);
+ }
}
void
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 229a47e1aa..ed42ea5b40 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -40,7 +40,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {minutes, 5}}].
+ {timetrap, {minutes, 10}}].
all() ->
[core_files].
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 82e9326294..c09b0f47d1 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,14 +47,20 @@ dialyzer_suppressions(#gen{erule=per,aligned=Aligned}) ->
false -> uper;
true -> per
end,
- case asn1ct_func:is_used({Mod,complete,1}) of
+ suppress({Mod,complete,1}),
+ suppress({per_common,to_bitstring,2}),
+ emit([" ok.",nl]).
+
+suppress({M,F,A}=MFA) ->
+ case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
- emit([" _ = complete(Arg),",nl])
- end,
- emit([" ok.",nl]).
-
+ Args =
+ [lists:concat(["element(",I,", Arg)"])
+ || I <- lists:seq(1, A)],
+ emit([" ",{call,M,F,Args},com,nl])
+ end.
gen_encode(Erules,Type) when is_record(Type,typedef) ->
gen_encode_user(Erules,Type).
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index f206f967c7..f410542f72 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -72,7 +72,12 @@ groups() ->
init_per_suite(Config) ->
try crypto:start() of
ok ->
- Config;
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ Config
+ end;
{error,{already_started,crypto}} ->
Config
catch _:_ ->
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 6b84b22eb5..6bc7d147c0 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1865,8 +1865,8 @@ An example return value with for a client service with Origin-Host
{raddr,{127,0,0,1}},
{rport,3868},
{reuseaddr,true}]}]},
- {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
- {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {watchdog,{&lt;0.66.0>,-576460736368485571,okay}},
+ {peer,{&lt;0.67.0>,-576460736357885808}},
{apps,[{0,common}]},
{caps,[{origin_host,{"client.example.com","server.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1946,8 +1946,8 @@ connection might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {accept,[[{watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1976,7 +1976,7 @@ connection might look as follows.</p>
{send_max,148},
{send_avg,87},
{send_pend,0}]}]}],
- [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ [{watchdog,{&lt;0.72.0>,-576460638229717546,initial}}]]},
{statistics,[{{{0,280,0},recv},7},
{{{0,280,1},send},7},
{{{0,280,0},recv,{'Result-Code',2001}},7},
@@ -2024,8 +2024,8 @@ A return value for the server above might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 5b7cfab31a..c1762a07e3 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -246,8 +246,11 @@ handle_call({add, Uniq, Key}, {Pid, _}, S) ->
handle_call({remove, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- ets:delete_object(?TABLE, Rec),
- {reply, true, notify(remove, Rec, S)};
+ {reply, true, try
+ notify(remove, Rec, S)
+ after
+ ets:delete_object(?TABLE, Rec)
+ end};
handle_call({wait, Pat}, {Pid, _} = From, S) ->
NS = add_monitor(Pid, S),
@@ -370,10 +373,12 @@ send({_,_} = From, add, Rec) ->
down(Pid, #state{monitors = Ps} = S) ->
Recs = match('_', Pid),
- ets:match_delete(?TABLE, {'_', Pid}),
- lists:foldl(fun(R,NS) -> notify(remove, R, NS) end,
- flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
- Recs).
+ Acc0 = flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
+ try
+ lists:foldl(fun(R,NS) -> notify(remove, R, NS) end, Acc0, Recs)
+ after
+ ets:match_delete(?TABLE, {'_', Pid})
+ end.
%% flush/3
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 31dd92f878..cbe66ef27a 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -151,7 +151,7 @@
apps :: match([{0..16#FFFFFFFF, diameter:app_alias()}] %% {Id, Alias}
| [diameter:app_alias()]), %% remote
caps :: match(#diameter_caps{}),
- started = diameter_lib:now(), %% at process start or sharing
+ started = diameter_lib:now(), %% at connection_up
watchdog :: match(pid() %% key into watchdogT
| undefined)}). %% undefined if remote
@@ -554,15 +554,25 @@ terminate(Reason, #state{service_name = Name, local = {PeerT, _, _}} = S) ->
%% wait for watchdog state changes to take care of if. That this
%% takes place after deleting the state entry ensures that the
%% resulting failover by request processes accomplishes nothing.
- ets:foldl(fun(#peer{pid = TPid}, _) ->
- diameter_traffic:peer_down(TPid)
- end,
- ok,
- PeerT),
+ ets:foldl(fun peer_down/2, ok, PeerT),
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
+%% peer_down/1
+%%
+%% Entries with watchdog state SUSPECT are already down: ignore the
+%% expected failure. This assumes the current implementation, but
+%% double the number of lookups (in the typical case) could be the
+%% greater evil if there are many peer connections.
+
+peer_down(#peer{pid = TPid}, _) ->
+ try
+ diameter_traffic:peer_down(TPid)
+ catch
+ error: {badmatch, []} -> ok
+ end.
+
%% ---------------------------------------------------------------------------
%% # code_change/3
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 7da59f8b25..05a8c9378e 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -54,10 +54,10 @@
{"1.12.1", [{restart_application, diameter}]}, %% 19.1
{"1.12.2", [{restart_application, diameter}]}, %% 19.3
{"2.0", [{restart_application, diameter}]}, %% 20.0
- {"2.1", [{load_module, diameter_gen}, %% 20.1
- {update, diameter_reg, {advanced, "2.1"}}]},
- {"2.1.1", [{load_module, diameter_gen}]}, %% 20.1.2
- {"2.1.2", []} %% 20.1.3
+ {"2.1", [{restart_application, diameter}]}, %% 20.1
+ {"2.1.1", [{restart_application, diameter}]}, %% 20.1.2
+ {"2.1.2", [{restart_application, diameter}]}, %% 20.1.3
+ {"2.1.3", [{restart_application, diameter}]} %% 20.2
],
[
{"0.9", [{restart_application, diameter}]},
@@ -94,7 +94,8 @@
{"1.12.2", [{restart_application, diameter}]},
{"2.0", [{restart_application, diameter}]},
{"2.1", [{restart_application, diameter}]},
- {"2.1.1", [{load_module, diameter_gen}]},
- {"2.1.2", []}
+ {"2.1.1", [{restart_application, diameter}]},
+ {"2.1.2", [{restart_application, diameter}]},
+ {"2.1.3", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 0c852d75cd..b0fb4ada28 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2017. All Rights Reserved.
+# Copyright Ericsson AB 2010-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 2.1.3
+DIAMETER_VSN = 2.1.4
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index ae188ae932..a4b4f798f9 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -319,16 +319,16 @@ get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
iolist_to_binary(Sofar)
end.
-eot(_Bs, <<>>, _Size, _Max) ->
+eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max ->
more;
+eot(Bs, <<>>, Size, Max) ->
+ binary:part(Bs, {0, Max - Size});
eot(Bs, Eot, Size, Max) ->
case binary:match(Bs, Eot) of
- nomatch when Size + byte_size(Bs) < Max ->
- more;
{Pos, _} when Size + Pos < Max ->
binary:part(Bs,{0, Pos});
_ ->
- binary:part(Bs,{0, Max - Size})
+ eot(Bs, <<>>, Size, Max)
end.
%% When port_close returns we know that all the
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index bbfaa9d147..e34b4d77d2 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -95,7 +95,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
+init_per_testcase(TC, Config) when TC == hostnames;
+ TC == nodenames ->
+ file:make_dir("hostnames_nodedir"),
+ file:write_file("hostnames_nodedir/ignore_core_files",""),
+ Config;
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Config.
@@ -251,7 +255,7 @@ test_node(Name, Illigal) ->
end,
net_kernel:monitor_nodes(true),
BinCommand = unicode:characters_to_binary(Command, utf8),
- Prt = open_port({spawn, BinCommand}, [stream]),
+ Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
Node = list_to_atom(Name),
receive
{nodeup, Node} ->
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 3b502be8b8..ba0d075ef2 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1083,11 +1083,9 @@ ifaddrs([{If,Opts}|IOs]) ->
#ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}),
case F of
{flags,Flags} ->
- case lists:member(up, Flags) of
- true ->
- Ifopts#ifopts.addrs;
- false ->
- []
+ case lists:member(running, Flags) of
+ true -> Ifopts#ifopts.addrs;
+ false -> []
end ++ ifaddrs(IOs);
undefined ->
ifaddrs(IOs)
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 079b9790f6..a0bcde68db 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -324,8 +324,8 @@ max_size_command(_Config) ->
Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
32768 = length(Res32768),
- ResHello = os:cmd("echo hello", #{ max_size => 20 }),
- 6 = length(ResHello).
+ ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })),
+ 5 = length(ResHello).
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/observer/src/cdv_port_cb.erl b/lib/observer/src/cdv_port_cb.erl
index b5cbe8132d..6bb8f07a74 100644
--- a/lib/observer/src/cdv_port_cb.erl
+++ b/lib/observer/src/cdv_port_cb.erl
@@ -34,7 +34,8 @@
-define(COL_CONN, ?COL_ID+1).
-define(COL_NAME, ?COL_CONN+1).
-define(COL_CTRL, ?COL_NAME+1).
--define(COL_SLOT, ?COL_CTRL+1).
+-define(COL_QUEUE, ?COL_CTRL+1).
+-define(COL_SLOT, ?COL_QUEUE+1).
@@ -44,6 +45,7 @@ col_to_elem(?COL_ID) -> #port.id;
col_to_elem(?COL_CONN) -> #port.connected;
col_to_elem(?COL_NAME) -> #port.name;
col_to_elem(?COL_CTRL) -> #port.controls;
+col_to_elem(?COL_QUEUE) -> #port.queue;
col_to_elem(?COL_SLOT) -> #port.slot.
col_spec() ->
@@ -51,6 +53,7 @@ col_spec() ->
{"Connected", ?wxLIST_FORMAT_LEFT, 120},
{"Name", ?wxLIST_FORMAT_LEFT, 150},
{"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Queue", ?wxLIST_FORMAT_RIGHT, 100},
{"Slot", ?wxLIST_FORMAT_RIGHT, 50}].
get_info(_) ->
@@ -96,9 +99,17 @@ format(D) ->
info_fields() ->
[{"Overview",
[{"Name", name},
+ {"State", state},
+ {"Task Flags", task_flags},
{"Connected", {click,connected}},
{"Slot", slot},
- {"Controls", controls}]},
+ {"Controls", controls},
+ {"Input bytes", input},
+ {"Output bytes", output},
+ {"Queue bytes", queue},
+ {"Port data", port_data}]},
{scroll_boxes,
[{"Links",1,{click,links}},
- {"Monitors",1,{click,monitors}}]}].
+ {"Monitors",1,{click,monitors}},
+ {"Suspended",1,{click,suspended}}
+ ]}].
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index f10650bbb7..0ea23dd7cb 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -149,6 +149,10 @@ info_fields() ->
{"Old Heap", old_heap},
{"Heap Unused", heap_unused},
{"Old Heap Unused", old_heap_unused},
+ {"Binary vheap", bin_vheap},
+ {"Old Binary vheap", old_bin_vheap},
+ {"Binary vheap unused", bin_vheap_unused},
+ {"Old Binary vheap unused", old_bin_vheap_unused},
{"Number of Heap Fragements", num_heap_frag},
{"Heap Fragment Data",heap_frag_data},
{"New Heap Start", new_heap_start},
diff --git a/lib/observer/src/cdv_sched_cb.erl b/lib/observer/src/cdv_sched_cb.erl
index 192aaf31a7..d2696a276f 100644
--- a/lib/observer/src/cdv_sched_cb.erl
+++ b/lib/observer/src/cdv_sched_cb.erl
@@ -31,7 +31,8 @@
%% Columns
-define(COL_ID, 0).
--define(COL_PROC, ?COL_ID+1).
+-define(COL_TYPE, ?COL_ID+1).
+-define(COL_PROC, ?COL_TYPE+1).
-define(COL_PORT, ?COL_PROC+1).
-define(COL_RQL, ?COL_PORT+1).
-define(COL_PQL, ?COL_RQL+1).
@@ -39,6 +40,7 @@
%% Callbacks for cdv_virtual_list_wx
col_to_elem(id) -> col_to_elem(?COL_ID);
col_to_elem(?COL_ID) -> #sched.name;
+col_to_elem(?COL_TYPE) -> #sched.type;
col_to_elem(?COL_PROC) -> #sched.process;
col_to_elem(?COL_PORT) -> #sched.port;
col_to_elem(?COL_RQL) -> #sched.run_q;
@@ -46,6 +48,7 @@ col_to_elem(?COL_PQL) -> #sched.port_q.
col_spec() ->
[{"Id", ?wxLIST_FORMAT_RIGHT, 50},
+ {"Type", ?wxLIST_FORMAT_CENTER, 100},
{"Current Process", ?wxLIST_FORMAT_CENTER, 130},
{"Current Port", ?wxLIST_FORMAT_CENTER, 130},
{"Run Queue Length", ?wxLIST_FORMAT_RIGHT, 180},
@@ -73,7 +76,8 @@ detail_pages() ->
[{"Scheduler Information", fun init_gen_page/2}].
init_gen_page(Parent, Info0) ->
- Fields = info_fields(),
+ Type = proplists:get_value(type, Info0),
+ Fields = info_fields(Type),
Details = proplists:get_value(details, Info0),
Info = if is_map(Details) -> Info0 ++ maps:to_list(Details);
true -> Info0
@@ -81,15 +85,16 @@ init_gen_page(Parent, Info0) ->
cdv_info_wx:start_link(Parent,{Fields,Info,[]}).
%%% Internal
-info_fields() ->
+info_fields(Type) ->
[{"Scheduler Overview",
[{"Id", id},
+ {"Type", type},
{"Current Process",process},
{"Current Port", port},
{"Sleep Info Flags", sleep_info},
{"Sleep Aux Work", sleep_aux}
]},
- {"Run Queues",
+ {run_queues_header(Type),
[{"Flags", runq_flags},
{"Priority Max Length", runq_max},
{"Priority High Length", runq_high},
@@ -116,3 +121,8 @@ info_fields() ->
{" ", {currp_stack, 11}}
]}
].
+
+run_queues_header(normal) ->
+ "Run Queues";
+run_queues_header(DirtyX) ->
+ "Run Queues (common for all '" ++ atom_to_list(DirtyX) ++ "' schedulers)".
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 861b592439..d2a175d52d 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -116,6 +116,10 @@
-define(allocator,allocator).
-define(atoms,atoms).
-define(binary,binary).
+-define(dirty_cpu_scheduler,dirty_cpu_scheduler).
+-define(dirty_cpu_run_queue,dirty_cpu_run_queue).
+-define(dirty_io_scheduler,dirty_io_scheduler).
+-define(dirty_io_run_queue,dirty_io_run_queue).
-define(ende,ende).
-define(erl_crash_dump,erl_crash_dump).
-define(ets,ets).
@@ -1222,6 +1226,18 @@ all_procinfo(Fd,Fun,Proc,WS,LineHead) ->
"OldHeap unused" ->
Bytes = list_to_integer(bytes(Fd))*WS,
get_procinfo(Fd,Fun,Proc#proc{old_heap_unused=Bytes},WS);
+ "BinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap=Bytes},WS);
+ "OldBinVHeap" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap=Bytes},WS);
+ "BinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{bin_vheap_unused=Bytes},WS);
+ "OldBinVHeap unused" ->
+ Bytes = list_to_integer(bytes(Fd))*WS,
+ get_procinfo(Fd,Fun,Proc#proc{old_bin_vheap_unused=Bytes},WS);
"New heap start" ->
get_procinfo(Fd,Fun,Proc#proc{new_heap_start=bytes(Fd)},WS);
"New heap top" ->
@@ -1632,6 +1648,10 @@ port_to_tuple("#Port<"++Port) ->
get_portinfo(Fd,Port) ->
case line_head(Fd) of
+ "State" ->
+ get_portinfo(Fd,Port#port{state=bytes(Fd)});
+ "Task Flags" ->
+ get_portinfo(Fd,Port#port{task_flags=bytes(Fd)});
"Slot" ->
%% stored as integer so we can sort on it
get_portinfo(Fd,Port#port{slot=list_to_integer(bytes(Fd))});
@@ -1656,6 +1676,10 @@ get_portinfo(Fd,Port) ->
{Pid,Pid++" ("++Ref++")"}
end || Mon <- Monitors0],
get_portinfo(Fd,Port#port{monitors=Monitors});
+ "Suspended" ->
+ Pids = split_pid_list_no_space(bytes(Fd)),
+ Suspended = [{Pid,Pid} || Pid <- Pids],
+ get_portinfo(Fd,Port#port{suspended=Suspended});
"Port controls linked-in driver" ->
Str = lists:flatten(["Linked in driver: " | string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
@@ -1671,6 +1695,15 @@ get_portinfo(Fd,Port) ->
"Port is UNIX fd not opened by emulator" ->
Str = lists:flatten(["UNIX fd not opened by emulator: "| string(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
+ "Input" ->
+ get_portinfo(Fd,Port#port{input=list_to_integer(bytes(Fd))});
+ "Output" ->
+ get_portinfo(Fd,Port#port{output=list_to_integer(bytes(Fd))});
+ "Queue" ->
+ get_portinfo(Fd,Port#port{queue=list_to_integer(bytes(Fd))});
+ "Port Data" ->
+ get_portinfo(Fd,Port#port{port_data=string(Fd)});
+
"=" ++ _next_tag ->
Port;
Other ->
@@ -2501,73 +2534,142 @@ get_indextableinfo1(Fd,IndexTable) ->
%%-----------------------------------------------------------------
%% Page with scheduler table information
schedulers(File) ->
- case lookup_index(?scheduler) of
- [] ->
- [];
- Schedulers ->
- Fd = open(File),
- R = lists:map(fun({Name,Start}) ->
- get_schedulerinfo(Fd,Name,Start)
- end,
- Schedulers),
- close(Fd),
- R
- end.
+ Fd = open(File),
-get_schedulerinfo(Fd,Name,Start) ->
+ Schds0 = case lookup_index(?scheduler) of
+ [] ->
+ [];
+ Normals ->
+ [{Normals, #sched{type=normal}}]
+ end,
+ Schds1 = case lookup_index(?dirty_cpu_scheduler) of
+ [] ->
+ Schds0;
+ DirtyCpus ->
+ [{DirtyCpus, get_dirty_runqueue(Fd, ?dirty_cpu_run_queue)}
+ | Schds0]
+ end,
+ Schds2 = case lookup_index(?dirty_io_scheduler) of
+ [] ->
+ Schds1;
+ DirtyIos ->
+ [{DirtyIos, get_dirty_runqueue(Fd, ?dirty_io_run_queue)}
+ | Schds1]
+ end,
+
+ R = schedulers1(Fd, Schds2, []),
+ close(Fd),
+ R.
+
+schedulers1(_Fd, [], Acc) ->
+ Acc;
+schedulers1(Fd, [{Scheds,Sched0} | Tail], Acc0) ->
+ Acc1 = lists:foldl(fun({Name,Start}, AccIn) ->
+ [get_schedulerinfo(Fd,Name,Start,Sched0) | AccIn]
+ end,
+ Acc0,
+ Scheds),
+ schedulers1(Fd, Tail, Acc1).
+
+get_schedulerinfo(Fd,Name,Start,Sched0) ->
pos_bof(Fd,Start),
- get_schedulerinfo1(Fd,#sched{name=Name}).
+ get_schedulerinfo1(Fd,Sched0#sched{name=list_to_integer(Name)}).
-get_schedulerinfo1(Fd,Sched=#sched{details=Ds}) ->
+sched_type(?dirty_cpu_run_queue) -> dirty_cpu;
+sched_type(?dirty_io_run_queue) -> dirty_io.
+
+get_schedulerinfo1(Fd, Sched) ->
+ case get_schedulerinfo2(Fd, Sched) of
+ {more, Sched2} ->
+ get_schedulerinfo1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_schedulerinfo2(Fd, Sched=#sched{details=Ds}) ->
case line_head(Fd) of
"Current Process" ->
- get_schedulerinfo1(Fd,Sched#sched{process=bytes(Fd, "None")});
+ {more, Sched#sched{process=bytes(Fd, "None")}};
"Current Port" ->
- get_schedulerinfo1(Fd,Sched#sched{port=bytes(Fd, "None")});
+ {more, Sched#sched{port=bytes(Fd, "None")}};
+
+ "Scheduler Sleep Info Flags" ->
+ {more, Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}}};
+ "Scheduler Sleep Info Aux Work" ->
+ {more, Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}}};
+
+ "Current Process State" ->
+ {more, Sched#sched{details=Ds#{currp_state=>bytes(Fd)}}};
+ "Current Process Internal State" ->
+ {more, Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}}};
+ "Current Process Program counter" ->
+ {more, Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}}};
+ "Current Process CP" ->
+ {more, Sched#sched{details=Ds#{currp_cp=>string(Fd)}}};
+ "Current Process Limited Stack Trace" ->
+ %% If there shall be last in scheduler information block
+ {done, Sched#sched{details=get_limited_stack(Fd, 0, Ds)}};
+
+ "=" ++ _next_tag ->
+ {done, Sched};
+
+ Other ->
+ case Sched#sched.type of
+ normal ->
+ get_runqueue_info2(Fd, Other, Sched);
+ _ ->
+ unexpected(Fd,Other,"dirty scheduler information"),
+ {done, Sched}
+ end
+ end.
+
+get_dirty_runqueue(Fd, Tag) ->
+ case lookup_index(Tag) of
+ [{_, Start}] ->
+ pos_bof(Fd,Start),
+ get_runqueue_info1(Fd,#sched{type=sched_type(Tag)});
+ [] ->
+ #sched{}
+ end.
+
+get_runqueue_info1(Fd, Sched) ->
+ case get_runqueue_info2(Fd, line_head(Fd), Sched) of
+ {more, Sched2} ->
+ get_runqueue_info1(Fd, Sched2);
+ {done, Sched2} ->
+ Sched2
+ end.
+
+get_runqueue_info2(Fd, LineHead, Sched=#sched{details=Ds}) ->
+ case LineHead of
"Run Queue Max Length" ->
RQMax = list_to_integer(bytes(Fd)),
RQ = RQMax + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_max=>RQMax}}};
"Run Queue High Length" ->
RQHigh = list_to_integer(bytes(Fd)),
RQ = RQHigh + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_high=>RQHigh}}};
"Run Queue Normal Length" ->
RQNorm = list_to_integer(bytes(Fd)),
RQ = RQNorm + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_norm=>RQNorm}}};
"Run Queue Low Length" ->
RQLow = list_to_integer(bytes(Fd)),
RQ = RQLow + Sched#sched.run_q,
- get_schedulerinfo1(Fd,Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}});
+ {more, Sched#sched{run_q=RQ, details=Ds#{runq_low=>RQLow}}};
"Run Queue Port Length" ->
RQ = list_to_integer(bytes(Fd)),
- get_schedulerinfo1(Fd,Sched#sched{port_q=RQ});
-
- "Scheduler Sleep Info Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_info=>bytes(Fd, "None")}});
- "Scheduler Sleep Info Aux Work" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{sleep_aux=>bytes(Fd, "None")}});
+ {more, Sched#sched{port_q=RQ}};
"Run Queue Flags" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}});
+ {more, Sched#sched{details=Ds#{runq_flags=>bytes(Fd, "None")}}};
- "Current Process State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_state=>bytes(Fd)}});
- "Current Process Internal State" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_int_state=>bytes(Fd)}});
- "Current Process Program counter" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_prg_cnt=>string(Fd)}});
- "Current Process CP" ->
- get_schedulerinfo1(Fd,Sched#sched{details=Ds#{currp_cp=>string(Fd)}});
- "Current Process Limited Stack Trace" ->
- %% If there shall be last in scheduler information block
- Sched#sched{details=get_limited_stack(Fd, 0, Ds)};
"=" ++ _next_tag ->
- Sched;
+ {done, Sched};
Other ->
unexpected(Fd,Other,"scheduler information"),
- Sched
+ {done, Sched}
end.
get_limited_stack(Fd, N, Ds) ->
@@ -2998,6 +3100,10 @@ tag_to_atom("allocated_areas") -> ?allocated_areas;
tag_to_atom("allocator") -> ?allocator;
tag_to_atom("atoms") -> ?atoms;
tag_to_atom("binary") -> ?binary;
+tag_to_atom("dirty_cpu_scheduler") -> ?dirty_cpu_scheduler;
+tag_to_atom("dirty_cpu_run_queue") -> ?dirty_cpu_run_queue;
+tag_to_atom("dirty_io_scheduler") -> ?dirty_io_scheduler;
+tag_to_atom("dirty_io_run_queue") -> ?dirty_io_run_queue;
tag_to_atom("end") -> ?ende;
tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
tag_to_atom("ets") -> ?ets;
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 6a93a089fd..252e19379d 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -80,6 +80,10 @@
old_heap,
heap_unused,
old_heap_unused,
+ bin_vheap,
+ old_bin_vheap,
+ bin_vheap_unused,
+ old_bin_vheap_unused,
new_heap_start,
new_heap_top,
stack_top,
@@ -95,19 +99,27 @@
-record(port,
{id,
+ state,
+ task_flags=0,
slot,
connected,
links,
name,
monitors,
- controls}).
+ suspended,
+ controls,
+ input,
+ output,
+ queue,
+ port_data}).
-record(sched,
{name,
+ type,
process,
port,
run_q=0,
- port_q=0,
+ port_q,
details=#{}
}).
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 32773a779e..41ca3f3ce9 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -820,10 +820,10 @@ dump_with_size_limit_reached(DataDir,Rel,DumpName,Max) ->
"-env ERL_CRASH_DUMP_BYTES " ++
integer_to_list(Bytes)),
{ok,#file_info{size=Size}} = file:read_file_info(CD),
- if Size < Bytes ->
+ if Size =< Bytes ->
%% This means that the dump was actually smaller than the
%% randomly selected truncation size, so we'll just do it
- %% again with a smaller numer
+ %% again with a smaller number
ok = file:delete(CD),
dump_with_size_limit_reached(DataDir,Rel,DumpName,Size-3);
true ->
diff --git a/lib/snmp/doc/src/snmp_impl_example_agent.xml b/lib/snmp/doc/src/snmp_impl_example_agent.xml
index a86006a0a7..e576fa51f3 100644
--- a/lib/snmp/doc/src/snmp_impl_example_agent.xml
+++ b/lib/snmp/doc/src/snmp_impl_example_agent.xml
@@ -47,6 +47,7 @@
EX1-MIB DEFINITIONS ::= BEGIN
IMPORTS
+ experimental FROM RFC1155-SMI
RowStatus FROM STANDARD-MIB
DisplayString FROM RFC1213-MIB
OBJECT-TYPE FROM RFC-1212
@@ -81,7 +82,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
FriendsEntry ::=
SEQUENCE {
- fIndex
+ fIndex
INTEGER,
fName
DisplayString,
@@ -105,6 +106,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Name of friend"
::= { friendsEntry 2 }
+
fAddress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-write
@@ -112,6 +114,7 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"Address of friend"
::= { friendsEntry 3 }
+
fStatus OBJECT-TYPE
SYNTAX RowStatus
ACCESS read-write
@@ -119,12 +122,13 @@ EX1-MIB DEFINITIONS ::= BEGIN
DESCRIPTION
"The status of this conceptual row."
::= { friendsEntry 4 }
+
fTrap TRAP-TYPE
ENTERPRISE example1
VARIABLES { myName, fIndex }
DESCRIPTION
- "This trap is sent when something happens to
- the friend specified by fIndex."
+ "This trap is sent when something happens to
+ the friend specified by fIndex."
::= 1
END
</code>
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index c559da911b..5fc5ebda24 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -49,7 +49,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -96,7 +95,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -126,7 +124,6 @@
</section>
<section><title>Ssh 4.6.2</title>
-
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
@@ -406,6 +403,22 @@
</section>
+
+<section><title>Ssh 4.4.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
<section><title>Ssh 4.4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -776,6 +789,21 @@
</section>
+<section><title>Ssh 4.2.2.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Default exec is disabled when a user-defined shell is
+ enabled.</p>
+ <p>
+ Own Id: OTP-14881</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
<section><title>Ssh 4.2.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index ed7fbf9cf3..129426a6d5 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -464,11 +464,16 @@
<v>FileInfo = record()</v>
</type>
<desc>
- <p>Returns a <c><![CDATA[file_info]]></c> record from the file specified by
+ <p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
<seealso marker="kernel:file#read_file_info-2">file:read_file_info/2</seealso>
for information about the record.
</p>
+ <p>
+ Depending on the underlying OS:es links might be followed and info on the final file, directory
+ etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ on how to get information on links instead.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 032d87bdad..25d537c624 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -184,7 +184,6 @@ channel_info(ConnectionRef, ChannelId, Options) ->
daemon(Port) ->
daemon(Port, []).
-
daemon(Socket, UserOptions) when is_port(Socket) ->
try
#{} = Options = ssh_options:handle_options(server, UserOptions),
@@ -267,8 +266,6 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535,
daemon(_, _, _) ->
{error, badarg}.
-
-
%%--------------------------------------------------------------------
-spec daemon_info(daemon_ref()) -> ok_error( [{atom(), term()}] ).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 3dee1c5521..4711f54fb5 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -35,6 +35,8 @@
-define(DEFAULT_TRANSPORT, {tcp, gen_tcp, tcp_closed} ).
+-define(DEFAULT_SHELL, {shell, start, []} ).
+
-define(MAX_RND_PADDING_LEN, 15).
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 62854346b0..958c342f5f 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -127,7 +127,8 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
cm = ConnectionHandler}};
handle_ssh_msg({ssh_cm, ConnectionHandler,
- {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined} = State) ->
+ {exec, ChannelId, WantReply, Cmd}}, #state{exec=undefined,
+ shell=?DEFAULT_SHELL} = State) ->
{Reply, Status} = exec(Cmd),
write_chars(ConnectionHandler,
ChannelId, io_lib:format("~p\n", [Reply])),
@@ -136,6 +137,15 @@ handle_ssh_msg({ssh_cm, ConnectionHandler,
ssh_connection:exit_status(ConnectionHandler, ChannelId, Status),
ssh_connection:send_eof(ConnectionHandler, ChannelId),
{stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
+handle_ssh_msg({ssh_cm, ConnectionHandler,
+ {exec, ChannelId, WantReply, _Cmd}}, #state{exec = undefined} = State) ->
+ write_chars(ConnectionHandler, ChannelId, 1, "Prohibited.\n"),
+ ssh_connection:reply_request(ConnectionHandler, WantReply, success, ChannelId),
+ ssh_connection:exit_status(ConnectionHandler, ChannelId, 255),
+ ssh_connection:send_eof(ConnectionHandler, ChannelId),
+ {stop, ChannelId, State#state{channel = ChannelId, cm = ConnectionHandler}};
+
handle_ssh_msg({ssh_cm, ConnectionHandler,
{exec, ChannelId, WantReply, Cmd}}, State) ->
NewState = start_shell(ConnectionHandler, Cmd, State),
@@ -453,11 +463,14 @@ move_cursor(From, To, #ssh_pty{width=Width, term=Type}) ->
%% %%% make sure that there is data to send
%% %%% before calling ssh_connection:send
write_chars(ConnectionHandler, ChannelId, Chars) ->
+ write_chars(ConnectionHandler, ChannelId, ?SSH_EXTENDED_DATA_DEFAULT, Chars).
+
+write_chars(ConnectionHandler, ChannelId, Type, Chars) ->
case has_chars(Chars) of
false -> ok;
true -> ssh_connection:send(ConnectionHandler,
ChannelId,
- ?SSH_EXTENDED_DATA_DEFAULT,
+ Type,
Chars)
end.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index c8ac3a9c04..e11d3adee4 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1174,17 +1174,25 @@ handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
- D = handle_request(ChannelId, Type, Data, true, From, D0),
- %% Note reply to channel will happen later when reply is recived from peer on the socket
- start_channel_request_timer(ChannelId, From, Timeout),
- {keep_state, cache_request_idle_timer_check(D)};
+ case handle_request(ChannelId, Type, Data, true, From, D0) of
+ {error,Error} ->
+ {keep_state, D0, {reply,From,{error,Error}}};
+ D ->
+ %% Note reply to channel will happen later when reply is recived from peer on the socket
+ start_channel_request_timer(ChannelId, From, Timeout),
+ {keep_state, cache_request_idle_timer_check(D)}
+ end;
handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
when ?CONNECTED(StateName) ->
@@ -1773,21 +1781,31 @@ is_usable_user_pubkey(A, Ssh) ->
%%%----------------------------------------------------------------
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} = Channel ->
+ #channel{remote_id = Id,
+ sent_close = false} = Channel ->
update_sys(cache(D), Channel, Type, ChannelPid),
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
handle_request(ChannelId, Type, Data, WantReply, From, D) ->
case ssh_channel:cache_lookup(cache(D), ChannelId) of
- #channel{remote_id = Id} ->
+ #channel{remote_id = Id,
+ sent_close = false} ->
send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
add_request(WantReply, ChannelId, From, D));
- undefined ->
- D
+
+ _ when WantReply==true ->
+ {error,closed};
+
+ _ ->
+ D
end.
%%%----------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index cf1534bd78..1e10f72956 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -268,7 +268,7 @@ default(server) ->
},
{shell, def} =>
- #{default => {shell, start, []},
+ #{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 202b0afe57..365f25fabb 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -60,7 +60,7 @@
login_bad_pwd_no_retry5/1,
misc_ssh_options/1,
openssh_zlib_basic_test/1,
- packet_size_zero/1,
+ packet_size/1,
pass_phrase/1,
peername_sockname/1,
send/1,
@@ -111,7 +111,7 @@ all() ->
double_close,
daemon_opt_fd,
multi_daemon_opt_fd,
- packet_size_zero,
+ packet_size,
ssh_info_print,
{group, login_bad_pwd_no_retry},
shell_exit_status
@@ -764,11 +764,11 @@ cli(Config) when is_list(Config) ->
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
ssh_connection:shell(ConnectionRef, ChannelId),
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
receive
{ssh_cm, ConnectionRef,
{data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
- ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
end,
@@ -1104,7 +1104,7 @@ multi_daemon_opt_fd(Config) ->
end || {S,Pid,C} <- Tests].
%%--------------------------------------------------------------------
-packet_size_zero(Config) ->
+packet_size(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -1119,21 +1119,31 @@ packet_size_zero(Config) ->
{user_interaction, false},
{user, "vego"},
{password, "morot"}]),
-
- {ok,Chan} = ssh_connection:session_channel(Conn, 1000, _MaxPacketSize=0, 60000),
- ok = ssh_connection:shell(Conn, Chan),
+ lists:foreach(
+ fun(MaxPacketSize) ->
+ ct:log("Try max_packet_size=~p",[MaxPacketSize]),
+ {ok,Ch} = ssh_connection:session_channel(Conn, 1000, MaxPacketSize, 60000),
+ ok = ssh_connection:shell(Conn, Ch),
+ rec(Server, Conn, Ch, MaxPacketSize)
+ end, [0, 1, 10, 25]),
ssh:close(Conn),
- ssh:stop_daemon(Server),
+ ssh:stop_daemon(Server).
+rec(Server, Conn, Ch, MaxSz) ->
receive
- {ssh_cm,Conn,{data,Chan,_Type,_Msg1}} = M ->
- ct:log("Got ~p",[M]),
- ct:fail(doesnt_obey_max_packet_size_0)
- after 5000 ->
- ok
- end.
-
+ {ssh_cm,Conn,{data,Ch,_,M}} when size(M) =< MaxSz ->
+ ct:log("~p: ~p",[MaxSz,M]),
+ rec(Server, Conn, Ch, MaxSz);
+ {ssh_cm,Conn,{data,Ch,_,_}} = M ->
+ ct:log("Max pkt size=~p. Got ~p",[MaxSz,M]),
+ ssh:close(Conn),
+ ssh:stop_daemon(Server),
+ ct:fail("Does not obey max_packet_size=~p",[MaxSz])
+ after
+ 2000 -> ok
+ end.
+
%%--------------------------------------------------------------------
shell_no_unicode(Config) ->
new_do_shell(proplists:get_value(io,Config),
@@ -1491,7 +1501,7 @@ new_do_shell(IO, N, Ops=[{Order,Arg}|More]) ->
ct:fail("*** Expected ~p, but got ~p",[string:strip(ExpStr),RecStr])
end
after 30000 ->
- ct:log("Meassage queue of ~p:~n~p",
+ ct:log("Message queue of ~p:~n~p",
[self(), erlang:process_info(self(), messages)]),
case Order of
expect -> ct:fail("timeout, expected ~p",[string:strip(Arg)]);
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 82b83dd83d..f7eda1dc08 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -92,6 +92,7 @@ end_per_suite(Config) ->
%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
%% Remove dangling images:
%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ catch ssh:stop(),
Config.
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index ba4518cfe6..9587c0c251 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -45,6 +45,8 @@ all() ->
{group, openssh},
small_interrupted_send,
interrupted_send,
+ exec_erlang_term,
+ exec_erlang_term_non_default_shell,
start_shell,
start_shell_exec,
start_shell_exec_fun,
@@ -85,6 +87,7 @@ init_per_suite(Config) ->
?CHECK_CRYPTO(Config).
end_per_suite(Config) ->
+ catch ssh:stop(),
Config.
%%--------------------------------------------------------------------
@@ -542,6 +545,79 @@ start_shell_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+exec_erlang_term(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got expected ~p",[R]);
+ Other ->
+ ct:log("Got unexpected ~p",[Other])
+ after 5000 ->
+ {fail,"Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
+exec_erlang_term_non_default_shell(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = proplists:get_value(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(U, H) -> start_our_shell(U, H) end}
+ ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}
+ ]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "1+2.", infinity),
+ TestResult =
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"3",_/binary>>}} = R ->
+ ct:log("Got unexpected ~p",[R]),
+ {fail,"Could exec erlang term although non-erlang shell"};
+ Other ->
+ ct:log("Got expected ~p",[Other])
+ after 5000 ->
+ {fail, "Exec Timeout"}
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid),
+ TestResult.
+
+%%--------------------------------------------------------------------
start_shell_exec_fun() ->
[{doc, "start shell to exec command"}].
@@ -800,6 +876,8 @@ stop_listener(Config) when is_list(Config) ->
ssh:stop_daemon(Pid0),
ssh:stop_daemon(Pid1);
Error ->
+ ssh:close(ConnectionRef0),
+ ssh:stop_daemon(Pid0),
ct:fail({unexpected, Error})
end.
@@ -819,11 +897,22 @@ start_subsystem_on_closed_channel(Config) ->
{user_interaction, false},
{user_dir, UserDir}]),
- {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
- ok = ssh_connection:close(ConnectionRef, ChannelId),
+ {ok, ChannelId1} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId1),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId1, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId1, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId1, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId1, "exit().\n", 5000),
- {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+ %% Test that there could be a gap between close and an operation (Bugfix OTP-14939):
+ {ok, ChannelId2} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:close(ConnectionRef, ChannelId2),
+ timer:sleep(2000),
+ {error, closed} = ssh_connection:ptty_alloc(ConnectionRef, ChannelId2, []),
+ {error, closed} = ssh_connection:subsystem(ConnectionRef, ChannelId2, "echo_n", 5000),
+ {error, closed} = ssh_connection:exec(ConnectionRef, ChannelId2, "testing1.\n", 5000),
+ {error, closed} = ssh_connection:send(ConnectionRef, ChannelId2, "exit().\n", 5000),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index bb09ca4c8b..12a85c40aa 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -208,35 +208,23 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
ssh:start(),
- Config.
-
-end_per_testcase(TestCase, Config) when TestCase == server_password_option;
- TestCase == server_userpassword_option;
- TestCase == server_pwdfun_option;
- TestCase == server_pwdfun_4_option ;
- TestCase == save_accepted_host_option ->
+ %% Create a clean user_dir
UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
- end_per_testcase(Config);
-end_per_testcase(_TestCase, Config) ->
- end_per_testcase(Config).
+ file:make_dir(UserDir),
+ [{user_dir,UserDir}|Config].
-end_per_testcase(_Config) ->
- ct:log("~p: Before ssh:stop()",[?FUNCTION_NAME]),
+end_per_testcase(_TestCase, Config) ->
ssh:stop(),
- ct:log("~p: After ssh:stop()",[?FUNCTION_NAME]),
ok.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-%%--------------------------------------------------------------------
%%% validate to server that uses the 'password' option
server_password_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -267,12 +255,10 @@ server_password_option(Config) when is_list(Config) ->
%%% validate to server that uses the 'password' option
server_userpassword_option(Config) when is_list(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{user_passwords, [{"vego", "morot"}]}]),
ConnectionRef =
@@ -302,15 +288,13 @@ server_userpassword_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun' option
server_pwdfun_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
CHKPWD = fun("foo",Pwd) -> Pwd=="bar";
(_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,CHKPWD}]),
ConnectionRef =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -340,9 +324,7 @@ server_pwdfun_option(Config) ->
%%--------------------------------------------------------------------
%%% validate to server that uses the 'pwdfun/4' option
server_pwdfun_4_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
PWDFUN = fun("foo",Pwd,{_,_},undefined) -> Pwd=="bar";
("fie",Pwd,{_,_},undefined) -> {Pwd=="bar",new_state};
@@ -350,7 +332,7 @@ server_pwdfun_4_option(Config) ->
(_,_,_,_) -> false
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{pwdfun,PWDFUN}]),
ConnectionRef1 =
ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
@@ -400,9 +382,7 @@ server_pwdfun_4_option(Config) ->
%%--------------------------------------------------------------------
server_pwdfun_4_option_repeat(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
%% Test that the state works
Parent = self(),
@@ -411,7 +391,7 @@ server_pwdfun_4_option_repeat(Config) ->
(_,P,_,S) -> Parent!{P,S}, {false,S+1}
end,
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, PrivDir},
+ {user_dir, UserDir},
{auth_methods,"keyboard-interactive"},
{pwdfun,PWDFUN}]),
@@ -495,9 +475,7 @@ user_dir_option(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
@@ -535,9 +513,7 @@ ssh_msg_debug_fun_option_client(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -581,9 +557,7 @@ connectfun_disconnectfun_server(Config) ->
%%--------------------------------------------------------------------
connectfun_disconnectfun_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -612,9 +586,7 @@ connectfun_disconnectfun_client(Config) ->
%%--------------------------------------------------------------------
%%% validate client that uses the 'ssh_msg_debug_fun' option
ssh_msg_debug_fun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -656,9 +628,7 @@ ssh_msg_debug_fun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -691,9 +661,7 @@ disconnectfun_option_server(Config) ->
%%--------------------------------------------------------------------
disconnectfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -725,9 +693,7 @@ disconnectfun_option_client(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_server(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -768,9 +734,7 @@ unexpectedfun_option_server(Config) ->
%%--------------------------------------------------------------------
unexpectedfun_option_client(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
Parent = self(),
@@ -845,14 +809,9 @@ supported_hash(HashAlg) ->
really_do_hostkey_fingerprint_check(Config, HashAlg) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDirServer = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDirServer),
+ UserDir = proplists:get_value(user_dir, Config),
SysDir = proplists:get_value(data_dir, Config),
- UserDirClient =
- ssh_test_lib:create_random_dir(Config), % Ensure no 'known_hosts' disturbs
-
%% All host key fingerprints. Trust that public_key has checked the ssh_hostkey_fingerprint
%% function since that function is used by the ssh client...
FPs0 = [case HashAlg of
@@ -878,7 +837,7 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
%% Start daemon with the public keys that we got fingerprints from
{Pid, Host0, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
- {user_dir, UserDirServer},
+ {user_dir, UserDir},
{password, "morot"}]),
Host = ssh_test_lib:ntoa(Host0),
FP_check_fun = fun(PeerName, FP) ->
@@ -901,7 +860,8 @@ really_do_hostkey_fingerprint_check(Config, HashAlg) ->
end},
{user, "foo"},
{password, "morot"},
- {user_dir, UserDirClient},
+ {user_dir, UserDir},
+ {save_accepted_host, false}, % Ensure no 'known_hosts' disturbs
{user_interaction, false}]),
ssh:stop_daemon(Pid).
@@ -992,9 +952,7 @@ ms_passed(T0) ->
%%--------------------------------------------------------------------
ssh_daemon_minimal_remote_max_packet_size_option(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
- file:make_dir(UserDir),
+ UserDir = proplists:get_value(user_dir, Config),
{Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
@@ -1320,11 +1278,8 @@ try_to_connect(Connect, Host, Port, Pid, Tref, N) ->
%%--------------------------------------------------------------------
save_accepted_host_option(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ UserDir = proplists:get_value(user_dir, Config),
KnownHosts = filename:join(UserDir, "known_hosts"),
- file:make_dir(UserDir),
- file:delete(KnownHosts),
SysDir = proplists:get_value(data_dir, Config),
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index d453a2e143..1df55834b1 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -201,7 +201,7 @@ killed_acceptor_restarts(Config) ->
Port2 = ssh_test_lib:daemon_port(DaemonPid2),
true = (Port /= Port2),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
{ok,[{AccPid,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
{ok,[{AccPid2,ListenAddr,Port2}]} = acceptor_pid(DaemonPid2),
@@ -218,11 +218,14 @@ killed_acceptor_restarts(Config) ->
%% Make acceptor restart:
exit(AccPid, kill),
+ ?wait_match(undefined, process_info(AccPid)),
%% Check it is a new acceptor:
- {ok,[{AccPid1,ListenAddr,Port}]} = acceptor_pid(DaemonPid),
- true = (AccPid /= AccPid1),
- true = (AccPid2 /= AccPid1),
+ ?wait_match({ok,[{AccPid1,ListenAddr,Port}]}, AccPid1=/=AccPid,
+ acceptor_pid(DaemonPid),
+ AccPid1,
+ 500, 30),
+ AccPid1 =/= AccPid2,
%% Connect second client and check it is alive:
{ok,C2} = ssh:connect("localhost", Port, [{silently_accept_hosts, true},
@@ -232,21 +235,21 @@ killed_acceptor_restarts(Config) ->
{user_dir, UserDir}]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
- ct:pal("~s",[lists:flatten(ssh_info:string())]),
+ ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Check first client is still alive:
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
ok = ssh:stop_daemon(DaemonPid2),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid2), 1000, 30),
[{client_version,_}] = ssh:connection_info(C1,[client_version]),
[{client_version,_}] = ssh:connection_info(C2,[client_version]),
ok = ssh:stop_daemon(DaemonPid),
- timer:sleep(15000),
+ ?wait_match(undefined, process_info(DaemonPid), 1000, 30),
{error,closed} = ssh:connection_info(C1,[client_version]),
{error,closed} = ssh:connection_info(C2,[client_version]).
-
+
%%-------------------------------------------------------------------------
shell_channel_tree(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
@@ -257,7 +260,7 @@ shell_channel_tree(Config) ->
fun() ->
io:format("TimeoutShell started!~n",[]),
timer:sleep(5000),
- ct:pal("~p TIMEOUT!",[self()])
+ ct:log("~p TIMEOUT!",[self()])
end,
{Daemon, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
{user_dir, UserDir},
@@ -283,14 +286,14 @@ shell_channel_tree(Config) ->
[GroupPid]),
{links,GroupLinks} = erlang:process_info(GroupPid, links),
[ShellPid] = GroupLinks--[ChannelSup],
- ct:pal("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
+ ct:log("GroupPid = ~p, ShellPid = ~p",[GroupPid,ShellPid]),
receive
{ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"TimeoutShell started!\r\n">>}} ->
receive
%%---- wait for the subsystem to terminate
{ssh_cm,ConnectionRef,{closed,ChannelId0}} ->
- ct:pal("Subsystem terminated",[]),
+ ct:log("Subsystem terminated",[]),
case {chk_empty_con_daemon(Daemon),
process_info(GroupPid),
process_info(ShellPid)} of
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index f97c3b1352..57ae2dbac2 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -53,10 +53,12 @@ daemon(Host, Options) ->
daemon(Host, Port, Options) ->
- %% ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
+ ct:log("~p:~p Calling ssh:daemon(~p, ~p, ~p)",[?MODULE,?LINE,Host,Port,Options]),
case ssh:daemon(Host, Port, Options) of
{ok, Pid} ->
- {ok,L} = ssh:daemon_info(Pid),
+ R = ssh:daemon_info(Pid),
+ ct:log("~p:~p ssh:daemon_info(~p) ->~n ~p",[?MODULE,?LINE,Pid,R]),
+ {ok,L} = R,
ListenPort = proplists:get_value(port, L),
ListenIP = proplists:get_value(ip, L),
{Pid, ListenIP, ListenPort};
diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl
index eaf856e6e8..4b6579bd71 100644
--- a/lib/ssh/test/ssh_test_lib.hrl
+++ b/lib/ssh/test/ssh_test_lib.hrl
@@ -16,12 +16,12 @@
%%-------------------------------------------------------------------------
%% Help macro
%%-------------------------------------------------------------------------
--define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+-define(wait_match(Pattern, Guard, FunctionCall, Bind, Timeout, Ntries),
Bind =
(fun() ->
F = fun(N, F1) ->
case FunctionCall of
- Pattern -> Bind;
+ Pattern when Guard -> Bind;
_ when N>0 ->
ct:pal("Must sleep ~p ms at ~p:~p",[Timeout,?MODULE,?LINE]),
timer:sleep(Timeout),
@@ -34,6 +34,9 @@
end)()
).
+-define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries),
+ ?wait_match(Pattern, true, FunctionCall, Bind, Timeout, Ntries)).
+
-define(wait_match(Pattern, FunctionCall, Timeout, Ntries), ?wait_match(Pattern, FunctionCall, ok, Timeout, Ntries)).
-define(wait_match(Pattern, FunctionCall, Bind), ?wait_match(Pattern, FunctionCall, Bind, 500, 10) ).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 6aaa22a6b4..480e955ec4 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
SSH_VSN = 4.6.5
-
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 79176f5edf..bdf8711b2f 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -307,6 +307,21 @@
</section>
</section>
+<section><title>SSL 8.1.3.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix alert handling so that unexpected messages are logged
+ and alerted correctly</p>
+ <p>
+ Own Id: OTP-14929</p>
+ </item>
+ </list>
+ </section>
+</section>
+
<section><title>SSL 8.1.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index c6927bd276..62a172ca7c 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -95,7 +95,7 @@ security_parameters(Version, CipherSuite, SecParams) ->
expanded_key_material_length = expanded_key_material(Cipher),
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
- mac_algorithm = hash_algorithm(Hash),
+ mac_algorithm = mac_algorithm(Hash),
prf_algorithm = prf_algorithm(PrfHashAlg, Version),
hash_size = hash_size(Hash)}.
@@ -2334,6 +2334,11 @@ prf_algorithm(default_prf, {3, _}) ->
prf_algorithm(Algo, _) ->
hash_algorithm(Algo).
+mac_algorithm(aead) ->
+ aead;
+mac_algorithm(Algo) ->
+ hash_algorithm(Algo).
+
hash_algorithm(null) -> ?NULL;
hash_algorithm(md5) -> ?MD5;
hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1"
@@ -2364,6 +2369,10 @@ sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other
hash_size(null) ->
0;
+%% The AEAD MAC hash size is not used in the context
+%% of calculating the master secret. See RFC 5246 Section 6.2.3.3.
+hash_size(aead) ->
+ 0;
hash_size(md5) ->
16;
hash_size(sha) ->
diff --git a/lib/ssl/test/ssl_engine_SUITE.erl b/lib/ssl/test/ssl_engine_SUITE.erl
index bc221d35fd..71891356e8 100644
--- a/lib/ssl/test/ssl_engine_SUITE.erl
+++ b/lib/ssl/test/ssl_engine_SUITE.erl
@@ -39,23 +39,28 @@ init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
ok ->
- ssl_test_lib:clean_start(),
- case crypto:get_test_engine() of
- {ok, EngineName} ->
- try crypto:engine_load(<<"dynamic">>,
- [{<<"SO_PATH">>, EngineName},
- <<"LOAD">>],
- []) of
- {ok, Engine} ->
- [{engine, Engine} |Config];
- {error, Reason} ->
- ct:pal("Reason ~p", [Reason]),
- {skip, "No dynamic engine support"}
- catch error:notsup ->
- {skip, "No engine support in OpenSSL"}
- end;
- {error, notexist} ->
- {skip, "Test engine not found"}
+ case crypto:info_lib() of
+ [{_,_, <<"OpenSSL 1.0.1s-freebsd 1 Mar 2016">>}] ->
+ {skip, "Problem with engine on OpenSSL 1.0.1s-freebsd"};
+ _ ->
+ ssl_test_lib:clean_start(),
+ case crypto:get_test_engine() of
+ {ok, EngineName} ->
+ try crypto:engine_load(<<"dynamic">>,
+ [{<<"SO_PATH">>, EngineName},
+ <<"LOAD">>],
+ []) of
+ {ok, Engine} ->
+ [{engine, Engine} |Config];
+ {error, Reason} ->
+ ct:pal("Reason ~p", [Reason]),
+ {skip, "No dynamic engine support"}
+ catch error:notsup ->
+ {skip, "No engine support in OpenSSL"}
+ end;
+ {error, notexist} ->
+ {skip, "Test engine not found"}
+ end
end
catch _:_ ->
{skip, "Crypto did not start"}
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index fcaccdb2cb..350847bf7d 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -270,7 +270,7 @@
<item>
<p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>,
<anno>Arguments</anno>)</c> and measures the elapsed real time as
- reported by <seealso marker="os:timestamp/0">
+ reported by <seealso marker="kernel:os#timestamp/0">
<c>os:timestamp/0</c></seealso>.</p>
<p>Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where
<c><anno>Time</anno></c> is the elapsed real time in
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index ab041ff53c..6f5e617230 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -411,10 +411,12 @@ to_number(_, Number, Rest, _, Tail) ->
%% Return the remaining string with prefix removed or else nomatch
-spec prefix(String::unicode:chardata(), Prefix::unicode:chardata()) ->
'nomatch' | unicode:chardata().
-prefix(Str, []) -> Str;
prefix(Str, Prefix0) ->
- Prefix = unicode:characters_to_list(Prefix0),
- case prefix_1(Str, Prefix) of
+ Result = case unicode:characters_to_list(Prefix0) of
+ [] -> Str;
+ Prefix -> prefix_1(Str, Prefix)
+ end,
+ case Result of
[] when is_binary(Str) -> <<>>;
Res -> Res
end.
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 71f86e32e5..7b82647416 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -894,10 +894,13 @@ match_limit(Config) when is_list(Config) ->
%% Test that we get sub-binaries if subject is a binary and we capture
%% binaries.
sub_binaries(Config) when is_list(Config) ->
- Bin = list_to_binary(lists:seq(1,255)),
- {match,[B,C]}=re:run(Bin,"(a)",[{capture,all,binary}]),
- 255 = binary:referenced_byte_size(B),
- 255 = binary:referenced_byte_size(C),
- {match,[D]}=re:run(Bin,"(a)",[{capture,[1],binary}]),
- 255 = binary:referenced_byte_size(D),
+ %% The GC can auto-convert tiny sub-binaries to heap binaries, so we
+ %% extract large sequences to make the test more stable.
+ Bin = << <<I>> || I <- lists:seq(1, 4096) >>,
+ {match,[B,C]}=re:run(Bin,"a(.+)$",[{capture,all,binary}]),
+ true = byte_size(B) =/= byte_size(C),
+ 4096 = binary:referenced_byte_size(B),
+ 4096 = binary:referenced_byte_size(C),
+ {match,[D]}=re:run(Bin,"a(.+)$",[{capture,[1],binary}]),
+ 4096 = binary:referenced_byte_size(D),
ok.
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index f43bfb4482..17714b8d4d 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -485,6 +485,10 @@ to_float(_) ->
prefix(_) ->
?TEST("", ["a"], nomatch),
?TEST("a", [""], "a"),
+ ?TEST("a", [[[]]], "a"),
+ ?TEST("a", [<<>>], "a"),
+ ?TEST("a", [[<<>>]], "a"),
+ ?TEST("a", [[[<<>>]]], "a"),
?TEST("b", ["a"], nomatch),
?TEST("a", ["a"], ""),
?TEST("å", ["a"], nomatch),
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 35c93ba4ed..ea4d6cb723 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -54,8 +54,6 @@ EL_FILES = $(EMACS_FILES:%=%.el)
ELC_FILES = $(EMACS_FILES:%=%.elc)
-TEST_FILES = test.erl.indented test.erl.orig
-
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
@@ -75,7 +73,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/emacs"
- $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \
+ $(INSTALL_DATA) $(EL_FILES) $(README_FILES) \
"$(RELSYSDIR)/emacs"
ifeq ($(DOCTYPE),pdf)
@@ -89,19 +87,3 @@ release_docs_spec: docs
$(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3"
endif
endif
-
-EMACS ?= emacs
-
-test_indentation:
- @rm -f test.erl
- @rm -f test_indent.el
- @echo '(load "erlang-start")' >> test_indent.el
- @echo '(find-file "test.erl.orig")' >> test_indent.el
- @echo "(require 'cl) ; required with Emacs < 23 for ignore-errors" >> test_indent.el
- @echo '(erlang-mode)' >> test_indent.el
- @echo '(toggle-debug-on-error)' >> test_indent.el
- @echo '(erlang-indent-current-buffer)' >> test_indent.el
- @echo '(write-file "test.erl")' >> test_indent.el
- $(EMACS) --batch -Q -L . -l test_indent.el
- diff -u test.erl.indented test.erl
- @echo "No differences between expected and actual indentation"
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index bdb3d9ad4a..534f50ab33 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -279,7 +279,8 @@ Please see the function `tempo-define-template'.")
'((erlang-skel-include erlang-skel-large-header)
"-behaviour(application)." n n
"%% Application callbacks" n
- "-export([start/2, stop/1])." n n
+ "-export([start/2, start_phase/3, stop/1, prep_stop/1," n>
+ "config_change/3])." n n
(erlang-skel-double-separator-start 3)
"%%% Application callbacks" n
(erlang-skel-double-separator-end 3) n
@@ -291,13 +292,14 @@ Please see the function `tempo-define-template'.")
"%% application. If the application is structured according to the OTP" n
"%% design principles as a supervision tree, this means starting the" n
"%% top supervisor of the tree." n
- "%%" n
- "%% @spec start(StartType, StartArgs) -> {ok, Pid} |" n
- "%% {ok, Pid, State} |" n
- "%% {error, Reason}" n
- "%% StartType = normal | {takeover, Node} | {failover, Node}" n
- "%% StartArgs = term()" n
(erlang-skel-separator-end 2)
+ "-spec start(StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "StartArgs :: term()) ->" n>
+ "{ok, Pid :: pid()} |" n>
+ "{ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Reason :: term()}." n
"start(_StartType, _StartArgs) ->" n>
"case 'TopSupervisor':start_link() of" n>
"{ok, Pid} ->" n>
@@ -309,15 +311,52 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
+ "%% top supervisor of the tree." n
+ "%% Starts an application with included applications, when" n
+ "%% synchronization is needed between processes in the different" n
+ "%% applications during startup."
+ (erlang-skel-separator-end 2)
+ "-spec start_phase(Phase :: atom()," n>
+ "StartType :: normal |" n>
+ "{takeover, Node :: node()} |" n>
+ "{failover, Node :: node()}," n>
+ "PhaseArgs :: term()) -> ok | {error, Reason :: term()}." n
+ "start_phase(_Phase, _StartType, _PhaseArgs) ->" n>
+ "ok."n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
"%% This function is called whenever an application has stopped. It" n
"%% is intended to be the opposite of Module:start/2 and should do" n
"%% any necessary cleaning up. The return value is ignored." n
- "%%" n
- "%% @spec stop(State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec stop(State :: term()) -> any()." n
"stop(_State) ->" n>
"ok." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called when an application is about to be stopped," n
+ "%% before shutting down the processes of the application." n
+ (erlang-skel-separator-end 2)
+ "-spec prep_stop(State :: term()) -> NewState :: term()." n
+ "prep_stop(State) ->" n>
+ "State." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by an application after a code replacement," n
+ "%% if the configuration parameters have changed." n
+ (erlang-skel-separator-end 2)
+ "-spec config_change(Changed :: [{Par :: atom(), Val :: term()}]," n>
+ "New :: [{Par :: atom(), Val :: term()}]," n>
+ "Removed :: [Par :: atom()]) -> ok." n
+ "config_change(_Changed, _New, _Removed) ->" n>
+ "ok." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -343,9 +382,12 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, {shutdown, term()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -359,11 +401,11 @@ Please see the function `tempo-define-template'.")
"%% this function is called by the new process to find out about" n
"%% restart strategy, maximum restart intensity, and child" n
"%% specifications." n
- "%%" n
- "%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) ->" n>
+ "{ok, {SupFlags :: supervisor:sup_flags()," n>
+ "[ChildSpec :: supervisor:child_spec()]}} |" n>
+ "ignore." n
"init([]) ->" n
"" n>
"SupFlags = #{strategy => one_for_one," n>
@@ -406,9 +448,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the supervisor bridge" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, {already_started, Pid :: pid()}} |" n>
+ "{error, term()} |" n>
+ "ignore." n
"start_link() ->" n>
"supervisor_bridge:start_link({local, ?SERVER}, ?MODULE, [])." n
n
@@ -422,11 +466,10 @@ Please see the function `tempo-define-template'.")
"%% which calls Module:init/1 to start the subsystem. To ensure a" n
"%% synchronized start-up procedure, this function does not return" n
"%% until Module:init/1 has returned." n
- "%%" n
- "%% @spec init(Args) -> {ok, Pid, State} |" n
- "%% ignore |" n
- "%% {error, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, Pid :: pid(), State :: term()} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"case 'AModule':start_link() of" n>
"{ok, Pid} ->" n>
@@ -442,10 +485,9 @@ Please see the function `tempo-define-template'.")
"%% to terminate. It should be the opposite of Module:init/1 and stop" n
"%% the subsystem and do any necessary cleaning up.The return value is" n
"%% ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(Reason, State) ->" n>
+ "-spec terminate(Reason :: shutdown | term(), State :: term()) -> any()." n
+ "terminate(_Reason, _State) ->" n>
"'AModule':stop()," n>
"ok." n
n
@@ -464,9 +506,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0])." n n
"%% gen_server callbacks" n
- "-export([init/1, handle_call/3, handle_cast/2, "
- "handle_info/2," n>
- "terminate/2, code_change/3])." n n
+ "-export([init/1, handle_call/3, handle_cast/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -478,9 +519,11 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Starts the server" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()}} |" n>
+ "{error, Error :: term()} |" n>
+ "ignore." n
"start_link() ->" n>
"gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])." n
n
@@ -492,12 +535,12 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Initializes the server" n
- "%%" n
- "%% @spec init(Args) -> {ok, State} |" n
- "%% {ok, State, Timeout} |" n
- "%% ignore |" n
- "%% {stop, Reason}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term()) -> {ok, State :: term()} |" n>
+ "{ok, State :: term(), Timeout :: timeout()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{stop, Reason :: term()} |" n>
+ "ignore." n
"init([]) ->" n>
"process_flag(trap_exit, true)," n>
"{ok, #state{}}." n
@@ -506,15 +549,16 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling call messages" n
- "%%" n
- "%% @spec handle_call(Request, From, State) ->" n
- "%% {reply, Reply, State} |" n
- "%% {reply, Reply, State, Timeout} |" n
- "%% {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, Reply, State} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->" n>
+ "{reply, Reply :: term(), NewState :: term()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} |" n>
+ "{reply, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), Reply :: term(), NewState :: term()} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
"handle_call(_Request, _From, State) ->" n>
"Reply = ok," n>
"{reply, Reply, State}." n
@@ -523,23 +567,25 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Handling cast messages" n
- "%%" n
- "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
- "handle_cast(_Msg, State) ->" n>
+ "-spec handle_cast(Request :: term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: term(), NewState :: term()}." n
+ "handle_cast(_Request, State) ->" n>
"{noreply, State}." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Handling all non call/cast messages" n
- "%%" n
- "%% @spec handle_info(Info, State) -> {noreply, State} |" n
- "%% {noreply, State, Timeout} |" n
- "%% {stop, Reason, State}" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: timeout() | term(), State :: term()) ->" n>
+ "{noreply, NewState :: term()} |" n>
+ "{noreply, NewState :: term(), Timeout :: timeout()} |" n>
+ "{noreply, NewState :: term(), hibernate} |" n>
+ "{stop, Reason :: normal | term(), NewState :: term()}." n
"handle_info(_Info, State) ->" n>
"{noreply, State}." n
n
@@ -550,9 +596,9 @@ Please see the function `tempo-define-template'.")
"%% terminate. It should be the opposite of Module:init/1 and do any" n
"%% necessary cleaning up. When it returns, the gen_server terminates" n
"%% with Reason. The return value is ignored." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
+ "-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term()," n>
+ "State :: term()) -> any()." n
"terminate(_Reason, _State) ->" n>
"ok." n
n
@@ -560,12 +606,26 @@ Please see the function `tempo-define-template'.")
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()} |" n>
+ "{error, Reason :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_server status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
@@ -581,8 +641,8 @@ Please see the function `tempo-define-template'.")
"-export([start_link/0, add_handler/0])." n n
"%% gen_event callbacks" n
- "-export([init/1, handle_event/2, handle_call/2, " n>
- "handle_info/2, terminate/2, code_change/3])." n n
+ "-export([init/1, handle_event/2, handle_call/2, handle_info/2," n>
+ "terminate/2, code_change/3, format_status/2])." n n
"-define(SERVER, ?MODULE)." n n
@@ -594,18 +654,17 @@ Please see the function `tempo-define-template'.")
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Creates an event manager" n
- "%%" n
- "%% @spec start_link() -> {ok, Pid} | {error, Error}" n
(erlang-skel-separator-end 2)
+ "-spec start_link() -> {ok, Pid :: pid()} |" n>
+ "{error, Error :: {already_started, pid()} | term()}." n
"start_link() ->" n>
"gen_event:start_link({local, ?SERVER})." n
n
(erlang-skel-separator-start 2)
"%% @doc" n
"%% Adds an event handler" n
- "%%" n
- "%% @spec add_handler() -> ok | {'EXIT', Reason} | term()" n
(erlang-skel-separator-end 2)
+ "-spec add_handler() -> ok | {'EXIT', Reason :: term()} | term()." n
"add_handler() ->" n>
"gen_event:add_handler(?SERVER, ?MODULE, [])." n
n
@@ -617,9 +676,11 @@ Please see the function `tempo-define-template'.")
"%% @doc" n
"%% Whenever a new event handler is added to an event manager," n
"%% this function is called to initialize the event handler." n
- "%%" n
- "%% @spec init(Args) -> {ok, State}" n
(erlang-skel-separator-end 2)
+ "-spec init(Args :: term() | {Args :: term(), Term :: term()}) ->" n>
+ "{ok, State :: term()} |" n>
+ "{ok, State :: term(), hibernate} |" n>
+ "{error, Reason :: term()}." n
"init([]) ->" n>
"{ok, #state{}}." n
n
@@ -629,12 +690,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives an event sent using" n
"%% gen_event:notify/2 or gen_event:sync_notify/2, this function is" n
"%% called for each installed event handler to handle the event." n
- "%%" n
- "%% @spec handle_event(Event, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |"n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_event(Event :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()} , Args2 :: term()}." n>
"handle_event(_Event, State) ->" n>
"{ok, State}." n
n
@@ -644,12 +706,13 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event manager receives a request sent using" n
"%% gen_event:call/3,4, this function is called for the specified" n
"%% event handler to handle the request." n
- "%%" n
- "%% @spec handle_call(Request, State) ->" n
- "%% {ok, Reply, State} |" n
- "%% {swap_handler, Reply, Args1, State1, Mod2, Args2} |" n
- "%% {remove_handler, Reply}" n
(erlang-skel-separator-end 2)
+ "-spec handle_call(Request :: term(), State :: term()) ->" n>
+ "{ok, Reply :: term(), NewState :: term()} |" n>
+ "{ok, Reply :: term(), NewState :: term(), hibernate} |" n>
+ "{remove_handler, Reply :: term()} |" n>
+ "{swap_handler, Reply :: term(), Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_call(_Request, State) ->" n>
"Reply = ok," n>
"{ok, Reply, State}." n
@@ -660,12 +723,13 @@ Please see the function `tempo-define-template'.")
"%% This function is called for each installed event handler when" n
"%% an event manager receives any other message than an event or a" n
"%% synchronous request (or a system message)." n
- "%%" n
- "%% @spec handle_info(Info, State) ->" n
- "%% {ok, State} |" n
- "%% {swap_handler, Args1, State1, Mod2, Args2} |" n
- "%% remove_handler" n
(erlang-skel-separator-end 2)
+ "-spec handle_info(Info :: term(), State :: term()) ->" n>
+ "{ok, NewState :: term()} |" n>
+ "{ok, NewState :: term(), hibernate} |" n>
+ "remove_handler |" n>
+ "{swap_handler, Args1 :: term(), NewState :: term()," n>
+ "Handler2 :: atom() | {atom(), term()}, Args2 :: term()}." n
"handle_info(_Info, State) ->" n>
"{ok, State}." n
n
@@ -675,22 +739,40 @@ Please see the function `tempo-define-template'.")
"%% Whenever an event handler is deleted from an event manager, this" n
"%% function is called. It should be the opposite of Module:init/1 and" n
"%% do any necessary cleaning up." n
- "%%" n
- "%% @spec terminate(Reason, State) -> void()" n
(erlang-skel-separator-end 2)
- "terminate(_Reason, _State) ->" n>
+ "-spec terminate(Arg :: {stop, Reason :: term()} |" n>
+ "stop |" n>
+ "remove_handler |" n>
+ "{error, {'EXIT', Reason :: term()}} |" n>
+ "{error, Term :: term()} |" n>
+ "term()," n>
+ "State :: term()) -> any()." n
+ "terminate(_Arg, _State) ->" n>
"ok." n
n
(erlang-skel-separator-start 2)
"%% @private" n
"%% @doc" n
"%% Convert process state when code is changed" n
- "%%" n
- "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
(erlang-skel-separator-end 2)
+ "-spec code_change(OldVsn :: term() | {down, term()}," n>
+ "State :: term()," n>
+ "Extra :: term()) -> {ok, NewState :: term()}." n
"code_change(_OldVsn, State, _Extra) ->" n>
"{ok, State}." n
n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called for changing the form and appearance" n
+ "%% of gen_event status when it is returned from sys:get_status/1,2" n
+ "%% or when it appears in termination error logs." n
+ (erlang-skel-separator-end 2)
+ "-spec format_status(Opt :: normal | terminate," n>
+ "Status :: list()) -> Status :: term()." n
+ "format_status(_Opt, Status) ->" n>
+ "Status." n
+ n
(erlang-skel-double-separator-start 3)
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 411e0e13df..e4166053d5 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -4,7 +4,7 @@
;; Author: Anders Lindgren
;; Keywords: erlang, languages, processes
;; Date: 2011-12-11
-;; Version: 2.8.0
+;; Version: 2.8.1
;; Package-Requires: ((emacs "24.1"))
;; %CopyrightBegin%
@@ -84,7 +84,7 @@
"The Erlang programming language."
:group 'languages)
-(defconst erlang-version "2.8.0"
+(defconst erlang-version "2.8.1"
"The version number of Erlang mode.")
(defcustom erlang-root-dir nil
@@ -2745,7 +2745,7 @@ Return nil if inside string, t if in a comment."
(1+ (nth 2 stack-top)))
((= (char-syntax (following-char)) ?\))
(goto-char (nth 1 stack-top))
- (cond ((looking-at "[({]\\s *\\($\\|%\\)")
+ (cond ((erlang-record-or-function-args-p)
;; Line ends with parenthesis.
(let ((previous (erlang-indent-find-preceding-expr))
(stack-pos (nth 2 stack-top)))
@@ -2755,19 +2755,10 @@ Return nil if inside string, t if in a comment."
(nth 2 stack-top))))
((= (following-char) ?,)
;; a comma at the start of the line: line up with opening parenthesis.
- (nth 2 stack-top))
+ (min (nth 2 stack-top)
+ (erlang-indent-element stack-top indent-point token)))
(t
- (goto-char (nth 1 stack-top))
- (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)")
- ;; Line ends with parenthesis.
- (erlang-indent-parenthesis (nth 2 stack-top)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (1+ (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column)))))
- (erlang-indent-standard indent-point token base 't)))))
+ (erlang-indent-element stack-top indent-point token))))
;;
((eq (car stack-top) '<<)
;; Element of binary (possible comprehension) expression,
@@ -2776,13 +2767,11 @@ Return nil if inside string, t if in a comment."
(+ 2 (nth 2 stack-top)))
((looking-at "\\(>>\\)[^_a-zA-Z0-9]")
(nth 2 stack-top))
+ ((= (following-char) ?,)
+ (min (+ (nth 2 stack-top) 1)
+ (- (erlang-indent-to-first-element stack-top 2) 1)))
(t
- (goto-char (nth 1 stack-top))
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))))
+ (erlang-indent-to-first-element stack-top 2))))
((memq (car stack-top) '(icr fun spec))
;; The default indentation is the column of the option
@@ -2838,12 +2827,13 @@ Return nil if inside string, t if in a comment."
(let ((base (erlang-indent-find-base stack indent-point off skip)))
;; Special cases
(goto-char indent-point)
- (cond ((looking-at "\\(end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(;\\|end\\|after\\)\\($\\|[^_a-zA-Z0-9]\\)")
(if (eq (car stack-top) '->)
(erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
+ (cond ((and stack (looking-at ";"))
+ (+ (erlang-caddr (car stack)) (- erlang-indent-level 2)))
+ (stack (erlang-caddr (car stack)))
+ (t off)))
((looking-at "catch\\b\\($\\|[^_a-zA-Z0-9]\\)")
;; Are we in a try
(let ((start (if (eq (car stack-top) '->)
@@ -2917,6 +2907,22 @@ Return nil if inside string, t if in a comment."
(current-column))) start-alternativ))))))
)))
+(defun erlang-indent-to-first-element (stack-top extra)
+ ;; Indent to the same column as the first
+ ;; argument. extra should be 1 for lists tuples or 2 for binaries
+ (goto-char (+ (nth 1 stack-top) extra))
+ (skip-chars-forward " \t")
+ (current-column))
+
+(defun erlang-indent-element (stack-top indent-point token)
+ (goto-char (nth 1 stack-top))
+ (let ((base (cond ((erlang-record-or-function-args-p)
+ ;; Line ends with parenthesis.
+ (erlang-indent-parenthesis (nth 2 stack-top)))
+ (t
+ (erlang-indent-to-first-element stack-top 1)))))
+ (erlang-indent-standard indent-point token base 't)))
+
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
"Standard indent when in blocks or tuple or arguments.
Look at last thing to see in what state we are, move relative to the base."
@@ -2942,6 +2948,9 @@ Return nil if inside string, t if in a comment."
;; Avoid treating comments a continued line.
((= (following-char) ?%)
base)
+ ((and (= (following-char) ?,) inside-parenthesis)
+ ;; a comma at the start of the line line up with parenthesis
+ (- base 1))
;; Continued line (e.g. line beginning
;; with an operator.)
(t
@@ -3031,11 +3040,21 @@ This assumes that the preceding expression is either simple
(t col)))
col))))
+(defun erlang-record-or-function-args-p ()
+ (and (looking-at "[({]\\s *\\($\\|%\\)")
+ (or (eq (following-char) ?\( )
+ (save-excursion
+ (ignore-errors (forward-sexp (- 1)))
+ (eq (preceding-char) ?#)))))
+
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
- (if (> previous stack-position)
- (+ stack-position erlang-argument-indent)
- (+ previous erlang-argument-indent))))
+ (cond ((eq previous stack-position) ;; tuple or map not a record
+ (1+ stack-position))
+ ((> previous stack-position)
+ (+ stack-position erlang-argument-indent))
+ (t
+ (+ previous erlang-argument-indent)))))
(defun erlang-skip-blank (&optional lim)
"Skip over whitespace and comments until limit reached."
@@ -5169,7 +5188,6 @@ future, a new shell on an already running host will be started."
;; e.g. it does not assume that we are running an inferior
;; Erlang, there exists a lot of other possibilities.
-
(defvar erlang-shell-buffer-name "*erlang*"
"The name of the Erlang link shell buffer.")
@@ -5180,46 +5198,28 @@ Also see the description of `ielm-prompt-read-only'."
:type 'boolean
:package-version '(erlang . "2.8.0"))
-(defvar erlang-shell-mode-map nil
- "Keymap used by Erlang shells.")
-
-
-(defvar erlang-shell-mode-hook nil
- "User functions to run when an Erlang shell is started.
-
-This hook is used to change the behaviour of Erlang mode. It is
-normally used by the user to personalise the programming environment.
-When used in a site init file, it could be used to customise Erlang
-mode for all users on the system.
-
-The function added to this hook is run every time a new Erlang
-shell is started.
+(defvar erlang-shell-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\M-\t" 'erlang-complete-tag)
-See also `erlang-load-hook', a hook which is run once, when Erlang
-mode is loaded, and `erlang-mode-hook' which is run every time a new
-Erlang source file is loaded into Emacs.")
+ ;; Normally the other way around.
+ (define-key map "\C-a" 'comint-bol)
+ (define-key map "\C-c\C-a" 'beginning-of-line)
+ (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
+ (define-key map "\M-\C-m" 'compile-goto-error)
+ map)
+ "Keymap used by Erlang shells.")
(defvar erlang-input-ring-file-name "~/.erlang_history"
"When non-nil, file name used to store Erlang shell history information.")
-
-(defun erlang-shell-mode ()
+(define-derived-mode erlang-shell-mode comint-mode "Erlang Shell"
"Major mode for interacting with an Erlang shell.
-We assume that we already are in Comint mode.
-
The following special commands are available:
\\{erlang-shell-mode-map}"
- (interactive)
- (setq major-mode 'erlang-shell-mode)
- (setq mode-name "Erlang Shell")
(erlang-mode-variables)
- (if erlang-shell-mode-map
- nil
- (setq erlang-shell-mode-map (copy-keymap comint-mode-map))
- (erlang-shell-mode-commands erlang-shell-mode-map))
- (use-local-map erlang-shell-mode-map)
;; Needed when compiling directly from the Erlang shell.
(setq compilation-last-buffer (current-buffer))
(setq comint-prompt-regexp "^[^>=]*> *")
@@ -5233,7 +5233,6 @@ The following special commands are available:
'inferior-erlang-strip-delete nil t)
(add-hook 'comint-output-filter-functions
'inferior-erlang-strip-ctrl-m nil t)
-
(setq comint-input-ring-file-name erlang-input-ring-file-name)
(comint-read-input-ring t)
(make-local-variable 'kill-buffer-hook)
@@ -5252,8 +5251,7 @@ The following special commands are available:
(define-key map [menu-bar compilation]
(cons "Errors" compilation-menu-map)))
map))))
- (erlang-tags-init)
- (run-hooks 'erlang-shell-mode-hook))
+ (erlang-tags-init))
(defun erlang-mouse-2-command (event)
@@ -5275,13 +5273,6 @@ Selects Comint or Compilation mode command as appropriate."
(call-interactively (lookup-key compilation-mode-map "\C-m"))
(call-interactively (lookup-key comint-mode-map "\C-m"))))
-(defun erlang-shell-mode-commands (map)
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-a" 'comint-bol) ; Normally the other way around.
- (define-key map "\C-c\C-a" 'beginning-of-line)
- (define-key map "\C-d" nil) ; Was `comint-delchar-or-maybe-eof'
- (define-key map "\M-\C-m" 'compile-goto-error))
-
;;;
;;; Inferior Erlang -- Run an Erlang shell as a subprocess.
;;;
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
deleted file mode 100644
index 14a4eca7c3..0000000000
--- a/lib/tools/emacs/test.erl.indented
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
- M#{a :=<<"a">>
- ,b:=1}.
-foo() ->
- #{a =><<"a">>
- ,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
- }).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
- , b = foobar :: atom()
- }).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
- <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
- <<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
- nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
- 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
- , b :: any()
- }.
--type t27() :: { integer()
- , atom()
- }.
--type t99() ::
- {t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
- t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
- (t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
- (t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
- (t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
- , Y :: integer()
- ) -> integer().
-
--opaque attributes_data() ::
- [{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
- f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
- [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
- f3 = x:y(),
- f4 = x:z() :: t99(),
- f17 :: 'undefined',
- f18 :: 1 | 2 | 'undefined',
- f19 = 3 :: integer()|undefined,
- f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
- % Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
- Z < 13;
- Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
- ],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
- <<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
- Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
- %% check EEP37 named funs
- Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
- Fact(0) ->
- 1
- end,
- %% check anonymous funs too
- Fn2 = fun(0) ->
- 1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
-indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
- sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
- (catch exit())
- catch
- _ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
- %% I don't have a good idea how we want to handle this
- %% but they are here to show how they are indented today.
- Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
- Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
- Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
- Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
- ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
- [#foo{
- foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
- ]
- }.
-
-
-%% this used to result in a scan-sexp error
-[{
- }].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
- try generate_exception(N) of
- Val -> {N, normal, Val}
- catch
- throw:X -> {N, caught, thrown, X};
- exit:X -> {N, caught, exited, X};
- error:X -> {N, caught, error, X}
- end.
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
deleted file mode 100644
index c0cf1749b6..0000000000
--- a/lib/tools/emacs/test.erl.orig
+++ /dev/null
@@ -1,784 +0,0 @@
-%% -*- Mode: erlang; indent-tabs-mode: nil -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-
-%%%-------------------------------------------------------------------
-%%% File : test.erl
-%%% Author : Dan Gudmundsson <[email protected]>
-%%% Description : Test emacs mode indention and font-locking
-%%% this file is intentionally not indented.
-%%% Copy the file and indent it and you should end up with test.erl.indented
-%%% Created : 6 Oct 2009 by Dan Gudmundsson <[email protected]>
-%%%-------------------------------------------------------------------
-
-%% Start off with syntax highlighting you have to verify this by looking here
-%% and see that the code looks alright
-
--module(test).
--compile(export_all).
-
-%% Used to cause an "Unbalanced parentheses" error.
-foo(M) ->
-M#{a :=<<"a">>
-,b:=1}.
-foo() ->
-#{a =><<"a">>
-,b=>1}.
-
-%% Module attributes should be highlighted
-
--export([t/1]).
--record(record1, {a,
- b,
- c
-}).
--record(record2, {
- a,
- b
- }).
-
--record(record3, {a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
-234,
- d}).
-
--record(record4, {
- a = 8#42423 bor
- 8#4234,
- b = 8#5432
- bor 2#1010101
- c = 123 +
- 234,
- d}).
-
--record(record5, { a = 1 :: integer()
-, b = foobar :: atom()
-}).
-
--define(MACRO_1, macro).
--define(MACRO_2(_), macro).
-
--spec t(integer()) -> any().
-
--type ann() :: Var :: integer().
--type ann2() :: Var ::
- 'return'
- | 'return_white_spaces'
- | 'return_comments'
- | 'text' | ann().
--type paren() ::
- (ann2()).
--type t1() :: atom().
--type t2() :: [t1()].
--type t3(Atom) :: integer(Atom).
--type t4() :: t3(foobar).
--type t5() :: {t1(), t3(foo)}.
--type t6() :: 1 | 2 | 3 |
- 'foo' | 'bar'.
--type t7() :: [].
--type t71() :: [_].
--type t8() :: {any(),none(),pid(),port(),
- reference(),float()}.
--type t9() :: [1|2|3|foo|bar] |
- list(a | b | c) | t71().
--type t10() :: {1|2|3|foo|t9()} | {}.
--type t11() :: 1..2.
--type t13() :: maybe_improper_list(integer(), t11()).
--type t14() :: [erl_scan:foo() |
- %% Should be highlighted
- term() |
- bool() |
- byte() |
- char() |
- non_neg_integer() | nonempty_list() |
- pos_integer() |
- neg_integer() |
- number() |
- list() |
- nonempty_improper_list() | nonempty_maybe_improper_list() |
- maybe_improper_list() | string() | iolist() | byte() |
- module() |
- mfa() |
- node() |
- timeout() |
- no_return() |
- %% Should not be highlighted
- nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
-
-
--type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
- <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
-<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
-<<_:34>>|<<_:34>>|<<_:34>>].
--type t16() :: fun().
--type t17() :: fun((...) -> paren()).
--type t18() :: fun(() -> t17() | t16()).
--type t19() :: fun((t18()) -> t16()) |
- fun((nonempty_maybe_improper_list('integer', any())|
- 1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
-nonempty_maybe_improper_list('integer', any())|
-1|2|3|a|b|<<_:3,_:_*14>>|integer()).
--type t20() :: [t19(), ...].
--type t21() :: tuple().
--type t21(A) :: A.
--type t22() :: t21(integer()).
--type t23() :: #rec1{}.
--type t24() :: #rec2{a :: t23(), b :: [atom()]}.
--type t25() :: #rec3{f123 :: [t24() |
-1|2|3|4|a|b|c|d|
-nonempty_maybe_improper_list(integer, any())]}.
--type t26() :: #rec4{ a :: integer()
-, b :: any()
-}.
--type t27() :: { integer()
-, atom()
-}.
--type t99() ::
-{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
-t15(),t20(),t21(), t22(),t25()}.
--spec t1(FooBar :: t99()) -> t99();
-(t2()) -> t2();
- (t4()) -> t4() when is_subtype(t4(), t24);
-(t23()) -> t23() when is_subtype(t23(), atom()),
- is_subtype(t23(), t14());
-(t24()) -> t24() when is_subtype(t24(), atom()),
- is_subtype(t24(), t14()),
- is_subtype(t24(), t4()).
-
--spec over(I :: integer()) -> R1 :: foo:typen();
- (A :: atom()) -> R2 :: foo:atomen();
- (T :: tuple()) -> R3 :: bar:typen().
-
--spec mod:t2() -> any().
-
--spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec handle_cast(Cast ::
- {'exchange', node(), [[name(),...]]}
- | {'del_member', name(), pid()},
- #state{}) -> {'noreply', #state{}}.
-
--spec all(fun((T) -> boolean()), List :: [T]) ->
- boolean() when is_subtype(T, term()). % (*)
-
--spec get_closest_pid(term()) ->
- Return :: pid()
- | {'error', {'no_process', term()}
- | {'no_such_group', term()}}.
-
--spec add( X :: integer()
-, Y :: integer()
-) -> integer().
-
--opaque attributes_data() ::
-[{'column', column()} | {'line', info_line()} |
- {'text', string()}] | {line(),column()}.
--record(r,{
- f1 :: attributes_data(),
-f222 = foo:bar(34, #rec3{}, 234234234423,
- aassdsfsdfsdf, 2234242323) ::
-[t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f333 :: [t24() | 1|2|3|4|a|b|c|d|
- nonempty_maybe_improper_list(integer, any())],
-f3 = x:y(),
-f4 = x:z() :: t99(),
-f17 :: 'undefined',
-f18 :: 1 | 2 | 'undefined',
-f19 = 3 :: integer()|undefined,
-f5 = 3 :: undefined|integer()}).
-
--record(state, {
- sequence_number = 1 :: integer()
- }).
-
-
-highlighting(X) % Function definitions should be highlighted
- when is_integer(X) -> % and so should `when' and `is_integer' be
- %% Highlighting
- %% Various characters (we keep an `atom' after to see that highlighting ends)
- $a,atom, % Characters should be marked
- "string",atom, % and strings
- 'asdasd',atom, % quote should be atoms??
- 'VaV',atom,
- 'aVa',atom,
- '\'atom',atom,
- 'atom\'',atom,
- 'at\'om',atom,
- '#1',atom,
-
- $", atom, % atom should be ok
- $', atom,
-
- "string$", atom, "string$", atom, % currently buggy I know...
- "string\$", atom, % workaround for bug above
-
- "char $in string", atom,
-
- 'atom$', atom, 'atom$', atom,
- 'atom\$', atom,
-
- 'char $in atom', atom,
-
- $[, ${, $\\, atom,
- ?MACRO_1,
- ?MACRO_2(foo),
-
- %% Numerical constants
- 16#DD, % AD Should not be highlighted
- 32#dd, % AD Should not be highlighted
- 32#ddAB, % AD Should not be highlighted
- 32#101, % AD Should not be highlighted
- 32#ABTR, % AD Should not be highlighted
-
- %% Variables
- Variables = lists:foo(),
- _Variables = lists:foo(), % AD
- AppSpec = Xyz/2,
- Module42 = Xyz(foo, bar),
- Module:foo(),
- _Module:foo(), % AD
- FooÅÅ = lists:reverse([tl,hd,tl,hd]), % AD Should highlight FooÅÅ
- _FooÅÅ = 42, % AD Should highlight _FooÅÅ
-
- %% Bifs
- erlang:registered(),
- registered(),
- hd(tl(tl(hd([a,b,c])))),
- erlang:anything(lists),
- %% Guards
- is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
- is_function(Fun), is_pid(self()),
- not_a_guard:is_list([]),
- %% Other Types
-
- atom, % not (currently) hightlighted
- 234234,
- 234.43,
-
- [list, are, not, higlighted],
- {nor, is, tuple},
- ok.
-
-%%%
-%%% Indentation
-%%%
-
-%%% Left
-
-%% Indented
-
-% Right
-
-
-indent_basics(X, Y, Z)
- when X > 42,
-Z < 13;
-Y =:= 4711 ->
- %% comments
- % right comments
- case lists:filter(fun(_, AlongName,
- B,
- C) ->
- true
- end,
- [a,v,b])
- of
- [] ->
- Y = 5 * 43,
- ok;
- [_|_] ->
- Y = 5 * 43,
- ok
- end,
- Y,
- %% List, tuples and binaries
- [a,
- b, c
- ],
- [ a,
- b, c
- ],
-
- [
- a,
- b
-],
- {a,
- b,c
- },
- { a,
- b,c
- },
-
- {
- a,
- b
- },
-
-<<1:8,
- 2:8
- >>,
- <<
- 1:8,
- 2:8
- >>,
- << 1:8,
- 2:8
- >>,
-
- (a,
- b,
- c
- ),
-
- ( a,
- b,
- c
- ),
-
-
- (
- a,
- b,
- c
- ),
-
- call(2#42423 bor
- #4234,
- 2#5432,
- other_arg),
- ok;
-indent_basics(Xlongname,
- #struct{a=Foo,
- b=Bar},
- [X|
- Y]) ->
- testing_next_clause,
- ok;
-indent_basics( % AD added clause
- X, % not sure how this should look
- Y,
- Z)
- when
- X < 42, Z > 13;
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) when % AD added clause
- X < 42, Z > 13; % testing when indentation
- Y =:= 4711 ->
- foo;
-indent_basics(X, Y, Z) % AD added clause
- when % testing when indentation
- X < 42, Z > 13; % unsure about this one
- Y =:= 4711 ->
- foo.
-
-
-
-indent_nested() ->
- [
- {foo, 2, "string"},
- {bar, 3, "another string"}
- ].
-
-
-indent_icr(Z) -> % icr = if case receive
- %% If
- if Z >= 0 ->
- X = 43 div 4,
- foo(X);
- Z =< 10 ->
- X = 43 div 4,
- foo(X);
- Z == 5 orelse
- Z == 7 ->
- X = 43 div 4,
- foo(X);
- true ->
- if_works
- end,
- %% Case
- case {Z, foo, bar} of
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- {Z,_,_} when
- Z =:= 42 -> % AD line should be indented as a when
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when Z < 10 -> % AD when should be indented
- X = 43 div 4,
- foo(X);
- {Z,_,_}
- when % AD when should be indented
- Z < 10 % and the guards should follow when
- andalso % unsure about how though
- true ->
- X = 43 div 4,
- foo(X)
- end,
- %% begin
- begin
- sune,
- X = 74234 + foo(8456) +
- 345 div 43,
- ok
- end,
-
-
- %% receive
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- end,
- receive
- {Z,_,_} ->
- X = 43 div 4,
- foo(X);
- Z % AD added clause
- when Z =:= 1 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z when % AD added clause
- Z =:= 2 -> % This line should be indented by 2
- X = 43 div 4,
- foo(X);
- Z ->
- X = 43 div 4,
- foo(X)
- after infinity ->
- foo(X),
- asd(X),
- 5*43
- end,
- receive
- after 10 ->
- foo(X),
- asd(X),
- 5*43
- end,
- ok.
-
-indent_fun() ->
- %% Changed fun to one indention level
-Var = spawn(fun(X)
- when X == 2;
- X > 10 ->
- hello,
- case Hello() of
- true when is_atom(X) ->
- foo;
- false ->
- bar
- end;
- (Foo) when is_atom(Foo),
- is_integer(X) ->
- X = 6* 45,
- Y = true andalso
- kalle
- end),
-%% check EEP37 named funs
-Fn1 = fun Fact(N) when N > 0 ->
- F = Fact(N-1),
- N * F;
-Fact(0) ->
- 1
- end,
-%% check anonymous funs too
- Fn2 = fun(0) ->
-1;
- (N) ->
- N
- end,
- ok.
-
-indent_try_catch() ->
- try
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R % AD added clause
- when R =:= 42 -> % when should be indented
- foo(R);
- error:R % AD added clause
- when % when should be indented
- R =:= 42 -> % but unsure about this (maybe 2 more)
- foo(R);
- error:R when % AD added clause
- R =:= foo -> % line should be 2 indented (works)
- foo(R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile)
- end;
-indent_try_catch() ->
- try
- foo(bar)
- of
- X when true andalso
- kalle ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2);
- X % AD added clause
- when false andalso % when should be 2 indented
- bengt ->
- gurka();
- X when % AD added clause
- false andalso % line should be 2 indented
- not bengt ->
- gurka();
- X ->
- io:format(stdout, "Parsing file ~s, ",
- [St0#leex.xfile]),
- {ok,Line3,REAs,Actions,St3} =
- parse_rules(Xfile, Line2, Macs, St2)
- catch
- exit:{badarg,R} ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R);
- error:R ->
- foo(R),
- io:format(stdout,
- "ERROR reason ~p~n",
- R)
- after
- foo('after'),
- file:close(Xfile),
- bar(with_long_arg,
- with_second_arg)
- end;
- indent_try_catch() ->
- try foo()
- after
- foo(),
- bar(with_long_arg,
- with_second_arg)
- end.
-
-indent_catch() ->
- D = B +
- float(43.1),
-
- B = catch oskar(X),
-
- A = catch (baz +
- bax),
- catch foo(),
-
- C = catch B +
- float(43.1),
-
- case catch foo(X) of
- A ->
- B
- end,
-
- case
- catch foo(X)
- of
- A ->
- B
- end,
-
- case
- foo(X)
- of
- A ->
- catch B,
- X
- end,
-
- try sune of
- _ -> foo
- catch _:_ -> baf
- end,
-
- try
-sune
- of
- _ ->
- X = 5,
- (catch foo(X)),
- X + 10
- catch _:_ -> baf
- end,
-
- try
- (catch sune)
- of
- _ ->
- catch foo() %% BUGBUG can't handle catch inside try without parentheses
- catch _:_ ->
- baf
- end,
-
- try
-(catch exit())
- catch
-_ ->
- catch baf()
- end,
- ok.
-
-indent_binary() ->
- X = lists:foldr(fun(M) ->
- <<Ma/binary, " ">>
- end, [], A),
- A = <<X/binary, 0:8>>,
- B.
-
-
-indent_comprehensions() ->
-%% I don't have a good idea how we want to handle this
-%% but they are here to show how they are indented today.
-Result1 = [X ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- ],
-Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- ],
-
-Binary1 = << <<X:8>> ||
- #record{a=X} <- lists:seq(1, 10),
- true = (X rem 2)
- >>,
-
-Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
- true = (X rem 2)
- >>,
-ok.
-
-%% This causes an error in earlier erlang-mode versions.
-foo() ->
-[#foo{
-foo = foo}].
-
-%% Record indentation
-some_function_with_a_very_long_name() ->
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b},
- case dummy_function_with_a_very_very_long_name(x) of
- #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- ok;
- Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b} ->
- Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
- field1=a,
- field2=b};
- #xyz{
- a=1,
- b=2} ->
- ok
- end.
-
-another_function_with_a_very_very_long_name() ->
- #rec{
- field1=1,
- field2=1}.
-
-some_function_name_xyz(xyzzy, #some_record{
- field1=Field1,
- field2=Field2}) ->
- SomeVariable = f(#'Some-long-record-name'{
- field_a = 1,
- 'inter-xyz-parameters' =
- #'Some-other-very-long-record-name'{
- field2 = Field1,
- field2 = Field2}}),
- {ok, SomeVariable}.
-
-commas_first() ->
- {abc, [ {some_var, 1}
- , {some_other_var, 2}
- , {erlang_ftw, 9}
- , {erlang_cookie, 'cookie'}
- , {cmds,
- [ {one, "sudo ls"}
- , {one, "sudo ls"}
- , {two, "sudo ls"}
- , {three, "sudo ls"}
- , {four, "sudo ls"}
- , {three, "sudo ls"}
- ] }
- , {ssh_username, "yow"}
- , {cluster,
- [ {aaaa, [ {"10.198.55.12" , "" }
- , {"10.198.55.13" , "" }
- ] }
- , {bbbb, [ {"10.198.55.151", "" }
- , {"10.198.55.123", "" }
- , {"10.198.55.34" , "" }
- , {"10.198.55.85" , "" }
- , {"10.198.55.67" , "" }
- ] }
- , {cccc, [ {"10.198.55.68" , "" }
- , {"10.198.55.69" , "" }
- ] }
- ] }
-]
-}.
-
-
-%% this used to result in a scan-sexp error
-[{
-}].
-
-%% this used to result in 2x the correct indentation within the function
-%% body, due to the function name being mistaken for a keyword
-catcher(N) ->
-try generate_exception(N) of
-Val -> {N, normal, Val}
-catch
-throw:X -> {N, caught, thrown, X};
-exit:X -> {N, caught, exited, X};
-error:X -> {N, caught, error, X}
-end.
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
index 77a8813db5..f4e78da667 100644
--- a/lib/tools/test/emacs_SUITE.erl
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -23,10 +23,10 @@
-export([all/0, init_per_testcase/2, end_per_testcase/2]).
--export([bif_highlight/1]).
+-export([bif_highlight/1, indent/1]).
-all() ->
- [bif_highlight].
+all() ->
+ [bif_highlight, indent].
init_per_testcase(_Case, Config) ->
ErlangEl = filename:join([code:lib_dir(tools),"emacs","erlang.el"]),
@@ -74,4 +74,69 @@ check_bif_highlight(Bin, Tag, Compare) ->
[] = Compare -- EmacsIntBifs,
[] = EmacsIntBifs -- Compare.
-
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+indent(Config) ->
+ case emacs_version_ok() of
+ false -> {skip, "Old or no emacs found"};
+ true ->
+ Def = filename:dirname(code:which(?MODULE)) ++ "/" ++ ?MODULE_STRING ++ "_data",
+ Dir = proplists:get_value(data_dir, Config, Def),
+ OrigFs = filelib:wildcard(Dir ++ "/*"),
+ io:format("Dir: ~s~nFs: ~p~n", [Dir, OrigFs]),
+ Fs = [{File, unindent(File)} || File <- OrigFs,
+ filename:extension(File) =:= ""],
+ Indent = fun emacs/1,
+ [Indent(File) || {_, File} <- Fs],
+ Res = [diff(Orig, File) || {Orig, File} <- Fs],
+ [file:delete(File) || {ok, File} <- Res], %% Cleanup
+ [] = [Fail || {fail, Fail} <- Res],
+ ok
+ end.
+
+unindent(Input) ->
+ Output = Input ++ ".erl",
+ {ok, Bin} = file:read_file(Input),
+ Lines0 = string:split(Bin, "\n", all),
+ Lines = [string:trim(Line, leading, [$\s,$\t]) || Line <- Lines0],
+ %% io:format("File: ~s lines: ~w~n", [Input, length(Lines0)]),
+ %% [io:format("~s~n", [L]) || L <- Lines],
+ ok = file:write_file(Output, lists:join("\n", Lines)),
+ Output.
+
+diff(Orig, File) ->
+ case os:cmd(["diff ", Orig, " ", File]) of
+ "" -> {ok, File};
+ Diff ->
+ io:format("Fail: ~s vs ~s~n~s~n~n",[Orig, File, Diff]),
+ {fail, File}
+ end.
+
+emacs_version_ok() ->
+ case os:cmd("emacs --version | head -1") of
+ "GNU Emacs " ++ Ver ->
+ case string:to_float(Ver) of
+ {Vsn, _} when Vsn >= 24.1 ->
+ true;
+ _ ->
+ io:format("Emacs version fail~n~s~n~n",[Ver]),
+ false
+ end;
+ Res ->
+ io:format("Emacs version fail~n~s~n~n",[Res]),
+ false
+ end.
+
+emacs(File) ->
+ EmacsErlDir = filename:join([code:lib_dir(tools), "emacs"]),
+ Cmd = ["emacs ",
+ "--batch --quick ",
+ "--directory ", EmacsErlDir, " ",
+ "--eval \"(require 'erlang-start)\" ",
+ File, " ",
+ "--eval '(indent-region (point-min) (point-max) nil)' ",
+ "--eval '(save-buffer 0)'"
+ ],
+ _Res = os:cmd(Cmd),
+ % io:format("cmd ~s:~n=> ~s~n", [Cmd, _Res]),
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/comments b/lib/tools/test/emacs_SUITE_data/comments
new file mode 100644
index 0000000000..ff974ca295
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comments
@@ -0,0 +1,25 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% 3 comment chars: always left indented
+%%% 2 comment chars: Context indented
+%%% 1 comment char: Rigth indented
+
+%%% left
+%% context dependent
+ % rigth
+
+func() ->
+%%% left
+ %% context dependent
+ % right indented
+ case get(foo) of
+ undefined ->
+ %% Testing indention
+ ok;
+ %% Catch all
+ Other ->
+ Other
+ end,
+ ok.
+
diff --git a/lib/tools/test/emacs_SUITE_data/comprehensions b/lib/tools/test/emacs_SUITE_data/comprehensions
new file mode 100644
index 0000000000..45279850a5
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/comprehensions
@@ -0,0 +1,47 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of comprehensions
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+list() ->
+ %% I don't have a good idea how we want to handle this
+ %% but they are here to show how they are indented today.
+ Result1 = [X ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ ],
+ Res = [ func(X,
+ arg2)
+ ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ ],
+ Result1.
+
+binary(B) ->
+ Binary1 = << <<X:8>> ||
+ #record{a=X} <- lists:seq(1, 10),
+ true = (X rem 2)
+ >>,
+
+ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+
+ Bin3 = <<
+ <<
+ X:8,
+ 34:8
+ >>
+ || <<X:32,_:32>> <= <<0:512>>,
+ true = (X rem 2)
+ >>,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/funcs b/lib/tools/test/emacs_SUITE_data/funcs
new file mode 100644
index 0000000000..877f982005
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/funcs
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Function (and funs) indentation
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+-export([
+ func1/0,
+ func2/0,
+ a_function_with_a_very_very_long_name/0,
+ when1/2
+ ]).
+
+-compile([nowarn_unused_functions,
+ {inline, [
+ func2/2,
+ func3/2
+ ]
+ }
+ ]).
+
+func1() ->
+ basic.
+
+func2(A1,
+ A2) ->
+ ok.
+
+func3(
+ A1,
+ A2
+ ) ->
+ ok.
+
+%% Okeefe style
+func4(A1
+ ,A2
+ ,A3
+ ) ->
+ ok.
+
+func5(
+ A41
+ ,A42) ->
+ ok.
+
+a_function_with_a_very_very_long_name() ->
+ A00 = #record{
+ field1=1,
+ field2=1
+ },
+ A00.
+
+when1(W1, W2)
+ when is_number(W1),
+ is_number(W2) ->
+ ok.
+
+when2(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+when3(W1,W2,W3) when
+ W1 > W2,
+ W2 > W3
+ ->
+ ok.
+
+when4(W1,W2,W3)
+ when
+ W1 > W2,
+ W2 > W3 ->
+ ok.
+
+match1({[H|T],
+ Other},
+ M1A2) ->
+ ok.
+
+match2(
+ {
+ [H|T],
+ Other
+ },
+ M2A2
+ ) ->
+ ok.
+
+match3({
+ M3A1,
+ [
+ H |
+ T
+ ],
+ Other
+ },
+ M3A2
+ ) ->
+ ok.
+
+match4(<<
+ M4A:8,
+ M4B:16/unsigned-integer,
+ _/binary
+ >>,
+ M4C) ->
+ ok.
+
+match5(M5A,
+ #record{
+ b=M5B,
+ c=M5C
+ }
+ ) ->
+ ok.
+
+match6(M6A,
+ #{key6a := a6,
+ key6b := b6
+ }) ->
+ ok.
+
+funs(1)
+ when
+ X ->
+ %% Changed fun to one indention level
+ %% 'when' and several clause forces a depth of '4'
+ Var = spawn(fun(X, _)
+ when X == 2;
+ X > 10 ->
+ hello,
+ case Hello() of
+ true when is_atom(X) ->
+ foo;
+ false ->
+ bar
+ end;
+ (Foo) when is_atom(Foo),
+ is_integer(X) ->
+ X = 6 * 45,
+ Y = true andalso
+ kalle
+ end),
+ Var;
+funs(2) ->
+ %% check EEP37 named funs
+ Fn1 = fun
+ Factory(N) when
+ N > 0 ->
+ F = Fact(N-1),
+ N * F;
+ Factory(0) ->
+ 1
+ end,
+ Fn1;
+funs(3) ->
+ %% check anonymous funs too
+ Fn2 = fun(0) ->
+ 1;
+ (N) ->
+ N
+ end,
+ ok;
+funs(4) ->
+ X = lists:foldr(fun(M) ->
+ <<M/binary, " ">>
+ end, [], Z),
+ A = <<X/binary, 0:8>>,
+ A.
diff --git a/lib/tools/test/emacs_SUITE_data/highlight b/lib/tools/test/emacs_SUITE_data/highlight
new file mode 100644
index 0000000000..0719f6516a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/highlight
@@ -0,0 +1,78 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Open this file in your editor and manually check the colors of
+%%% different types and calls and builtin words
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+highlighting(X) % Function definitions should be highlighted
+ when is_integer(X) -> % and so should `when' and `is_integer' be
+ %% Highlighting
+ %% Various characters (we keep an `atom' after to see that highlighting ends)
+ $a,atom, % Characters should be marked
+ "string",atom, % and strings
+ 'asdasd',atom, % quote should be atoms??
+ 'VaV',atom,
+ 'aVa',atom,
+ '\'atom',atom,
+ 'atom\'',atom,
+ 'at\'om',atom,
+ '#1',atom,
+
+ $", atom, % atom should be ok
+ $', atom,
+
+ "string$", atom, "string$", atom, % currently buggy I know...
+ "string\$", atom, % workaround for bug above
+
+ "char $in string", atom,
+
+ 'atom$', atom, 'atom$', atom,
+ 'atom\$', atom,
+
+ 'char $in atom', atom,
+
+ $[, ${, $\\, atom,
+ ?MACRO_1,
+ ?MACRO_2(foo),
+
+ %% Numerical constants
+ 16#DD, % Should not be highlighted
+ 32#dd, % Should not be highlighted
+ 32#ddAB, % Should not be highlighted
+ 32#101, % Should not be highlighted
+ 32#ABTR, % Should not be highlighted
+
+ %% Variables
+ Variables = lists:foo(),
+ _Variables = lists:foo(),
+ AppSpec = Xyz/2,
+ Module42 = Xyz(foo, bar),
+ Module:foo(),
+ _Module:foo(), %
+ FooÅÅ = lists:reverse([tl,hd,tl,hd]), % Should highlight FooÅÅ
+ _FooÅÅ = 42, % Should highlight _FooÅÅ
+
+ %% Bifs
+ erlang:registered(),
+ registered(),
+ hd(tl(tl(hd([a,b,c])))),
+ erlang:anything(lists),
+ %% Guards
+ is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
+ is_function(Fun), is_pid(self()),
+ not_a_guard:is_list([]),
+ %% Other Types
+
+ atom, % not (currently) hightlighted
+ 234234,
+ 234.43,
+
+ [list, are, not, higlighted],
+ {nor, is, tuple},
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/icr b/lib/tools/test/emacs_SUITE_data/icr
new file mode 100644
index 0000000000..8445c1a74d
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/icr
@@ -0,0 +1,157 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of if case receive statements
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+indent_if(1, Z) ->
+ %% If
+ if Z >= 0 ->
+ X = 43 div Z,
+ X;
+ Z =< 10 ->
+ X = 43 div Z,
+ X;
+ Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X;
+ is_number(Z),
+ Z < 32 ->
+ Z;
+ is_number(Z);
+ Z < 32 ->
+ Z * 32;
+ true ->
+ if_works
+ end;
+indent_if(2, Z) ->
+ %% If
+ if
+ Z >= 0 ->
+ X = 43 div Z,
+ X
+ ; Z =< 10 ->
+ 43 div Z
+ ; Z == 5 orelse
+ Z == 7 ->
+ X = 43 div Z,
+ X
+ ; is_number(Z),
+ Z < 32 ->
+ Z
+ ; true ->
+ if_works
+ end.
+
+indent_case(1, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X);
+ {Z,_,_}
+ when Z < 10 orelse
+ Z =:= foo -> % Binary op alignment here !!!
+ X = 43 div 4,
+ Bool = Z < 5 orelse % Binary op args align differently after when
+ Z =:= foo, % and elsewhere ???
+ foo(X);
+ {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end;
+indent_case(2, Z) ->
+ %% Case
+ case {Z, foo, bar} of
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_} when
+ Z =:= 42 -> % line should be indented as a when
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when Z < 10 -> % when should be indented
+ X = 43 div 4,
+ foo(X)
+ ; {Z,_,_}
+ when % when should be indented
+ Z < 10 % and the guards should follow when
+ andalso % unsure about how though
+ true ->
+ X = 43 div 4,
+ foo(X)
+ end.
+
+indent_begin(Z) ->
+ %% Begin
+ begin
+ sune,
+ Z = 74234 +
+ foo(8456) +
+ 345 div 43,
+ Foo = begin
+ ok,
+ foo(234),
+ begin
+ io:format("Down here\n")
+ end
+ end,
+ {Foo,
+ bar}
+ end.
+
+indent_receive(1) ->
+ %% receive
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X)
+ ; Z ->
+ X = 43 div 4,
+ foo(X)
+ end,
+ ok;
+indent_receive(2) ->
+ receive
+ {Z,_,_} ->
+ X = 43 div 4,
+ foo(X);
+ Z % added clause
+ when Z =:= 1 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z when % added clause
+ Z =:= 2 -> % This line should be indented by 2
+ X = 43 div 4,
+ foo(X);
+ Z ->
+ X = 43 div 4,
+ foo(X)
+ after infinity ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok;
+indent_receive() ->
+ receive
+ after 10 ->
+ foo(X),
+ asd(X),
+ 5*43
+ end,
+ ok.
diff --git a/lib/tools/test/emacs_SUITE_data/macros b/lib/tools/test/emacs_SUITE_data/macros
new file mode 100644
index 0000000000..6c874e9187
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/macros
@@ -0,0 +1,31 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Macros should be indented as code
+
+-define(M0, ok).
+
+-define(M1,
+ case X of
+ undefined -> error;
+ _ -> ok
+ end).
+
+-define(M2(M2A1,
+ M2A2),
+ func(M2A1,
+ M2A2)
+ ).
+
+-define(
+ M3,
+ undefined
+ ).
+
+-ifdef(DEBUG).
+-define(LOG,
+ logger:log(?MODULE,?LINE)
+ ).
+-else().
+-define(LOG, ok).
+-endif().
diff --git a/lib/tools/test/emacs_SUITE_data/records b/lib/tools/test/emacs_SUITE_data/records
new file mode 100644
index 0000000000..241582718c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/records
@@ -0,0 +1,35 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Test that records are indented correctly
+
+-record(record0,
+ {
+ r0a,
+ r0b,
+ r0c
+ }).
+
+-record(record1, {r1a,
+ r1b,
+ r1c
+ }).
+
+-record(record2, {
+ r2a,
+ r2b
+ }).
+
+-record(record3, {r3a = 8#42423 bor
+ 8#4234,
+ r3b = 8#5432
+ bor 2#1010101,
+ r3c = 123 +
+ 234,
+ r3d}).
+
+-record(record5,
+ { r5a = 1 :: integer()
+ , r5b = foobar :: atom()
+ }).
+
diff --git a/lib/tools/test/emacs_SUITE_data/terms b/lib/tools/test/emacs_SUITE_data/terms
new file mode 100644
index 0000000000..352364a73c
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/terms
@@ -0,0 +1,174 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% indentation of terms contain builtin types
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+
+list(1) ->
+ [a,
+ b,
+ c
+ ];
+list(2) ->
+ [ a,
+ b, c
+ ];
+list(3) ->
+ [
+ a,
+ b, c
+ ];
+list(4) ->
+ [ a
+ , b
+ , c
+ ].
+
+tuple(1) ->
+ {a,
+ b,c
+ };
+tuple(2) ->
+ { a,
+ b,c
+ };
+tuple(3) ->
+ {
+ a,
+ b,c
+ };
+tuple(4) ->
+ { a
+ , b
+ ,c
+ }.
+
+binary(1) ->
+ <<1:8,
+ 2:8
+ >>;
+binary(2) ->
+ <<
+ 1:8,
+ 2:8
+ >>;
+binary(3) ->
+ << 1:8,
+ 2:8
+ >>;
+binary(4) ->
+ <<
+ 1:8
+ ,2:8
+ >>;
+binary(5) ->
+ << 1:8
+ , 2:8
+ >>.
+
+record(1) ->
+ #record{a=1,
+ b=2
+ };
+record(2) ->
+ #record{ a=1,
+ b=2
+ };
+record(3) ->
+ #record{
+ a=1,
+ b=2
+ };
+record(4) ->
+ #record{
+ a=1
+ ,b=2
+ };
+record(Record) ->
+ Record#record{
+ a=1
+ ,b=2
+ }.
+
+map(1) ->
+ #{a=>1,
+ b=>2
+ };
+map(2) ->
+ #{ a=>1,
+ b=>2
+ };
+map(3) ->
+ #{
+ a=>1,
+ b=>2
+ };
+map(4) ->
+ #{
+ a => <<"a">>
+ ,b => 2
+ };
+map(MapVar) ->
+ MapVar = #{a :=<<"a">>
+ ,b:=1}.
+
+deep(Rec) ->
+ Rec#rec{ atom = 'atom',
+ map = #{ k1 => {v,
+ 1},
+ k2 => [
+ 1,
+ 2,
+ 3
+ ],
+ {key,
+ 3}
+ =>
+ <<
+ 123:8,
+ 255:8
+ >>
+ }
+ }.
+
+%% Record indentation
+some_function_with_a_very_long_name() ->
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b},
+ case dummy_function_with_a_very_very_long_name(x) of
+ #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ ok;
+ Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b} ->
+ Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
+ field1=a,
+ field2=b};
+ #xyz{
+ a=1,
+ b=2} ->
+ ok
+ end.
+
+some_function_name_xyz(xyzzy, #some_record{
+ field1=Field1,
+ field2=Field2}) ->
+ SomeVariable = f(#'Some-long-record-name'{
+ field_a = 1,
+ 'inter-xyz-parameters' =
+ #'Some-other-very-long-record-name'{
+ field2 = Field1,
+ field2 = Field2}}),
+ {ok, SomeVariable}.
+
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/test/emacs_SUITE_data/try_catch b/lib/tools/test/emacs_SUITE_data/try_catch
new file mode 100644
index 0000000000..0005b2003a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/try_catch
@@ -0,0 +1,166 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%%% Try and catch indentation is hard
+
+%%% Not everything in these test are set in stone
+%%% better indentation rules can be added but by having
+%%% these tests we can see what changes in new implementations
+%%% and notice when doing unintentional changes
+
+try_catch() ->
+ try
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R
+ when R =:= 42 -> % when should be indented
+ foo(R);
+ error:R
+ when % when should be indented
+ R =:= 42 -> % but unsure about this (maybe 2 more)
+ foo(R);
+ error:R when
+ R =:= foo -> % line should be 2 indented (works)
+ foo(R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile)
+ end;
+try_catch() ->
+ try
+ foo(bar)
+ of
+ X when true andalso
+ kalle ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2);
+ X
+ when false andalso % when should be 2 indented
+ bengt ->
+ gurka();
+ X when
+ false andalso % line should be 2 indented
+ not bengt ->
+ gurka();
+ X ->
+ io:format(stdout, "Parsing file ~s, ",
+ [St0#leex.xfile]),
+ {ok,Line3,REAs,Actions,St3} =
+ parse_rules(Xfile, Line2, Macs, St2)
+ catch
+ exit:{badarg,R} ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R);
+ error:R ->
+ foo(R),
+ io:format(stdout,
+ "ERROR reason ~p~n",
+ R)
+ after
+ foo('after'),
+ file:close(Xfile),
+ bar(with_long_arg,
+ with_second_arg)
+ end;
+try_catch() ->
+ try foo()
+ after
+ foo(),
+ bar(with_long_arg,
+ with_second_arg)
+ end.
+
+indent_catch() ->
+ D = B +
+ float(43.1),
+
+ B = catch oskar(X),
+
+ A = catch (baz +
+ bax),
+ catch foo(),
+
+ C = catch B +
+ float(43.1),
+
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
+ try sune of
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ Variable = try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ after cleanup()
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ foo1(),
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
+
+%% this used to result in 2x the correct indentation within the function
+%% body, due to the function name being mistaken for a keyword
+catcher(N) ->
+ try generate_exception(N) of
+ Val -> {N, normal, Val}
+ catch
+ throw:X -> {N, caught, thrown, X};
+ exit:X -> {N, caught, exited, X};
+ error:X -> {N, caught, error, X}
+ end.
diff --git a/lib/tools/test/emacs_SUITE_data/type_specs b/lib/tools/test/emacs_SUITE_data/type_specs
new file mode 100644
index 0000000000..e71841cc7a
--- /dev/null
+++ b/lib/tools/test/emacs_SUITE_data/type_specs
@@ -0,0 +1,110 @@
+%% -*- Mode: erlang; indent-tabs-mode: nil -*-
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+
+%% Tests how types and specs are indented (also that the editor can parse them)
+%% May need improvements
+
+
+-type ann() :: Var :: integer().
+-type ann2() ::
+ 'return'
+ | 'return_white_spaces'
+ | 'return_comments'
+ | 'text' | ann().
+-type paren() ::
+ (ann2()).
+
+-type t6() ::
+ 1 | 2 | 3 |
+ 'foo'
+ | 'bar'.
+
+-type t8() :: {any(),none(),pid(),port(),
+ reference(),float()}.
+
+-type t14() :: [erl_scan:foo() |
+ %% Should be highlighted
+ term() |
+ boolean() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
+ nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
+ %% Should not be highlighted
+ nonempty_() | nonlist() |
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
+ <<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
+ <<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
+ <<_:34>>|<<_:34>>|<<_:34>>].
+
+-type t18() ::
+ fun(() -> t17() | t16()).
+-type t19() ::
+ fun((t18()) -> t16()) |
+ fun((nonempty_maybe_improper_list('integer', any())|
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer())
+ ->
+ nonempty_maybe_improper_list('integer', any())| %% left to col 16?
+ 1|2|3|a|b|<<_:3,_:_*14>>|integer()). %% left to col 16?
+-type t20() :: [t19(), ...].
+-type t25() :: #rec3{f123 :: [t24() |
+ 1|2|3|4|a|b|c|d|
+ nonempty_maybe_improper_list(integer, any())]}.
+-type t26() :: #rec4{ a :: integer()
+ , b :: any()
+ }.
+
+%% Spec
+
+-spec t1(FooBar :: t99()) -> t99();
+ (t2()) -> t2();
+ (t4()) -> t4() when is_subtype(t4(), t24);
+ (t23()) -> t23() when is_subtype(t23(), atom()),
+ is_subtype(t23(), t14());
+ (t24()) -> t24() when is_subtype(t24(), atom()),
+ is_subtype(t24(), t14()),
+ is_subtype(t24(), t4()).
+
+-spec over(I :: integer()) -> R1 :: foo:typen();
+ (A :: atom()) -> R2 :: foo:atomen();
+ (T :: tuple()) -> R3 :: bar:typen().
+
+-spec mod:t2() -> any().
+
+-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) -> {'noreply', #state{}}.
+
+-spec handle_cast(Cast ::
+ {'exchange', node(), [[name(),...]]}
+ | {'del_member', name(), pid()},
+ #state{}) ->
+ {'noreply', #state{}}. %% left to col 10?
+
+-spec all(fun((T) -> boolean()), List :: [T]) ->
+ boolean() when is_subtype(T, term()). % (*)
+
+-spec get_closest_pid(term()) ->
+ Return :: pid() %% left to col 10?
+ | {'error', {'no_process', term()}} %% left to col 10?
+ | {'no_such_group', term()}. %% left to col 10?
+
+-spec add( X :: integer()
+ , Y :: integer()
+ ) -> integer().
+
+-opaque attributes_data() ::
+ [{'column', column()} | {'line', info_line()} |
+ {'text', string()}] | {line(),column()}.
diff --git a/otp_versions.table b/otp_versions.table
index b25117e072..9745cc1357 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -17,6 +17,7 @@ OTP-20.0.3 : asn1-5.0.2 compiler-7.1.1 erts-9.0.3 ssh-4.5.1 # common_test-1.15.1
OTP-20.0.2 : asn1-5.0.1 erts-9.0.2 kernel-5.3.1 # common_test-1.15.1 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12.1 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4.1 syntax_tools-2.1.2 tools-2.10.1 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0.1 : common_test-1.15.1 erts-9.0.1 runtime_tools-1.12.1 stdlib-3.4.1 tools-2.10.1 # asn1-5.0 compiler-7.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 eldap-1.2.2 erl_docgen-0.7 erl_interface-3.10 et-1.6 eunit-2.3.3 hipe-3.16 ic-4.4.2 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 odbc-2.12 orber-3.8.3 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 syntax_tools-2.1.2 wx-1.8.1 xmerl-1.3.15 :
OTP-20.0 : asn1-5.0 common_test-1.15 compiler-7.1 cosProperty-1.2.2 crypto-4.0 debugger-4.2.2 dialyzer-3.2 diameter-2.0 edoc-0.9 erl_docgen-0.7 erl_interface-3.10 erts-9.0 eunit-2.3.3 hipe-3.16 inets-6.4 jinterface-1.8 kernel-5.3 megaco-3.18.2 mnesia-4.15 observer-2.4 orber-3.8.3 parsetools-2.1.5 public_key-1.4.1 reltool-0.7.4 runtime_tools-1.12 sasl-3.0.4 snmp-5.2.6 ssh-4.5 ssl-8.2 stdlib-3.4 syntax_tools-2.1.2 tools-2.10 wx-1.8.1 xmerl-1.3.15 # cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosTime-1.2.2 cosTransactions-1.3.2 eldap-1.2.2 et-1.6 ic-4.4.2 odbc-2.12 os_mon-2.4.2 otp_mibs-1.1.1 :
+OTP-19.3.6.6 : ssh-4.4.2.2 ssl-8.1.3.1.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.4 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.5 : erts-8.3.5.4 mnesia-4.14.3.1 ssh-4.4.2.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssl-8.1.3.1 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.4 : ssl-8.1.3.1 # asn1-4.0.4 common_test-1.14 compiler-7.0.4.1 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.5.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.6.3 : compiler-7.0.4.1 erts-8.3.5.3 # asn1-4.0.4 common_test-1.14 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
@@ -49,6 +50,7 @@ OTP-19.0.3 : inets-6.3.2 kernel-5.0.1 ssl-8.0.1 # asn1-4.0.3 common_test-1.12.2
OTP-19.0.2 : compiler-7.0.1 erts-8.0.2 stdlib-3.0.1 # asn1-4.0.3 common_test-1.12.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0.1 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2.1 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3.1 ssl-8.0 syntax_tools-2.0 tools-2.8.5 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0.1 : dialyzer-3.0.1 erts-8.0.1 inets-6.3.1 observer-2.2.1 ssh-4.3.1 tools-2.8.5 # asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 typer-0.9.11 wx-1.7 xmerl-1.3.11 :
OTP-19.0 : asn1-4.0.3 common_test-1.12.2 compiler-7.0 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7 debugger-4.2 dialyzer-3.0 diameter-1.12 edoc-0.7.19 eldap-1.2.2 erl_docgen-0.5 erl_interface-3.9 erts-8.0 et-1.6 eunit-2.3 gs-1.6.1 hipe-3.15.1 ic-4.4.1 inets-6.3 jinterface-1.7 kernel-5.0 megaco-3.18.1 mnesia-4.14 observer-2.2 odbc-2.11.2 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.2 percept-0.9 public_key-1.2 reltool-0.7.1 runtime_tools-1.10 sasl-3.0 snmp-5.2.3 ssh-4.3 ssl-8.0 stdlib-3.0 syntax_tools-2.0 tools-2.8.4 typer-0.9.11 wx-1.7 xmerl-1.3.11 # :
+OTP-18.3.4.8 : ssh-4.2.2.5 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.7 : ssl-7.3.3.2 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1.4 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2.4 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.6 : compiler-6.0.3.1 eldap-1.2.1.1 erts-7.3.1.4 ssh-4.2.2.4 # asn1-4.0.2 common_test-1.12.1.1 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 inets-6.2.4.1 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
OTP-18.3.4.5 : crypto-3.6.3.1 erts-7.3.1.3 inets-6.2.4.1 ssh-4.2.2.3 # asn1-4.0.2 common_test-1.12.1.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssl-7.3.3.1 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index bb4440a245..7f719849cc 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -151,7 +151,7 @@
<row>
<cell>Processes</cell>
<cell>The maximum number of simultaneously alive Erlang processes
- is by default 32,768. This limit can be configured at startup.
+ is by default 262,144. This limit can be configured at startup.
For more information, see the
<seealso marker="erts:erl#max_processes"><c>+P</c></seealso>
command-line flag in the