diff options
507 files changed, 30103 insertions, 14478 deletions
diff --git a/Makefile.in b/Makefile.in index ca92bf604d..b17713f182 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2010. All Rights Reserved. +# Copyright Ericsson AB 1998-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -396,20 +396,25 @@ endif release_docs docs: mod2app ifeq ($(OTP_SMALL_BUILD),true) cd $(ERL_TOP)/lib && \ - ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ + $(MAKE) TESTROOT=$(RELEASE_ROOT) $@ else cd $(ERL_TOP)/lib && \ - ERL_TOP=$(ERL_TOP) $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ cd $(ERL_TOP)/lib/dialyzer && \ - ERL_TOP=$(ERL_TOP) $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ endif cd $(ERL_TOP)/erts && \ - ERL_TOP=$(ERL_TOP) $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ + PATH=$(ERL_TOP)/bin:$${PATH} ERL_TOP=$(ERL_TOP) \ + $(MAKE) BUILD_ALL=1 TESTROOT=$(RELEASE_ROOT) $@ cd $(ERL_TOP)/system/doc && \ + PATH=$(ERL_TOP)/bin:$${PATH} \ ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@ -mod2app: - $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml +mod2app: + PATH=$(ERL_TOP)/bin:$${PATH} escript $(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml # ---------------------------------------------------------------------- ERLANG_EARS=$(BOOTSTRAP_ROOT)/bootstrap/erts diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index a1211bbf0c..f7e5c31910 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1153,7 +1153,7 @@ case "$THR_LIB_NAME" in AO_nop_full(); AO_store(&x, (AO_t) 0); z = AO_load(&x); - z = AO_compare_and_swap(&x, (AO_t) 0, (AO_t) 1); + z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1); ], [ethr_have_native_atomics=yes ethr_have_libatomic_ops=yes]) diff --git a/erts/configure.in b/erts/configure.in index 31d1d55b8a..e4c6a7852f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -769,6 +769,7 @@ if test "$enable_halfword_emulator" = "yes"; then if test "$ARCH" = "amd64"; then AC_DEFINE(HALFWORD_HEAP_EMULATOR, [1], [Define if building a halfword-heap 64bit emulator]) + ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword" AC_MSG_RESULT([yes]) else AC_MSG_ERROR(no; halfword emulator not supported on this architecture) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 19f501391f..f98e15cb52 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2356,6 +2356,14 @@ os_prompt%</pre> <seealso marker="tools:instrument">instrument(3)</seealso> and/or <seealso marker="erts:erl">erl(1)</seealso>.</p> </item> + <tag><c>low</c></tag> + <item> + <p>Only on 64-bit halfword emulator.</p> + <p>The total amount of memory allocated in low memory areas + that are restricted to less than 4 Gb even though + the system may have more physical memory.</p> + <p>May be removed in future releases of halfword emulator.</p> + </item> </taglist> <note> <p>The <c>system</c> value is not complete. Some allocated diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index f5607945a8..7383ea381d 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 327620772f..de76cb9680 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -76,6 +76,7 @@ atom allocator_sizes atom alloc_util_allocators atom allow_passive_connect atom already_loaded +atom amd64 atom anchored atom and atom andalso diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 1ca405961f..d76a7d8e9f 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2010. All Rights Reserved. + * Copyright Ericsson AB 1999-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -142,7 +142,7 @@ BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) if ((modp = erts_get_module(BIF_ARG_1)) == NULL) { return am_undefined; } - return (is_native(modp->code) || + return ((modp->code && is_native(modp->code)) || (modp->old_code != 0 && is_native(modp->old_code))) ? am_true : am_false; } @@ -175,8 +175,12 @@ check_process_code_2(BIF_ALIST_2) Eterm res; if (internal_pid_index(BIF_ARG_1) >= erts_max_processes) goto error; - rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_1, ERTS_PROC_LOCK_MAIN); +#ifdef ERTS_SMP + rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, + BIF_ARG_1, ERTS_PROC_LOCK_MAIN); +#else + rp = erts_pid2proc(BIF_P, 0, BIF_ARG_1, 0); +#endif if (!rp) { BIF_RET(am_false); } @@ -187,8 +191,10 @@ check_process_code_2(BIF_ALIST_2) modp = erts_get_module(BIF_ARG_2); res = check_process_code(rp, modp); #ifdef ERTS_SMP - if (BIF_P != rp) + if (BIF_P != rp) { + erts_resume(rp, ERTS_PROC_LOCK_MAIN); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + } #endif BIF_RET(res); } diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 32ea8588d2..fb90a7d4f7 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1911,13 +1911,15 @@ void process_main(void) * Note that for the halfword emulator, the two first elements * of the array are used. */ - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg; + *pi = I+3; set_timer(c_p, unsigned_val(timeout_value)); } else if (timeout_value == am_infinity) { c_p->flags |= F_TIMO; #if !defined(ARCH_64) || HALFWORD_HEAP } else if (term_to_Uint(timeout_value, &time_val)) { - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg; + *pi = I+3; set_timer(c_p, time_val); #endif } else { /* Wrong time */ @@ -1974,7 +1976,8 @@ void process_main(void) * we must test the F_INSLPQUEUE flag as well as the F_TIMO flag. */ if ((c_p->flags & (F_INSLPQUEUE | F_TIMO)) == 0) { - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** p = (BeamInstr **) c_p->def_arg_reg; + *p = I+3; set_timer(c_p, Arg(1)); } goto wait2; @@ -5322,7 +5325,7 @@ void process_main(void) ep->code[3] = (BeamInstr) OpCode(apply_bif); ep->code[4] = (BeamInstr) bif_table[i].f; /* XXX: set func info for bifs */ - ((BeamInstr*)ep->code + 3)[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI); + ep->fake_op_func_info_for_hipe[0] = (BeamInstr) BeamOp(op_i_func_info_IaaI); } return; diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b3325d635b..68b3350d7f 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2168,20 +2168,146 @@ BIF_RETTYPE tl_1(BIF_ALIST_1) /**********************************************************************/ /* return the size of an I/O list */ -BIF_RETTYPE iolist_size_1(BIF_ALIST_1) +static Eterm +accumulate(Eterm acc, Uint size) { - Sint size = io_list_len(BIF_ARG_1); + if (is_non_value(acc)) { + /* + * There is no pre-existing accumulator. Allocate a + * bignum buffer with one extra word to be used if + * the bignum grows in the future. + */ + Eterm* hp = (Eterm *) erts_alloc(ERTS_ALC_T_TEMP_TERM, + (BIG_UINT_HEAP_SIZE+1) * + sizeof(Eterm)); + return uint_to_big(size, hp); + } else { + Eterm* big; + int need_heap; - if (size == -1) { - BIF_ERROR(BIF_P, BADARG); - } else if (IS_USMALL(0, (Uint) size)) { - BIF_RET(make_small(size)); + /* + * Add 'size' to 'acc' in place. There is always one + * extra word allocated in case the bignum grows by one word. + */ + big = big_val(acc); + need_heap = BIG_NEED_SIZE(BIG_SIZE(big)); + acc = big_plus_small(acc, size, big); + if (BIG_NEED_SIZE(big_size(acc)) > need_heap) { + /* + * The extra word has been consumed. Grow the + * allocation by one word. + */ + big = (Eterm *) erts_realloc(ERTS_ALC_T_TEMP_TERM, + big_val(acc), + (need_heap+1) * sizeof(Eterm)); + acc = make_big(big); + } + return acc; + } +} + +static Eterm +consolidate(Process* p, Eterm acc, Uint size) +{ + Eterm* hp; + + if (is_non_value(acc)) { + return erts_make_integer(size, p); } else { - Eterm* hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); - BIF_RET(uint_to_big(size, hp)); + Eterm* big; + Uint sz; + Eterm res; + + acc = accumulate(acc, size); + big = big_val(acc); + sz = BIG_NEED_SIZE(BIG_SIZE(big)); + hp = HAlloc(p, sz); + res = make_big(hp); + while (sz--) { + *hp++ = *big++; + } + erts_free(ERTS_ALC_T_TEMP_TERM, (void *) big_val(acc)); + return res; } } +BIF_RETTYPE iolist_size_1(BIF_ALIST_1) +{ + Eterm obj, hd; + Eterm* objp; + Uint size = 0; + Uint cur_size; + Uint new_size; + Eterm acc = THE_NON_VALUE; + DECLARE_ESTACK(s); + + obj = BIF_ARG_1; + goto L_again; + + while (!ESTACK_ISEMPTY(s)) { + obj = ESTACK_POP(s); + L_again: + if (is_list(obj)) { + L_iter_list: + objp = list_val(obj); + hd = CAR(objp); + obj = CDR(objp); + /* Head */ + if (is_byte(hd)) { + size++; + if (size == 0) { + acc = accumulate(acc, (Uint) -1); + size = 1; + } + } else if (is_binary(hd) && binary_bitsize(hd) == 0) { + cur_size = binary_size(hd); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_list(hd)) { + ESTACK_PUSH(s, obj); + obj = hd; + goto L_iter_list; + } else if (is_not_nil(hd)) { + goto L_type_error; + } + /* Tail */ + if (is_list(obj)) { + goto L_iter_list; + } else if (is_binary(obj) && binary_bitsize(obj) == 0) { + cur_size = binary_size(obj); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_not_nil(obj)) { + goto L_type_error; + } + } else if (is_binary(obj) && binary_bitsize(obj) == 0) { + cur_size = binary_size(obj); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_not_nil(obj)) { + goto L_type_error; + } + } + + DESTROY_ESTACK(s); + BIF_RET(consolidate(BIF_P, acc, size)); + + L_type_error: + DESTROY_ESTACK(s); + BIF_ERROR(BIF_P, BADARG); +} /**********************************************************************/ @@ -3215,20 +3341,32 @@ BIF_RETTYPE garbage_collect_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } - rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, + if (BIF_P->id == BIF_ARG_1) + rp = BIF_P; + else { +#ifdef ERTS_SMP + rp = erts_pid2proc_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_ARG_1, ERTS_PROC_LOCK_MAIN); - if (!rp) - BIF_RET(am_false); - if (rp == ERTS_PROC_LOCK_BUSY) - ERTS_BIF_YIELD1(bif_export[BIF_garbage_collect_1], BIF_P, BIF_ARG_1); + if (rp == ERTS_PROC_LOCK_BUSY) + ERTS_BIF_YIELD1(bif_export[BIF_garbage_collect_1], BIF_P, BIF_ARG_1); +#else + rp = erts_pid2proc(BIF_P, 0, BIF_ARG_1, 0); +#endif + if (!rp) + BIF_RET(am_false); + } /* The GC cost is taken for the process executing this BIF. */ FLAGS(rp) |= F_NEED_FULLSWEEP; reds = erts_garbage_collect(rp, 0, rp->arg_reg, rp->arity); - if (BIF_P != rp) +#ifdef ERTS_SMP + if (BIF_P != rp) { + erts_resume(rp, ERTS_PROC_LOCK_MAIN); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + } +#endif BIF_RET2(am_true, reds); } @@ -3270,6 +3408,7 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) Eterm* dead_ports; int alive, dead; Uint32 next_ss; + int i; /* To get a consistent snapshot... * We add alive ports from start of the buffer @@ -3281,21 +3420,18 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) erts_smp_atomic_set(&erts_dead_ports_ptr, (erts_aint_t) (port_buf + erts_max_ports)); - next_ss = erts_smp_atomic_inctest(&erts_ports_snapshot); + next_ss = erts_smp_atomic32_inctest(&erts_ports_snapshot); - if (erts_smp_atomic_read(&erts_ports_alive) > 0) { - erts_aint_t i; - for (i = erts_max_ports-1; i >= 0; i--) { - Port* prt = &erts_port[i]; - erts_smp_port_state_lock(prt); - if (!(prt->status & ERTS_PORT_SFLGS_DEAD) - && prt->snapshot != next_ss) { - ASSERT(prt->snapshot == next_ss - 1); - *pp++ = prt->id; - prt->snapshot = next_ss; /* Consumed by this snapshot */ - } - erts_smp_port_state_unlock(prt); + for (i = erts_max_ports-1; i >= 0; i--) { + Port* prt = &erts_port[i]; + erts_smp_port_state_lock(prt); + if (!(prt->status & ERTS_PORT_SFLGS_DEAD) + && prt->snapshot != next_ss) { + ASSERT(prt->snapshot == next_ss - 1); + *pp++ = prt->id; + prt->snapshot = next_ss; /* Consumed by this snapshot */ } + erts_smp_port_state_unlock(prt); } dead_ports = (Eterm*)erts_smp_atomic_xchg(&erts_dead_ports_ptr, diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index f47f5a9c0c..d18de9ae5d 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1588,7 +1588,7 @@ big_to_double(Wterm x, double* resp) /* ** Estimate the number of decimal digits (include sign) */ -int big_decimal_estimate(Eterm x) +int big_decimal_estimate(Wterm x) { Eterm* xp = big_val(x); int lg = I_lg(BIG_V(xp), BIG_SIZE(xp)); @@ -1602,7 +1602,7 @@ int big_decimal_estimate(Eterm x) ** Convert a bignum into a string of decimal numbers */ -static void write_big(Eterm x, void (*write_func)(void *, char), void *arg) +static void write_big(Wterm x, void (*write_func)(void *, char), void *arg) { Eterm* xp = big_val(x); ErtsDigit* dx = BIG_V(xp); @@ -1681,7 +1681,7 @@ write_string(void *arg, char c) *(--(*((char **) arg))) = c; } -char *erts_big_to_string(Eterm x, char *buf, Uint buf_sz) +char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz - 1; *big_str = '\0'; diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index f28a390aea..2afc37004f 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -114,9 +114,9 @@ typedef Uint dsize_t; /* Vector size type */ #endif -int big_decimal_estimate(Eterm); +int big_decimal_estimate(Wterm); Eterm erts_big_to_list(Eterm, Eterm**); -char *erts_big_to_string(Eterm x, char *buf, Uint buf_sz); +char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz); Eterm small_times(Sint, Sint, Eterm*); diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 9486602633..1fb39c6c67 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -32,11 +32,11 @@ #include "erl_bits.h" #ifdef DEBUG -static int list_to_bitstr_buf(Eterm obj, char* buf, int len); +static int list_to_bitstr_buf(Eterm obj, char* buf, Uint len); #else static int list_to_bitstr_buf(Eterm obj, char* buf); #endif -static Sint bitstr_list_len(Eterm obj); +static int bitstr_list_len(Eterm obj, Uint* num_bytes); void erts_init_binary(void) @@ -355,21 +355,24 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg) { Eterm bin; - int i; + Uint size; int offset; byte* bytes; + if (is_nil(arg)) { BIF_RET(new_binary(p,(byte*)"",0)); } if (is_not_list(arg)) { goto error; } - if ((i = io_list_len(arg)) < 0) { - goto error; + switch (erts_iolist_size(arg, &size)) { + case ERTS_IOLIST_OVERFLOW: BIF_ERROR(p, SYSTEM_LIMIT); + case ERTS_IOLIST_TYPE: goto error; + default: ; } - bin = new_binary(p, (byte *)NULL, i); + bin = new_binary(p, (byte *)NULL, size); bytes = binary_bytes(bin); - offset = io_list_to_buf(arg, (char*) bytes, i); + offset = io_list_to_buf(arg, (char*) bytes, size); ASSERT(offset == 0); BIF_RET(bin); @@ -396,7 +399,8 @@ BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1) BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) { Eterm bin; - int i,offset; + Uint sz; + int offset; byte* bytes; ErlSubBin* sb1; Eterm* hp; @@ -405,15 +409,19 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) BIF_RET(new_binary(BIF_P,(byte*)"",0)); } if (is_not_list(BIF_ARG_1)) { - goto error; + error: + BIF_ERROR(BIF_P, BADARG); } - if ((i = bitstr_list_len(BIF_ARG_1)) < 0) { + switch (bitstr_list_len(BIF_ARG_1, &sz)) { + case ERTS_IOLIST_TYPE: goto error; + case ERTS_IOLIST_OVERFLOW: + BIF_ERROR(BIF_P, SYSTEM_LIMIT); } - bin = new_binary(BIF_P, (byte *)NULL, i); + bin = new_binary(BIF_P, (byte *)NULL, sz); bytes = binary_bytes(bin); #ifdef DEBUG - offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, i); + offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, sz); #else offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes); #endif @@ -422,20 +430,16 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) hp = HAlloc(BIF_P, ERL_SUB_BIN_SIZE); sb1 = (ErlSubBin *) hp; sb1->thing_word = HEADER_SUB_BIN; - sb1->size = i-1; + sb1->size = sz-1; sb1->offs = 0; sb1->orig = bin; sb1->bitoffs = 0; sb1->bitsize = offset; sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; bin = make_binary(sb1); } BIF_RET(bin); - - error: - BIF_ERROR(BIF_P, BADARG); } BIF_RETTYPE split_binary_2(BIF_ALIST_2) @@ -499,7 +503,7 @@ BIF_RETTYPE split_binary_2(BIF_ALIST_2) */ static int #ifdef DEBUG -list_to_bitstr_buf(Eterm obj, char* buf, int len) +list_to_bitstr_buf(Eterm obj, char* buf, Uint len) #else list_to_bitstr_buf(Eterm obj, char* buf) #endif @@ -602,8 +606,8 @@ list_to_bitstr_buf(Eterm obj, char* buf) return offset; } -static Sint -bitstr_list_len(Eterm obj) +static int +bitstr_list_len(Eterm obj, Uint* num_bytes) { Eterm* objp; Uint len = 0; @@ -611,6 +615,26 @@ bitstr_list_len(Eterm obj) DECLARE_ESTACK(s); goto L_again; +#define SAFE_ADD(Var, Val) \ + do { \ + Uint valvar = (Val); \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } while (0) + +#define SAFE_ADD_BITSIZE(Var, Bin) \ + do { \ + if (*binary_val(Bin) == HEADER_SUB_BIN) { \ + Uint valvar = ((ErlSubBin *) binary_val(Bin))->bitsize; \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } \ + } while (0) + while (!ESTACK_ISEMPTY(s)) { obj = ESTACK_POP(s); L_again: @@ -621,9 +645,12 @@ bitstr_list_len(Eterm obj) obj = CAR(objp); if (is_byte(obj)) { len++; + if (len == 0) { + goto L_overflow_error; + } } else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_list(obj)) { ESTACK_PUSH(s, CDR(objp)); goto L_iter_list; /* on head */ @@ -635,24 +662,44 @@ bitstr_list_len(Eterm obj) if (is_list(obj)) goto L_iter_list; /* on tail */ else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_not_nil(obj)) { goto L_type_error; } } else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_not_nil(obj)) { goto L_type_error; } } +#undef SAFE_ADD +#undef SAFE_ADD_BITSIZE DESTROY_ESTACK(s); - return (Sint) (len + (offs/8) + ((offs % 8) != 0)); + + /* + * Make sure that the number of bits in the bitstring will fit + * in an Uint to ensure that the binary can be matched using + * the binary syntax. + */ + if (len << 3 < len) { + goto L_overflow_error; + } + len += (offs >> 3) + ((offs & 7) != 0); + if (len << 3 < len) { + goto L_overflow_error; + } + *num_bytes = len; + return ERTS_IOLIST_OK; L_type_error: DESTROY_ESTACK(s); - return (Sint) -1; + return ERTS_IOLIST_TYPE; + + L_overflow_error: + DESTROY_ESTACK(s); + return ERTS_IOLIST_OVERFLOW; } diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 673eac7fea..cda404af5e 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -90,6 +90,10 @@ typedef union { static ErtsAllocatorState_t sl_alloc_state; static ErtsAllocatorState_t std_alloc_state; static ErtsAllocatorState_t ll_alloc_state; +#if HALFWORD_HEAP +static ErtsAllocatorState_t std_alloc_low_state; +static ErtsAllocatorState_t ll_alloc_low_state; +#endif static ErtsAllocatorState_t temp_alloc_state; static ErtsAllocatorState_t eheap_alloc_state; static ErtsAllocatorState_t binary_alloc_state; @@ -166,6 +170,10 @@ typedef struct { struct au_init binary_alloc; struct au_init ets_alloc; struct au_init driver_alloc; +#if HALFWORD_HEAP + struct au_init std_alloc_low; + struct au_init ll_alloc_low; +#endif } erts_alc_hndl_args_init_t; #define ERTS_AU_INIT__ {0, 0, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} @@ -193,6 +201,10 @@ set_default_sl_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED; ip->init.util.rsbcst = 80; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif + } static void @@ -256,6 +268,9 @@ set_default_temp_alloc_opts(struct au_init *ip) ip->init.util.ts = ERTS_ALC_MTA_TEMPORARY; ip->init.util.rsbcst = 90; ip->init.util.rmbcmt = 100; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif } static void @@ -275,6 +290,9 @@ set_default_eheap_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_EHEAP; ip->init.util.rsbcst = 50; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif } static void @@ -531,6 +549,20 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_allctrs[ERTS_ALC_A_SYSTEM].free = erts_sys_free; erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1; +#if HALFWORD_HEAP + /* Init low memory variants by cloning */ + init.std_alloc_low = init.std_alloc; + init.std_alloc_low.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW; + init.std_alloc_low.init.util.low_mem = 1; + + init.ll_alloc_low = init.ll_alloc; + init.ll_alloc_low.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW; + init.ll_alloc_low.init.util.low_mem = 1; + + set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_alloc_low); + set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_alloc_low); +#endif /* HALFWORD */ + set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc); set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc); set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc); @@ -576,7 +608,14 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) start_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc, &ll_alloc_state); - +#if HALFWORD_HEAP + start_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, + &init.ll_alloc_low, + &ll_alloc_low_state); + start_au_allocator(ERTS_ALC_A_STANDARD_LOW, + &init.std_alloc_low, + &std_alloc_low_state); +#endif start_au_allocator(ERTS_ALC_A_EHEAP, &init.eheap_alloc, &eheap_alloc_state); @@ -612,11 +651,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_set_fix_size(ERTS_ALC_T_PROC, sizeof(Process)); erts_set_fix_size(ERTS_ALC_T_DB_TABLE, sizeof(DbTable)); erts_set_fix_size(ERTS_ALC_T_ATOM, sizeof(Atom)); - erts_set_fix_size(ERTS_ALC_T_EXPORT, sizeof(Export)); + erts_set_fix_size(ERTS_ALC_T_MODULE, sizeof(Module)); erts_set_fix_size(ERTS_ALC_T_REG_PROC, sizeof(RegProc)); - erts_set_fix_size(ERTS_ALC_T_MONITOR_SH, ERTS_MONITOR_SH_SIZE*sizeof(Uint)); - erts_set_fix_size(ERTS_ALC_T_NLINK_SH, ERTS_LINK_SH_SIZE*sizeof(Uint)); erts_set_fix_size(ERTS_ALC_T_FUN_ENTRY, sizeof(ErlFunEntry)); #ifdef ERTS_ALC_T_DRV_EV_D_STATE erts_set_fix_size(ERTS_ALC_T_DRV_EV_D_STATE, @@ -626,6 +663,11 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_set_fix_size(ERTS_ALC_T_DRV_SEL_D_STATE, sizeof(ErtsDrvSelectDataState)); #endif +#if !HALFWORD_HEAP + erts_set_fix_size(ERTS_ALC_T_EXPORT, sizeof(Export)); + erts_set_fix_size(ERTS_ALC_T_MONITOR_SH, ERTS_MONITOR_SH_SIZE*sizeof(Uint)); + erts_set_fix_size(ERTS_ALC_T_NLINK_SH, ERTS_LINK_SH_SIZE*sizeof(Uint)); +#endif #endif #endif @@ -638,6 +680,15 @@ set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init) ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n]; ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n]; +#if HALFWORD_HEAP + /* If halfword heap, silently ignore any disabling of internal + * allocators for low memory + */ + if (init->init.util.low_mem) { + init->enable = 1; + } +#endif + if (!init->enable) { af->alloc = erts_sys_alloc; af->realloc = erts_sys_realloc; @@ -1348,14 +1399,6 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) argv[j++] = argv[i]; } *argc = j; -#if HALFWORD_HEAP - /* If halfword heap, silently ignore any disabling of internal - allocators */ - for (i = 0; i < aui_sz; ++i) - aui[i]->enable = 1; -#endif - - } static char *type_no_str(ErtsAlcType_t n) @@ -1528,10 +1571,10 @@ erts_realloc_n_enomem(ErtsAlcType_t n, void *ptr, Uint size) erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_REALLOC, n, size); } -static ERTS_INLINE Uint +static ERTS_INLINE UWord alcu_size(ErtsAlcType_t ai) { - Uint res = 0; + UWord res = 0; ASSERT(erts_allctrs_info[ai].enabled); ASSERT(erts_allctrs_info[ai].alloc_util); @@ -1563,6 +1606,49 @@ alcu_size(ErtsAlcType_t ai) return res; } +#if HALFWORD_HEAP +static ERTS_INLINE int +alcu_is_low(ErtsAlcType_t ai) +{ + int is_low = 0; + ASSERT(erts_allctrs_info[ai].enabled); + ASSERT(erts_allctrs_info[ai].alloc_util); + + if (!erts_allctrs_info[ai].thr_spec) { + Allctr_t *allctr = erts_allctrs_info[ai].extra; + is_low = allctr->mseg_opt.low_mem; + } + else { + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ai]; + int i; +# ifdef DEBUG + int found_one = 0; +# endif + + ASSERT(tspec->all_thr_safe); + ASSERT(tspec->enabled); + + for (i = tspec->size - 1; i >= 0; i--) { + Allctr_t *allctr = tspec->allctr[i]; + if (allctr) { +# ifdef DEBUG + if (!found_one) { + is_low = allctr->mseg_opt.low_mem; + found_one = 1; + } + else ASSERT(is_low == allctr->mseg_opt.low_mem); +# else + is_low = allctr->mseg_opt.low_mem; + break; +# endif + } + } + ASSERT(found_one); + } + return is_low; +} +#endif /* HALFWORD */ + Eterm erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) { @@ -1579,22 +1665,28 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) int code; int ets; int maximum; +#if HALFWORD_HEAP + int low; +#endif } want = {0}; struct { - Uint total; - Uint processes; - Uint processes_used; - Uint system; - Uint atom; - Uint atom_used; - Uint binary; - Uint code; - Uint ets; - Uint maximum; + UWord total; + UWord processes; + UWord processes_used; + UWord system; + UWord atom; + UWord atom_used; + UWord binary; + UWord code; + UWord ets; + UWord maximum; +#if HALFWORD_HEAP + UWord low; +#endif } size = {0}; - Eterm atoms[sizeof(size)/sizeof(Uint)]; - Uint *uintps[sizeof(size)/sizeof(Uint)]; - Eterm euints[sizeof(size)/sizeof(Uint)]; + Eterm atoms[sizeof(size)/sizeof(UWord)]; + UWord *uintps[sizeof(size)/sizeof(UWord)]; + Eterm euints[sizeof(size)/sizeof(UWord)]; int want_tot_or_sys; int length; Eterm res = THE_NON_VALUE; @@ -1646,7 +1738,11 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) atoms[length] = am_maximum; uintps[length++] = &size.maximum; } - +#if HALFWORD_HEAP + want.low = 1; + atoms[length] = am_low; + uintps[length++] = &size.low; +#endif } else { DeclareTmpHeapNoproc(tmp_heap,2); @@ -1740,6 +1836,15 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) return am_badarg; } break; +#if HALFWORD_HEAP + case am_low: + if (!want.low) { + want.low = 1; + atoms[length] = am_low; + uintps[length++] = &size.low; + } + break; +#endif default: UnUseTmpHeapNoproc(2); return am_badarg; @@ -1769,7 +1874,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) ASSERT(length <= sizeof(atoms)/sizeof(Eterm)); ASSERT(length <= sizeof(euints)/sizeof(Eterm)); - ASSERT(length <= sizeof(uintps)/sizeof(Uint)); + ASSERT(length <= sizeof(uintps)/sizeof(UWord)); if (proc) { @@ -1788,8 +1893,8 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) { if (erts_allctrs_info[ai].alloc_util) { - Uint *save; - Uint asz; + UWord *save; + UWord asz; switch (ai) { case ERTS_ALC_A_TEMPORARY: /* @@ -1814,6 +1919,11 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (save) *save = asz; size.total += asz; +#if HALFWORD_HEAP + if (alcu_is_low(ai)) { + size.low += asz; + } +#endif } } } @@ -1821,7 +1931,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (want_tot_or_sys || want.processes || want.processes_used) { - Uint tmp; + UWord tmp; if (ERTS_MEM_NEED_ALL_ALCU) tmp = size.processes; @@ -1836,6 +1946,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) size.processes = size.processes_used = tmp; +#if HALFWORD_HEAP + /* BUG: We ignore link and monitor memory */ +#else erts_fix_info(ERTS_ALC_T_NLINK_SH, &efi); size.processes += efi.total; size.processes_used += efi.used; @@ -1843,6 +1956,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) erts_fix_info(ERTS_ALC_T_MONITOR_SH, &efi); size.processes += efi.total; size.processes_used += efi.used; +#endif erts_fix_info(ERTS_ALC_T_PROC, &efi); size.processes += efi.total; @@ -1879,8 +1993,12 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) erts_fix_info(ERTS_ALC_T_MODULE, &efi); size.code += efi.used; size.code += export_table_sz(); +#if HALFWORD_HEAP + size.code += export_list_size() * sizeof(Export); +#else erts_fix_info(ERTS_ALC_T_EXPORT, &efi); size.code += efi.used; +#endif size.code += erts_fun_table_sz(); erts_fix_info(ERTS_ALC_T_FUN_ENTRY, &efi); size.code += efi.used; @@ -1913,7 +2031,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) /* Print result... */ erts_print(to, arg, "=memory\n"); for (i = 0; i < length; i++) - erts_print(to, arg, "%T: %beu\n", atoms[i], *uintps[i]); + erts_print(to, arg, "%T: %bpu\n", atoms[i], *uintps[i]); } if (proc) { @@ -1926,9 +2044,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (only_one_value) { ASSERT(length == 1); hsz = 0; - erts_bld_uint(NULL, &hsz, *uintps[0]); + erts_bld_uword(NULL, &hsz, *uintps[0]); hp = hsz ? HAlloc((Process *) proc, hsz) : NULL; - res = erts_bld_uint(&hp, NULL, *uintps[0]); + res = erts_bld_uword(&hp, NULL, *uintps[0]); } else { Uint **hpp = NULL; @@ -1938,7 +2056,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) while (1) { int i; for (i = 0; i < length; i++) - euints[i] = erts_bld_uint(hpp, hszp, *uintps[i]); + euints[i] = erts_bld_uword(hpp, hszp, *uintps[i]); res = erts_bld_2tup_list(hpp, hszp, length, atoms, euints); if (hpp) break; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index ca71798917..c6cc0e1fac 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -75,6 +75,11 @@ allocator EHEAP true eheap_alloc allocator ETS true ets_alloc allocator FIXED_SIZE true fix_alloc ++if halfword +allocator LONG_LIVED_LOW true ll_alloc_low +allocator STANDARD_LOW true std_alloc_low ++endif + +else # Non smp build allocator TEMPORARY false temp_alloc @@ -85,12 +90,18 @@ allocator EHEAP false eheap_alloc allocator ETS false ets_alloc allocator FIXED_SIZE false fix_alloc ++if halfword +allocator LONG_LIVED_LOW false ll_alloc_low +allocator STANDARD_LOW false std_alloc_low ++endif + +endif allocator BINARY true binary_alloc allocator DRIVER true driver_alloc + # --- Class declarations ----------------------------------------------------- # # Syntax: class <CLASS> <DESCRIPTION> @@ -125,14 +136,9 @@ class SYSTEM system_data type PROC FIXED_SIZE PROCESSES proc type ATOM FIXED_SIZE ATOM atom_entry -type EXPORT FIXED_SIZE CODE export_entry type MODULE FIXED_SIZE CODE module_entry type REG_PROC FIXED_SIZE PROCESSES reg_proc type LINK_LH STANDARD PROCESSES link_lh -type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh -type MONITOR_LH STANDARD PROCESSES monitor_lh -type NLINK_SH FIXED_SIZE PROCESSES nlink_sh -type NLINK_LH STANDARD PROCESSES nlink_lh type SUSPEND_MON STANDARD PROCESSES suspend_monitor type PEND_SUSPEND SHORT_LIVED PROCESSES pending_suspend type PROC_LIST SHORT_LIVED PROCESSES proc_list @@ -175,7 +181,6 @@ type DRIVER STANDARD SYSTEM driver type NIF DRIVER SYSTEM nif_internal type BINARY BINARY BINARIES binary type NBIF_TABLE SYSTEM SYSTEM nbif_tab -type CODE LONG_LIVED CODE code type ARG_REG STANDARD PROCESSES arg_reg type PROC_DICT STANDARD PROCESSES proc_dict type CALLS_BUF STANDARD PROCESSES calls_buf @@ -193,10 +198,8 @@ type DB_FIXATION SHORT_LIVED ETS db_fixation type DB_FIX_DEL SHORT_LIVED ETS fixed_del type DB_TABLES LONG_LIVED ETS db_tabs type DB_NTAB_ENT STANDARD ETS db_named_table_entry -type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_TMP TEMPORARY ETS db_tmp type DB_MC_STK TEMPORARY ETS db_mc_stack -type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type DB_MS_RUN_HEAP SHORT_LIVED ETS db_match_spec_run_heap type DB_MS_CMPL_HEAP TEMPORARY ETS db_match_spec_cmpl_heap type DB_SEG ETS ETS db_segment @@ -213,10 +216,8 @@ type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf type INFO_DSBUF SYSTEM SYSTEM info_dsbuf # INFO_DSBUF have to use the SYSTEM allocator; otherwise, a deadlock might occur -type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data type SCHDLR_SLP_INFO LONG_LIVED SYSTEM scheduler_sleep_info type RUNQS LONG_LIVED SYSTEM run_queues -type DDLL_PROCESS STANDARD SYSTEM ddll_processes type DDLL_HANDLE STANDARD SYSTEM ddll_handle type DDLL_ERRCODES LONG_LIVED SYSTEM ddll_errcodes type DDLL_TMP_BUF TEMPORARY SYSTEM ddll_tmp_buf @@ -327,13 +328,45 @@ type SSB SHORT_LIVED PROCESSES ssb +endif ++if halfword + +type DDLL_PROCESS STANDARD_LOW SYSTEM ddll_processes +type MONITOR_LH STANDARD_LOW PROCESSES monitor_lh +type NLINK_LH STANDARD_LOW PROCESSES nlink_lh +type CODE LONG_LIVED_LOW CODE code +type DB_HEIR_DATA STANDARD_LOW ETS db_heir_data +type DB_MS_PSDO_PROC LONG_LIVED_LOW ETS db_match_pseudo_proc +type SCHDLR_DATA LONG_LIVED_LOW SYSTEM scheduler_data +type LL_TEMP_TERM LONG_LIVED_LOW SYSTEM ll_temp_term + +# no FIXED_SIZE for low memory +type EXPORT STANDARD_LOW CODE export_entry +type MONITOR_SH STANDARD_LOW PROCESSES monitor_sh +type NLINK_SH STANDARD_LOW PROCESSES nlink_sh + ++else # "fullword" + +type DDLL_PROCESS STANDARD SYSTEM ddll_processes +type MONITOR_LH STANDARD PROCESSES monitor_lh +type NLINK_LH STANDARD PROCESSES nlink_lh +type CODE LONG_LIVED CODE code +type DB_HEIR_DATA STANDARD ETS db_heir_data +type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc +type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data +type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term + +type EXPORT FIXED_SIZE CODE export_entry +type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh +type NLINK_SH FIXED_SIZE PROCESSES nlink_sh + ++endif + + # # Types used by system specific code # type TEMP_TERM TEMPORARY SYSTEM temp_term -type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term - type DRV_TAB LONG_LIVED SYSTEM drv_tab type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state type DRV_EV_D_STATE FIXED_SIZE SYSTEM driver_event_data_state diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 84c72439a3..cc04ef65bf 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1639,6 +1639,9 @@ static struct { Eterm e; Eterm t; Eterm ramv; +#if HALFWORD_HEAP + Eterm low; +#endif Eterm sbct; #if HAVE_ERTS_MSEG Eterm asbcst; @@ -1724,6 +1727,9 @@ init_atoms(Allctr_t *allctr) AM_INIT(e); AM_INIT(t); AM_INIT(ramv); +#if HALFWORD_HEAP + AM_INIT(low); +#endif AM_INIT(sbct); #if HAVE_ERTS_MSEG AM_INIT(asbcst); @@ -2168,6 +2174,9 @@ info_options(Allctr_t *allctr, "option e: true\n" "option t: %s\n" "option ramv: %s\n" +#if HALFWORD_HEAP + "option low: %s\n" +#endif "option sbct: %beu\n" #if HAVE_ERTS_MSEG "option asbcst: %bpu\n" @@ -2185,6 +2194,9 @@ info_options(Allctr_t *allctr, "option mbcgs: %beu\n", topt, allctr->ramv ? "true" : "false", +#if HALFWORD_HEAP + allctr->mseg_opt.low_mem ? "true" : "false", +#endif allctr->sbc_threshold, #if HAVE_ERTS_MSEG allctr->mseg_opt.abs_shrink_th, @@ -2243,6 +2255,9 @@ info_options(Allctr_t *allctr, add_2tup(hpp, szp, &res, am.sbct, bld_uint(hpp, szp, allctr->sbc_threshold)); +#if HALFWORD_HEAP + add_2tup(hpp, szp, &res, am.low, allctr->mseg_opt.low_mem ? am_true : am_false); +#endif add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false); add_2tup(hpp, szp, &res, am.t, (allctr->t ? bld_uint(hpp, szp, (Uint) allctr->t) @@ -3105,13 +3120,12 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) goto error; #if HAVE_ERTS_MSEG - { - ErtsMsegOpt_t mseg_opt = ERTS_MSEG_DEFAULT_OPT_INITIALIZER; - - sys_memcpy((void *) &allctr->mseg_opt, - (void *) &mseg_opt, - sizeof(ErtsMsegOpt_t)); - } + sys_memcpy((void *) &allctr->mseg_opt, + (void *) &erts_mseg_default_opt, + sizeof(ErtsMsegOpt_t)); +# if HALFWORD_HEAP + allctr->mseg_opt.low_mem = init->low_mem; +# endif #endif allctr->name_prefix = init->name_prefix; diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index d296081714..ddf84c086c 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -38,6 +38,7 @@ typedef struct { int tspec; int tpref; int ramv; + int low_mem; /* HALFWORD only */ UWord sbct; UWord asbcst; UWord rsbcst; @@ -70,6 +71,7 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ + 0, /* (bool) low_mem: HALFWORD only */\ 512*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ @@ -97,6 +99,7 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ + 0, /* (bool) low_mem: HALFWORD only */\ 64*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index c9cdcb87a6..9631fb50db 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2010. All Rights Reserved. + * Copyright Ericsson AB 2006-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term, Eterm name_term, Eterm options) { char *path = NULL; - int path_len; + Uint path_len; char *name = NULL; DE_Handle *dh; erts_driver_t *drv; @@ -221,9 +221,7 @@ BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term, goto error; } - path_len = io_list_len(path_term); - - if (path_len <= 0) { + if (erts_iolist_size(path_term, &path_len)) { goto error; } path = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, path_len + 1 /* might need path separator */ + sys_strlen(name) + 1); @@ -1878,7 +1876,7 @@ static Eterm mkatom(char *str) static char *pick_list_or_atom(Eterm name_term) { char *name = NULL; - int name_len; + Uint name_len; if (is_atom(name_term)) { Atom *ap = atom_tab(atom_val(name_term)); if (ap->len == 0) { @@ -1890,8 +1888,7 @@ static char *pick_list_or_atom(Eterm name_term) memcpy(name,ap->name,ap->len); name[ap->len] = '\0'; } else { - name_len = io_list_len(name_term); - if (name_len <= 0) { + if (erts_iolist_size(name_term, &name_len)) { goto error; } name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, name_len + 1); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index e50fc18e64..f264bf44df 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1732,14 +1732,14 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ # define ERTS_ERROR_CHECKER_PRINTF_XML VALGRIND_PRINTF_XML # endif #endif - int buf_size = 8*1024; /* Try with 8KB first */ + Uint buf_size = 8*1024; /* Try with 8KB first */ char *buf = erts_alloc(ERTS_ALC_T_TMP, buf_size); int r = io_list_to_buf(*tp, (char*) buf, buf_size - 1); if (r < 0) { erts_free(ERTS_ALC_T_TMP, (void *) buf); - buf_size = io_list_len(*tp); - if (buf_size < 0) + if (erts_iolist_size(*tp, &buf_size)) { goto badarg; + } buf_size++; buf = erts_alloc(ERTS_ALC_T_TMP, buf_size); r = io_list_to_buf(*tp, (char*) buf, buf_size - 1); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index fbc92b9730..3fd35dd963 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2010. All Rights Reserved. + * Copyright Ericsson AB 2001-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -996,6 +996,7 @@ static byte* convert_environment(Process* p, Eterm env) Eterm* hp; Uint heap_size; int n; + Uint size; byte* bytes; if ((n = list_length(env)) < 0) { @@ -1039,15 +1040,15 @@ static byte* convert_environment(Process* p, Eterm env) if (is_not_nil(env)) { goto done; } - if ((n = io_list_len(all)) < 0) { + if (erts_iolist_size(all, &size)) { goto done; } /* * Put the result in a binary (no risk for a memory leak that way). */ - (void) erts_new_heap_binary(p, NULL, n, &bytes); - io_list_to_buf(all, (char*)bytes, n); + (void) erts_new_heap_binary(p, NULL, size, &bytes); + io_list_to_buf(all, (char*)bytes, size); done: erts_free(ERTS_ALC_T_TMP, temp_heap); diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index d4a8a3aaa7..26891c4348 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2010. All Rights Reserved. + * Copyright Ericsson AB 2008-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -417,7 +417,7 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con BIF_RETTYPE re_compile_2(BIF_ALIST_2) { - int slen; + Uint slen; char *expr; pcre *result; int errcode = 0; @@ -444,7 +444,7 @@ re_compile_2(BIF_ALIST_2) BIF_TRAP2(ucompile_trap_exportp, BIF_P, BIF_ARG_1, BIF_ARG_2); } - if ((slen = io_list_len(BIF_ARG_1)) < 0) { + if (erts_iolist_size(BIF_ARG_1, &slen)) { BIF_ERROR(BIF_P,BADARG); } expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1); @@ -795,8 +795,8 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) memcpy(tmpb,ap->name,ap->len); tmpb[ap->len] = '\0'; } else { - int slen = io_list_len(val); - if (slen < 0) { + Uint slen; + if (erts_iolist_size(val, &slen)) { goto error; } if ((slen + 1) > tmpbsiz) { @@ -851,7 +851,7 @@ re_run_3(BIF_ALIST_3) const pcre *code_tmp; RestartContext restart; byte *temp_alloc = NULL; - int slength; + Uint slength; int startoffset = 0; int options = 0, comp_options = 0; int ovsize; @@ -875,7 +875,7 @@ re_run_3(BIF_ALIST_3) if (is_not_tuple(BIF_ARG_2) || (arityval(*tuple_val(BIF_ARG_2)) != 4)) { if (is_binary(BIF_ARG_2) || is_list(BIF_ARG_2) || is_nil(BIF_ARG_2)) { /* Compile from textual RE */ - int slen; + Uint slen; char *expr; pcre *result; int errcode = 0; @@ -889,7 +889,7 @@ re_run_3(BIF_ALIST_3) BIF_TRAP3(urun_trap_exportp, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } - if ((slen = io_list_len(BIF_ARG_2)) < 0) { + if (erts_iolist_size(BIF_ARG_2, &slen)) { BIF_ERROR(BIF_P,BADARG); } @@ -1027,7 +1027,7 @@ re_run_3(BIF_ALIST_3) restart.flags |= RESTART_FLAG_SUBJECT_IN_BINARY; } else { handle_iolist: - if ((slength = io_list_len(BIF_ARG_1)) < 0) { + if (erts_iolist_size(BIF_ARG_1, &slength)) { erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ovector); erts_free(ERTS_ALC_T_RE_SUBJECT, restart.code); if (restart.ret_info != NULL) { diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 9ef990cc4f..fdc82c8b88 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -105,7 +105,7 @@ #define NSEG_2 256 /* Size of second segment table */ #define NSEG_INC 128 /* Number of segments to grow after that */ -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read(&(tb)->segtab)) +#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_acqb(&(tb)->segtab)) #define NACTIVE(tb) ((int)erts_smp_atomic_read(&(tb)->nactive)) #define NITEMS(tb) ((int)erts_smp_atomic_read(&(tb)->common.nitems)) @@ -122,8 +122,8 @@ */ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) { - Uint mask = erts_smp_atomic_read(&tb->szm); - Uint ix = hval & mask; + Uint mask = erts_smp_atomic_read_acqb(&tb->szm); + Uint ix = hval & mask; if (ix >= erts_smp_atomic_read(&tb->nactive)) { ix &= mask>>1; ASSERT(ix < erts_smp_atomic_read(&tb->nactive)); @@ -668,6 +668,7 @@ int db_create_hash(Process *p, DbTable *tbl) else { /* coarse locking */ tb->locks = NULL; } + ERTS_THR_MEMORY_BARRIER; #endif /* ERST_SMP */ return DB_ERROR_NONE; } @@ -2085,7 +2086,14 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) while(list != 0) { if (list->hvalue == INVALID_HASH) erts_print(to, to_arg, "*"); - erts_print(to, to_arg, "%T", make_tuple(list->dbterm.tpl)); + if (tb->common.compress) { + Eterm key = GETKEY(tb, list->dbterm.tpl); + erts_print(to, to_arg, "key=%R", key, list->dbterm.tpl); + } + else { + Eterm obj = make_tuple_rel(list->dbterm.tpl,list->dbterm.tpl); + erts_print(to, to_arg, "%R", obj, list->dbterm.tpl); + } if (list->next != 0) erts_print(to, to_arg, ","); list = list->next; @@ -2342,7 +2350,7 @@ static int alloc_seg(DbTableHash *tb) struct ext_segment* eseg; eseg = (struct ext_segment*) SEGTAB(tb)[seg_ix-1]; MY_ASSERT(eseg!=NULL && eseg->s.is_ext_segment); - erts_smp_atomic_set(&tb->segtab, (erts_aint_t) eseg->segtab); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t) eseg->segtab); tb->nsegs = eseg->nsegs; } ASSERT(seg_ix < tb->nsegs); @@ -2414,7 +2422,7 @@ static int free_seg(DbTableHash *tb, int free_records) MY_ASSERT(newtop->s.is_ext_segment); if (newtop->prev_segtab != NULL) { /* Time to use a smaller segtab */ - erts_smp_atomic_set(&tb->segtab, (erts_aint_t)newtop->prev_segtab); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t)newtop->prev_segtab); tb->nsegs = seg_ix; ASSERT(tb->nsegs == EXTSEG(SEGTAB(tb))->nsegs); } @@ -2431,7 +2439,7 @@ static int free_seg(DbTableHash *tb, int free_records) if (seg_ix > 0) { if (seg_ix < tb->nsegs) SEGTAB(tb)[seg_ix] = NULL; } else { - erts_smp_atomic_set(&tb->segtab, (erts_aint_t)NULL); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t)NULL); } #endif tb->nslots -= SEGSZ; @@ -2526,9 +2534,9 @@ static void grow(DbTableHash* tb, int nactive) } erts_smp_atomic_inc(&tb->nactive); if (from_ix == 0) { - erts_smp_atomic_set(&tb->szm, szm); + erts_smp_atomic_set_relb(&tb->szm, szm); } - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); /* Finally, let's split the bucket. We try to do it in a smart way to keep link order and avoid unnecessary updates of next-pointers */ @@ -2560,7 +2568,7 @@ static void grow(DbTableHash* tb, int nactive) return; abort: - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); } @@ -2604,7 +2612,7 @@ static void shrink(DbTableHash* tb, int nactive) erts_smp_atomic_set(&tb->nactive, src_ix); if (dst_ix == 0) { - erts_smp_atomic_set(&tb->szm, low_szm); + erts_smp_atomic_set_relb(&tb->szm, low_szm); } WUNLOCK_HASH(lck); @@ -2618,7 +2626,7 @@ static void shrink(DbTableHash* tb, int nactive) } /*else already done */ - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); } diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index a59c0c258d..9a0ba3a418 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -111,7 +111,7 @@ static void release_stack(DbTableTree* tb, DbTreeStack* stack) { if (stack == &tb->static_stack) { ASSERT(erts_smp_atomic_read(&tb->is_stack_busy) == 1); - erts_smp_atomic_set(&tb->is_stack_busy, 0); + erts_smp_atomic_set_relb(&tb->is_stack_busy, 0); } else { erts_db_free(ERTS_ALC_T_DB_STK, (DbTable *) tb, @@ -179,7 +179,7 @@ static ERTS_INLINE TreeDbTerm* replace_dbterm(DbTableTree *tb, TreeDbTerm* old, static TreeDbTerm *traverse_until(TreeDbTerm *t, int *current, int to); static void check_slot_pos(DbTableTree *tb); static void check_saved_stack(DbTableTree *tb); -static int check_table_tree(TreeDbTerm *t); +static int check_table_tree(DbTableTree* tb, TreeDbTerm *t); #define TREE_DEBUG #endif @@ -194,8 +194,8 @@ static int check_table_tree(TreeDbTerm *t); ** Debugging dump */ -static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, - int offset); +static void do_dump_tree2(DbTableTree*, int to, void *to_arg, int show, + TreeDbTerm *t, int offset); #else @@ -1730,6 +1730,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, ** Other interface routines (not directly coupled to one bif) */ + /* Display tree contents (for dump) */ static void db_print_tree(int to, void *to_arg, int show, @@ -1740,7 +1741,7 @@ static void db_print_tree(int to, void *to_arg, if (show) erts_print(to, to_arg, "\nTree data dump:\n" "------------------------------------------------\n"); - do_dump_tree2(to, to_arg, show, tb->root, 0); + do_dump_tree2(&tbl->tree, to, to_arg, show, tb->root, 0); if (show) erts_print(to, to_arg, "\n" "------------------------------------------------\n"); @@ -2694,7 +2695,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) while (1) { if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done) return j; - if (*aa==*bb) + if (is_same(*aa, NULL, *bb, b_base)) return 0; if (is_not_list(*aa) || is_not_list(*bb)) return do_cmp_partly_bound(*aa, *bb, b_base, done); @@ -2742,7 +2743,7 @@ static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_ erts_fprintf(stderr," > "); else erts_fprintf(stderr," == "); - erts_fprintf(stderr,"%T\n",bound_key); // HALFWORD BUG: printing rterm + erts_fprintf(stderr,"%R\n", bound_key, bk_base); #endif return ret; } @@ -3084,19 +3085,28 @@ static int doit_select_delete(DbTableTree *tb, TreeDbTerm *this, void *ptr, } #ifdef TREE_DEBUG -static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, - int offset) +static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show, + TreeDbTerm *t, int offset) { if (t == NULL) - return 0; - do_dump_tree2(to, to_arg, show, t->right, offset + 4); + return; + do_dump_tree2(tb, to, to_arg, show, t->right, offset + 4); if (show) { - erts_print(to, to_arg, "%*s%T (addr = %p, bal = %d)\n" - offset, "", make_tuple(t->dbterm.tpl), + const char* prefix; + Eterm term; + if (tb->common.compress) { + prefix = "key="; + term = GETKEY(tb, t->dbterm.tpl); + } + else { + prefix = ""; + term = make_tuple_rel(t->dbterm.tpl,t->dbterm.tpl); + } + erts_print(to, to_arg, "%*s%s%R (addr = %p, bal = %d)\n", + offset, "", prefix, term, t->dbterm.tpl, t, t->balance); } - do_dump_tree2(to, to_arg, show, t->left, offset + 4); - return sum; + do_dump_tree2(tb, to, to_arg, show, t->left, offset + 4); } #endif @@ -3106,7 +3116,7 @@ static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, void db_check_table_tree(DbTable *tbl) { DbTableTree *tb = &tbl->tree; - check_table_tree(tb->root); + check_table_tree(tb, tb->root); check_saved_stack(tb); check_slot_pos(tb); } @@ -3137,7 +3147,7 @@ static void check_slot_pos(DbTableTree *tb) "element position %d is really 0x%08X, when stack says " "it's 0x%08X\n", tb->stack.slot, t, tb->stack.array[tb->stack.pos - 1]); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); } } @@ -3152,14 +3162,14 @@ static void check_saved_stack(DbTableTree *tb) if (t != stack->array[0]) { erts_fprintf(stderr,"tb->stack[0] is 0x%08X, should be 0x%08X\n", stack->array[0], t); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } while (n < stack->pos) { if (t == NULL) { erts_fprintf(stderr, "NULL pointer in tree when stack not empty," " stack depth is %d\n", n); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } n++; @@ -3173,28 +3183,26 @@ static void check_saved_stack(DbTableTree *tb) "represent child pointer in tree!" "(left == 0x%08X, right == 0x%08X\n", n, tb->stack[n], t->left, t->right); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } } } } -static int check_table_tree(TreeDbTerm *t) +static int check_table_tree(DbTableTree* tb, TreeDbTerm *t) { int lh, rh; if (t == NULL) return 0; - lh = check_table_tree(t->left); - rh = check_table_tree(t->right); + lh = check_table_tree(tb, t->left); + rh = check_table_tree(tb, t->right); if ((rh - lh) != t->balance) { erts_fprintf(stderr, "Invalid tree balance for this node:\n"); - erts_fprintf(stderr,"balance = %d, left = 0x%08X, right = 0x%08X\n" - "data = %T", - t->balance, t->left, t->right, - make_tuple(t->dbterm.tpl)); + erts_fprintf(stderr,"balance = %d, left = 0x%08X, right = 0x%08X\n", + t->balance, t->left, t->right); erts_fprintf(stderr,"\nDump:\n---------------------------------\n"); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, t, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, t, 0); erts_fprintf(stderr,"\n---------------------------------\n"); } return ((rh > lh) ? rh : lh) + 1; diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 39bbe9633b..dc578f6d2a 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -700,6 +700,13 @@ erl_drv_thread_join(ErlDrvTid tid, void **respp) extern int erts_darwin_main_thread_pipe[2]; extern int erts_darwin_main_thread_result_pipe[2]; +int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp); +int erl_drv_steal_main_thread(char *name, + ErlDrvTid *dtid, + void* (*func)(void*), + void* arg, + ErlDrvThreadOpts *opts); + int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp) diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 9e18997890..9180508a49 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -240,7 +240,7 @@ typedef struct erts_lc_locked_lock_t_ erts_lc_locked_lock_t; struct erts_lc_locked_lock_t_ { erts_lc_locked_lock_t *next; erts_lc_locked_lock_t *prev; - Eterm extra; + UWord extra; Sint16 id; Uint16 flags; }; @@ -441,12 +441,12 @@ new_locked_lock(erts_lc_lock_t *lck, Uint16 op_flags) } static void -print_lock2(char *prefix, Sint16 id, Eterm extra, Uint16 flags, char *suffix) +print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix) { char *lname = (0 <= id && id < ERTS_LOCK_ORDER_SIZE ? erts_lock_order[id].name : "unknown"); - if (is_boxed(extra)) + if (is_not_immed(extra)) erts_fprintf(stderr, "%s'%s:%p%s'%s%s", prefix, @@ -1260,7 +1260,8 @@ erts_lc_init_lock(erts_lc_lock_t *lck, char *name, Uint16 flags) { lck->id = erts_lc_get_lock_order_id(name); - lck->extra = make_boxed(&lck->extra); + lck->extra = &lck->extra; + ASSERT(is_not_immed(lck->extra)); lck->flags = flags; lck->inited = ERTS_LC_INITITALIZED; } @@ -1270,6 +1271,7 @@ erts_lc_init_lock_x(erts_lc_lock_t *lck, char *name, Uint16 flags, Eterm extra) { lck->id = erts_lc_get_lock_order_id(name); lck->extra = extra; + ASSERT(is_immed(lck->extra)); lck->flags = flags; lck->inited = ERTS_LC_INITITALIZED; } diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h index cdb06d4458..b67f36fa06 100644 --- a/erts/emulator/beam/erl_lock_check.h +++ b/erts/emulator/beam/erl_lock_check.h @@ -39,7 +39,7 @@ typedef struct { int inited; Sint16 id; Uint16 flags; - Eterm extra; + UWord extra; } erts_lc_lock_t; #define ERTS_LC_INITITALIZED 0x7f7f7f7f diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8b48444904..68421b4387 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -472,7 +472,7 @@ static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj) int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) { struct enif_tmp_obj_t* tobj; - int sz; + Uint sz; if (is_binary(term)) { return enif_inspect_binary(env,term,bin); } @@ -483,7 +483,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) bin->ref_bin = NULL; return 1; } - if ((sz = io_list_len(term)) < 0) { + if (erts_iolist_size(term, &sz)) { return 0; } diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 1b07024ca1..326021643f 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -658,8 +658,6 @@ erts_port_task_free_port(Port *pp) when scheduled out... */ ErtsPortTask *ptp = port_task_alloc(); erts_smp_port_state_lock(pp); - ASSERT(erts_smp_atomic_read(&erts_ports_alive) > 0); - erts_smp_atomic_dec(&erts_ports_alive); pp->status &= ~ERTS_PORT_SFLG_CLOSING; pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED; erts_may_save_closed_port(pp); @@ -681,7 +679,6 @@ erts_port_task_free_port(Port *pp) port_is_dequeued = 1; } erts_smp_port_state_lock(pp); - erts_smp_atomic_dec(&erts_ports_alive); pp->status &= ~ERTS_PORT_SFLG_CLOSING; pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED; erts_may_save_closed_port(pp); diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index b71404fd27..34da9cab84 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -114,13 +114,13 @@ do { \ /* return 0 if list is not a non-empty flat list of printable characters */ static int -is_printable_string(Eterm list) +is_printable_string(Eterm list, Eterm* base) { int len = 0; int c; while(is_list(list)) { - Eterm* consp = list_val(list); + Eterm* consp = list_val_rel(list, base); Eterm hd = CAR(consp); if (!is_byte(hd)) @@ -226,17 +226,20 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) #define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */ static int -print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) +print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, + Eterm* obj_base) /* ignored if !HALFWORD_HEAP */ { DECLARE_WSTACK(s); int res; int i; Eterm val; Uint32 *ref_num; + union { + UWord word; + Eterm* ptr; + }popped; Eterm* nobj; -#if HALFWORD_HEAP - UWord wobj; -#endif + Wterm wobj; res = 0; @@ -258,18 +261,17 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) PRINT_CHAR(res, fn, arg, '}'); goto L_outer_loop; default: -#if HALFWORD_HEAP - obj = (Eterm) (wobj = WSTACK_POP(s)); -#else - obj = WSTACK_POP(s); -#endif + popped.word = WSTACK_POP(s); + switch (val) { case PRT_TERM: + obj = (Eterm) popped.word; break; case PRT_ONE_CONS: + obj = (Eterm) popped.word; L_print_one_cons: { - Eterm* cons = list_val(obj); + Eterm* cons = list_val_rel(obj, obj_base); Eterm tl; obj = CAR(cons); @@ -288,27 +290,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } break; case PRT_LAST_ARRAY_ELEMENT: - { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else - Eterm* ptr = (Eterm *) obj; -#endif - obj = *ptr; - } + obj = *popped.ptr; break; default: /* PRT_LAST_ARRAY_ELEMENT+1 and upwards */ - { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else - Eterm* ptr = (Eterm *) obj; -#endif - obj = *ptr++; - WSTACK_PUSH(s, (UWord) ptr); - WSTACK_PUSH(s, val-1); - WSTACK_PUSH(s, PRT_COMMA); - } + obj = *popped.ptr; + WSTACK_PUSH(s, (UWord) (popped.ptr + 1)); + WSTACK_PUSH(s, val-1); + WSTACK_PUSH(s, PRT_COMMA); break; } break; @@ -325,8 +313,12 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) PRINT_CHAR(res, fn, arg, '>'); goto L_done; } - - switch (tag_val_def(obj)) { +#if HALFWORD_HEAP + wobj = is_immed(obj) ? (Wterm)obj : rterm2wterm(obj, obj_base); +#else + wobj = (Wterm)obj; +#endif + switch (tag_val_def(wobj)) { case NIL_DEF: PRINT_STRING(res, fn, arg, "[]"); break; @@ -348,13 +340,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) int print_res; char def_buf[64]; char *buf, *big_str; - Uint sz = (Uint) big_decimal_estimate(obj); + Uint sz = (Uint) big_decimal_estimate(wobj); sz++; if (sz <= 64) buf = &def_buf[0]; else buf = erts_alloc(ERTS_ALC_T_TMP, sz); - big_str = erts_big_to_string(obj, buf, sz); + big_str = erts_big_to_string(wobj, buf, sz); print_res = erts_printf_string(fn, arg, big_str); if (buf != &def_buf[0]) erts_free(ERTS_ALC_T_TMP, (void *) buf); @@ -369,9 +361,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) case EXTERNAL_REF_DEF: PRINT_STRING(res, fn, arg, "#Ref<"); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) ref_channel_no(obj)); - ref_num = ref_numbers(obj); - for (i = ref_no_of_numbers(obj)-1; i >= 0; i--) { + (unsigned long) ref_channel_no(wobj)); + ref_num = ref_numbers(wobj); + for (i = ref_no_of_numbers(wobj)-1; i >= 0; i--) { PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, (unsigned long) ref_num[i]); } @@ -381,30 +373,30 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) case EXTERNAL_PID_DEF: PRINT_CHAR(res, fn, arg, '<'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_channel_no(obj)); + (unsigned long) pid_channel_no(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_number(obj)); + (unsigned long) pid_number(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_serial(obj)); + (unsigned long) pid_serial(wobj)); PRINT_CHAR(res, fn, arg, '>'); break; case PORT_DEF: case EXTERNAL_PORT_DEF: PRINT_STRING(res, fn, arg, "#Port<"); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) port_channel_no(obj)); + (unsigned long) port_channel_no(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) port_number(obj)); + (unsigned long) port_number(wobj)); PRINT_CHAR(res, fn, arg, '>'); break; case LIST_DEF: - if (is_printable_string(obj)) { + if (is_printable_string(obj, obj_base)) { int c; PRINT_CHAR(res, fn, arg, '"'); - nobj = list_val(obj); + nobj = list_val_rel(obj, obj_base); while (1) { if ((*dcount)-- <= 0) goto L_done; @@ -418,7 +410,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } if (is_not_list(*nobj)) break; - nobj = list_val(*nobj); + nobj = list_val_rel(*nobj, obj_base); } PRINT_CHAR(res, fn, arg, '"'); } else { @@ -428,7 +420,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } break; case TUPLE_DEF: - nobj = tuple_val(obj); /* pointer to arity */ + nobj = tuple_val(wobj); /* pointer to arity */ i = arityval(*nobj); /* arity */ PRINT_CHAR(res, fn, arg, '{'); WSTACK_PUSH(s,PRT_CLOSE_TUPLE); @@ -440,13 +432,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case FLOAT_DEF: { FloatDef ff; - GET_DOUBLE(obj, ff); + GET_DOUBLE(wobj, ff); PRINT_DOUBLE(res, fn, arg, 'e', 6, 0, ff.fd); } break; case BINARY_DEF: { - ProcBin* pb = (ProcBin *) binary_val(obj); + ProcBin* pb = (ProcBin *) binary_val(wobj); if (pb->size == 1) PRINT_STRING(res, fn, arg, "<<1 byte>>"); else { @@ -458,7 +450,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case EXPORT_DEF: { - Export* ep = *((Export **) (export_val(obj) + 1)); + Export* ep = *((Export **) (export_val(wobj) + 1)); Atom* module = atom_tab(atom_val(ep->code[0])); Atom* name = atom_tab(atom_val(ep->code[1])); @@ -474,7 +466,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case FUN_DEF: { - ErlFunThing *funp = (ErlFunThing *) fun_val(obj); + ErlFunThing *funp = (ErlFunThing *) fun_val(wobj); Atom *ap = atom_tab(atom_val(funp->fe->module)); PRINT_STRING(res, fn, arg, "#Fun<"); @@ -490,7 +482,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; default: PRINT_STRING(res, fn, arg, "<unknown:"); - PRINT_POINTER(res, fn, arg, (UWord) obj); + PRINT_POINTER(res, fn, arg, wobj); PRINT_CHAR(res, fn, arg, '>'); break; } @@ -503,9 +495,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } int -erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision) +erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision, + unsigned long* term_base) { - int res = print_term(fn, arg, (Uint) term, &precision); + int res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base); if (res < 0) return res; if (precision <= 0) diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h index 4f76028396..4ba22f12de 100644 --- a/erts/emulator/beam/erl_printf_term.h +++ b/erts/emulator/beam/erl_printf_term.h @@ -21,6 +21,6 @@ #define ERL_PRINTF_TERM_H__ #include "erl_printf_format.h" -int erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision); - +int erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision, + unsigned long* term_base); #endif diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 31f23d3978..2704359a8f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -166,9 +166,8 @@ static struct { static struct { erts_smp_mtx_t update_mtx; - erts_smp_atomic32_t active_runqs; + erts_smp_atomic32_t no_runqs; int last_active_runqs; - erts_smp_atomic32_t used_runqs; int forced_check_balance; erts_smp_atomic32_t checking_balance; int halftime; @@ -965,7 +964,7 @@ sched_spin_wait(ErtsSchedulerSleepInfo *ssi, int spincount) erts_aint32_t flgs; do { - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) != (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) { break; @@ -1114,7 +1113,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) else { erts_aint_t dt; - erts_smp_atomic32_set(&function_calls, 0); + erts_smp_atomic32_set_relb(&function_calls, 0); *fcalls = 0; sched_waiting_sys(esdp->no, rq); @@ -1147,7 +1146,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) nonblockable_aux_work(esdp, ssi, aux_work); #endif - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); goto sys_woken; @@ -1333,6 +1332,76 @@ wake_all_schedulers(void) } } +#define ERTS_NO_USED_RUNQS_SHIFT 16 +#define ERTS_NO_RUNQS_MASK 0xffff + +#if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_NO_RUNQS_MASK +# error "Too large amount of schedulers allowed" +#endif + +static ERTS_INLINE void +init_no_runqs(int active, int used) +{ + erts_aint32_t no_runqs = (erts_aint32_t) (active & ERTS_NO_RUNQS_MASK); + no_runqs |= (erts_aint32_t) ((used & ERTS_NO_RUNQS_MASK) << ERTS_NO_USED_RUNQS_SHIFT); + erts_smp_atomic32_init(&balance_info.no_runqs, no_runqs); +} + +static ERTS_INLINE void +get_no_runqs(int *active, int *used) +{ + erts_aint32_t no_runqs = erts_smp_atomic32_read(&balance_info.no_runqs); + if (active) + *active = (int) (no_runqs & ERTS_NO_RUNQS_MASK); + if (used) + *used = (int) ((no_runqs >> ERTS_NO_USED_RUNQS_SHIFT) & ERTS_NO_RUNQS_MASK); +} + +static ERTS_INLINE void +set_no_used_runqs(int used) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + while (1) { + erts_aint32_t act, new; + new = (used << ERTS_NO_USED_RUNQS_SHIFT) | (exp & ERTS_NO_RUNQS_MASK); + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + break; + exp = act; + } +} + +static ERTS_INLINE void +set_no_active_runqs(int active) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + while (1) { + erts_aint32_t act, new; + new = (exp & (ERTS_NO_RUNQS_MASK << ERTS_NO_USED_RUNQS_SHIFT)) | active; + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + break; + exp = act; + } +} + +static ERTS_INLINE int +try_inc_no_active_runqs(int active) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + if (((exp >> ERTS_NO_USED_RUNQS_SHIFT) & ERTS_NO_RUNQS_MASK) < active) + return 0; + if ((exp & ERTS_NO_RUNQS_MASK) + 1 == active) { + erts_aint32_t new, act; + new = (exp & ~ERTS_NO_RUNQS_MASK) | active; + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + return 1; + } + return 0; +} + + static ERTS_INLINE int chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) { @@ -1344,9 +1413,7 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) iflgs = erts_smp_atomic32_read(&wrq->info_flags); if (!(iflgs & (ERTS_RUNQ_IFLG_SUSPENDED|ERTS_RUNQ_IFLG_NONEMPTY))) { if (activate) { - if (ix == erts_smp_atomic32_cmpxchg(&balance_info.active_runqs, - ix+1, - ix)) { + if (try_inc_no_active_runqs(ix+1)) { erts_smp_xrunq_lock(crq, wrq); wrq->flags &= ~ERTS_RUNQ_FLG_INACTIVE; erts_smp_xrunq_unlock(crq, wrq); @@ -1363,8 +1430,9 @@ wake_scheduler_on_empty_runq(ErtsRunQueue *crq) { int ix = crq->ix; int stop_ix = ix; - int active_ix = erts_smp_atomic32_read(&balance_info.active_runqs); - int balance_ix = erts_smp_atomic32_read(&balance_info.used_runqs); + int active_ix, balance_ix; + + get_no_runqs(&active_ix, &balance_ix); if (active_ix > balance_ix) active_ix = balance_ix; @@ -1416,7 +1484,7 @@ erts_sched_notify_check_cpu_bind(void) int ix; if (erts_common_run_queue) { for (ix = 0; ix < erts_no_schedulers; ix++) - erts_smp_atomic32_set(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); + erts_smp_atomic32_set_relb(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); wake_all_schedulers(); } else { @@ -1871,8 +1939,7 @@ try_steal_task(ErtsRunQueue *rq) ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, rq_locked); - active_rqs = erts_smp_atomic32_read(&balance_info.active_runqs); - blnc_rqs = erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(&active_rqs, &blnc_rqs); if (active_rqs > blnc_rqs) active_rqs = blnc_rqs; @@ -1883,7 +1950,7 @@ try_steal_task(ErtsRunQueue *rq) if (active_rqs < blnc_rqs) { int no = blnc_rqs - active_rqs; int stop_ix = vix = active_rqs + rq->ix % no; - while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read_acqb(&no_empty_run_queues) < blnc_rqs) { res = check_possible_steal_victim(rq, &rq_locked, vix); if (res) goto done; @@ -1898,7 +1965,7 @@ try_steal_task(ErtsRunQueue *rq) vix = rq->ix; /* ... then try to steal a job from another active queue... */ - while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read_acqb(&no_empty_run_queues) < blnc_rqs) { vix++; if (vix >= active_rqs) vix = 0; @@ -1999,7 +2066,7 @@ check_balance(ErtsRunQueue *c_rq) return; } - blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(NULL, &blnc_no_rqs); if (blnc_no_rqs == 1) { c_rq->check_balance_reds = INT_MAX; erts_smp_atomic32_set(&balance_info.checking_balance, 0); @@ -2038,7 +2105,8 @@ check_balance(ErtsRunQueue *c_rq) forced = balance_info.forced_check_balance; balance_info.forced_check_balance = 0; - blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(¤t_active, &blnc_no_rqs); + if (blnc_no_rqs == 1) { erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_runq_lock(c_rq); @@ -2052,8 +2120,6 @@ check_balance(ErtsRunQueue *c_rq) if (balance_info.full_reds_history_index >= ERTS_FULL_REDS_HISTORY_SIZE) balance_info.full_reds_history_index = 0; - current_active = erts_smp_atomic32_read(&balance_info.active_runqs); - /* Read balance information for all run queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(qix); @@ -2387,7 +2453,7 @@ erts_fprintf(stderr, "--------------------------------\n"); } balance_info.last_active_runqs = active; - erts_smp_atomic32_set(&balance_info.active_runqs, active); + set_no_active_runqs(active); balance_info.halftime = 1; erts_smp_atomic32_set(&balance_info.checking_balance, 0); @@ -2695,9 +2761,8 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) erts_smp_atomic32_init(&schdlr_sspnd.msb.ongoing, 0); erts_smp_atomic32_init(&schdlr_sspnd.active, no_schedulers); schdlr_sspnd.msb.procs = NULL; - erts_smp_atomic32_set(&balance_info.used_runqs, - erts_common_run_queue ? 1 : no_schedulers_online); - erts_smp_atomic32_init(&balance_info.active_runqs, no_schedulers); + init_no_runqs(no_schedulers, + erts_common_run_queue ? 1 : no_schedulers_online); balance_info.last_active_runqs = no_schedulers; erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update"); balance_info.forced_check_balance = 0; @@ -2939,7 +3004,7 @@ sched_spin_suspended(ErtsSchedulerSleepInfo *ssi, int spincount) erts_aint32_t flgs; do { - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) @@ -3068,7 +3133,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) wake = 0; } - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; erts_smp_mtx_unlock(&schdlr_sspnd.mtx); @@ -3292,7 +3357,7 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *to_rq = ERTS_RUNQ_IX(ix % no); evacuate_run_queue(from_rq, to_rq); } - erts_smp_atomic32_set(&balance_info.used_runqs, no); + set_no_used_runqs(no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); } @@ -3346,7 +3411,7 @@ erts_set_schedulers_online(Process *p, for (ix = erts_no_run_queues-1; ix >= no; ix--) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % no)); - erts_smp_atomic32_set(&balance_info.used_runqs, no); + set_no_used_runqs(no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); for (ix = no; ix < online; ix++) { @@ -3443,7 +3508,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) else { erts_smp_mtx_unlock(&schdlr_sspnd.mtx); erts_smp_mtx_lock(&balance_info.update_mtx); - erts_smp_atomic32_set(&balance_info.used_runqs, 1); + set_no_used_runqs(1); for (ix = 0; ix < online; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); erts_smp_runq_lock(rq); @@ -3580,7 +3645,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % online)); - erts_smp_atomic32_set(&balance_info.used_runqs, online); + set_no_used_runqs(online); /* Make sure that we balance soon... */ balance_info.forced_check_balance = 1; erts_smp_runq_lock(ERTS_RUNQ_IX(0)); @@ -3887,21 +3952,9 @@ handle_pend_sync_suspend(Process *suspendee, } } -/* - * Like erts_pid2proc() but: - * - * * At least ERTS_PROC_LOCK_MAIN have to be held on c_p. - * * At least ERTS_PROC_LOCK_MAIN have to be taken on pid. - * * It also waits for proc to be in a state != running and garbing. - * * If ERTS_PROC_LOCK_BUSY is returned, the calling process has to - * yield (ERTS_BIF_YIELD[0-3]()). c_p might in this case have been - * suspended. - */ - - -Process * -erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, - Eterm pid, ErtsProcLocks pid_locks) +static Process * +pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, + Eterm pid, ErtsProcLocks pid_locks, int suspend) { Process *rp; int unlock_c_p_status; @@ -3928,7 +3981,7 @@ erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, c_p->suspendee = NIL; ASSERT(c_p->flags & F_P2PNR_RESCHED); c_p->flags &= ~F_P2PNR_RESCHED; - if (rp) + if (!suspend && rp) resume_process(rp); } else { @@ -3992,6 +4045,8 @@ erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, } /* rp is not running and we got the locks we want... */ + if (suspend) + suspend_process(rp_rq, rp); } erts_smp_runqs_unlock(cp_rq, rp_rq); } @@ -4004,6 +4059,35 @@ erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, return rp; } + +/* + * Like erts_pid2proc() but: + * + * * At least ERTS_PROC_LOCK_MAIN have to be held on c_p. + * * At least ERTS_PROC_LOCK_MAIN have to be taken on pid. + * * It also waits for proc to be in a state != running and garbing. + * * If ERTS_PROC_LOCK_BUSY is returned, the calling process has to + * yield (ERTS_BIF_YIELD[0-3]()). c_p might in this case have been + * suspended. + */ +Process * +erts_pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, + Eterm pid, ErtsProcLocks pid_locks) +{ + return pid2proc_not_running(c_p, c_p_locks, pid, pid_locks, 0); +} + +/* + * Like erts_pid2proc_not_running(), but hands over the process + * in a suspended state unless (c_p is looked up). + */ +Process * +erts_pid2proc_suspend(Process *c_p, ErtsProcLocks c_p_locks, + Eterm pid, ErtsProcLocks pid_locks) +{ + return pid2proc_not_running(c_p, c_p_locks, pid, pid_locks, 1); +} + /* * erts_pid2proc_nropt() is normally the same as * erts_pid2proc_not_running(). However it is only @@ -4117,6 +4201,21 @@ handle_pend_bif_async_suspend(Process *suspendee, } } +#else + +/* + * Non-smp version of erts_pid2proc_suspend(). + */ +Process * +erts_pid2proc_suspend(Process *c_p, ErtsProcLocks c_p_locks, + Eterm pid, ErtsProcLocks pid_locks) +{ + Process *rp = erts_pid2proc(c_p, c_p_locks, pid, pid_locks); + if (rp) + erts_suspend(rp, pid_locks, NULL); + return rp; +} + #endif /* ERTS_SMP */ /* @@ -4650,7 +4749,7 @@ internal_add_to_runq(ErtsRunQueue *runq, Process *p) if (p->status_flags & ERTS_PROC_SFLG_INRUNQ) return NULL; else if (p->runq_flags & ERTS_PROC_RUNQ_FLG_RUNNING) { - ASSERT(p->rcount == 0); + ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0); ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p); p->status_flags |= ERTS_PROC_SFLG_PENDADD2SCHEDQ; return NULL; @@ -4661,7 +4760,7 @@ internal_add_to_runq(ErtsRunQueue *runq, Process *p) ERTS_DBG_CHK_PROCS_RUNQ_NOPROC(runq, p); #ifndef ERTS_SMP /* Never schedule a suspended process (ok in smp case) */ - ASSERT(p->rcount == 0); + ASSERT(ERTS_PROC_IS_EXITING(p) || p->rcount == 0); add_runq = runq; #else ASSERT(!p->bound_runq || p->bound_runq == p->run_queue); @@ -5097,7 +5196,7 @@ Process *schedule(Process *p, int calls) esdp = erts_get_scheduler_data(); rq = erts_get_runq_current(esdp); ASSERT(esdp); - fcalls = (int) erts_smp_atomic32_read(&function_calls); + fcalls = (int) erts_smp_atomic32_read_acqb(&function_calls); actual_reds = reds = 0; erts_smp_runq_lock(rq); } else { @@ -5248,14 +5347,14 @@ Process *schedule(Process *p, int calls) | ERTS_RUNQ_FLG_CHK_CPU_BIND | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic32_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); suspend_scheduler(esdp); } if ((rq->flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) - || erts_smp_atomic32_read(&esdp->chk_cpu_bind)) { + || erts_smp_atomic32_read_acqb(&esdp->chk_cpu_bind)) { erts_sched_check_cpu_bind(esdp); } } @@ -5306,7 +5405,7 @@ Process *schedule(Process *p, int calls) if (rq->flags & (ERTS_RUNQ_FLG_SHARED_RUNQ | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic32_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); @@ -5350,7 +5449,7 @@ Process *schedule(Process *p, int calls) * Schedule system-level activities. */ - erts_smp_atomic32_set(&function_calls, 0); + erts_smp_atomic32_set_relb(&function_calls, 0); fcalls = 0; ASSERT(!erts_port_task_have_outstanding_io_tasks()); @@ -5392,7 +5491,7 @@ Process *schedule(Process *p, int calls) if (erts_common_run_queue->waiting) wake_scheduler(erts_common_run_queue, 0, 1); } - else if (erts_smp_atomic32_read(&no_empty_run_queues) != 0) { + else if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { wake_scheduler_on_empty_runq(rq); rq->wakeup_other = 0; } @@ -7603,7 +7702,8 @@ continue_exit_process(Process *p static void timeout_proc(Process* p) { - p->i = *((BeamInstr **) (UWord) p->def_arg_reg); + BeamInstr** pi = (BeamInstr **) p->def_arg_reg; + p->i = *pi; p->flags |= F_TIMO; p->flags &= ~F_INSLPQUEUE; diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 334ae5573f..296acc7367 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1535,6 +1535,10 @@ erts_get_atom_cache_map(Process *c_p) } #endif +Process *erts_pid2proc_suspend(Process *, + ErtsProcLocks, + Eterm, + ErtsProcLocks); #ifdef ERTS_SMP Process *erts_pid2proc_not_running(Process *, diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index c0397ca6c3..8833137112 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1538,8 +1538,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, Eterm tracee; #endif Eterm transformed_args[MAX_ARG]; - DeclareTmpHeap(sub_bin_heap_et,ERL_SUB_BIN_SIZE,p); - ErlSubBin *sub_bin_heap = (ErlSubBin *) sub_bin_heap_et; + DeclareTypedTmpHeap(ErlSubBin,sub_bin_heap,p); ASSERT(tracer_pid); if (*tracer_pid == am_true) { @@ -1600,21 +1599,20 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, if (is_boxed(arg) && header_is_bin_matchstate(*boxed_val(arg))) { ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(arg); ErlBinMatchBuffer* mb = &ms->mb; - ErlSubBin* sb = sub_bin_heap; Uint bit_size; ASSERT(sub_bin_heap->thing_word == 0); /* At most one of match context */ bit_size = mb->size - mb->offset; - sb->thing_word = HEADER_SUB_BIN; - sb->size = BYTE_OFFSET(bit_size); - sb->bitsize = BIT_OFFSET(bit_size); - sb->offs = BYTE_OFFSET(mb->offset); - sb->bitoffs = BIT_OFFSET(mb->offset); - sb->is_writable = 0; - sb->orig = mb->orig; - - arg = make_binary(sb); + sub_bin_heap->thing_word = HEADER_SUB_BIN; + sub_bin_heap->size = BYTE_OFFSET(bit_size); + sub_bin_heap->bitsize = BIT_OFFSET(bit_size); + sub_bin_heap->offs = BYTE_OFFSET(mb->offset); + sub_bin_heap->bitoffs = BIT_OFFSET(mb->offset); + sub_bin_heap->is_writable = 0; + sub_bin_heap->orig = mb->orig; + + arg = make_binary(sub_bin_heap); } transformed_args[i] = arg; } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 96da894d90..499bdd77ba 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -183,7 +183,7 @@ struct port { process to get (line oriented I/O)*/ Uint32 status; /* Status and type flags */ int control_flags; /* Flags for port_control() */ - Uint32 snapshot; /* Next snapshot that port should be part of */ + erts_aint32_t snapshot; /* Next snapshot that port should be part of */ struct reg_proc *reg; ErlDrvPDL port_data_lock; @@ -527,11 +527,10 @@ union erl_off_heap_ptr { /* arrays that get malloced at startup */ extern Port* erts_port; -extern erts_smp_atomic_t erts_ports_alive; extern Uint erts_max_ports; extern Uint erts_port_tab_index_mask; -extern erts_smp_atomic_t erts_ports_snapshot; +extern erts_smp_atomic32_t erts_ports_snapshot; extern erts_smp_atomic_t erts_dead_ports_ptr; ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt); @@ -541,12 +540,12 @@ ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt); ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt) { ERTS_SMP_LC_ASSERT(erts_smp_lc_spinlock_is_locked(&prt->state_lck)); - if (prt->snapshot != erts_smp_atomic_read(&erts_ports_snapshot)) { + if (prt->snapshot != erts_smp_atomic32_read_acqb(&erts_ports_snapshot)) { /* Dead ports are added from the end of the snapshot buffer */ Eterm* tombstone = (Eterm*) erts_smp_atomic_addtest(&erts_dead_ports_ptr, -(erts_aint_t)sizeof(Eterm)); ASSERT(tombstone+1 != NULL); - ASSERT(prt->snapshot == (Uint32) erts_smp_atomic_read(&erts_ports_snapshot) - 1); + ASSERT(prt->snapshot == erts_smp_atomic32_read(&erts_ports_snapshot) - 1); *tombstone = prt->id; } /*else no ongoing snapshot or port was already included or created after snapshot */ @@ -1653,10 +1652,14 @@ struct Sint_buf { }; char* Sint_to_buf(Sint, struct Sint_buf*); +#define ERTS_IOLIST_OK 0 +#define ERTS_IOLIST_OVERFLOW 1 +#define ERTS_IOLIST_TYPE 2 + Eterm buf_to_intlist(Eterm**, char*, int, Eterm); /* most callers pass plain char*'s */ int io_list_to_buf(Eterm, char*, int); int io_list_to_buf2(Eterm, char*, int); -int io_list_len(Eterm); +int erts_iolist_size(Eterm, Uint *); int is_string(Eterm); void erl_at_exit(void (*) (void*), void*); Eterm collect_memory(Process *); @@ -1890,6 +1893,8 @@ erts_alloc_message_heap(Uint size, # if defined(DEBUG) # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,Process) +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type *VariableName = (Type *) erts_debug_allocate_tmp_heap(sizeof(Type)/sizeof(Eterm),Process) # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,NULL) # define UseTmpHeap(Size,Proc) \ @@ -1911,6 +1916,8 @@ erts_alloc_message_heap(Uint size, # else # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm *VariableName = (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type *VariableName = (Type *) (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm *VariableName = (erts_get_scheduler_data()->tmp_heap)+(erts_get_scheduler_data()->num_tmp_heap_used) # define UseTmpHeap(Size,Proc) \ @@ -1936,6 +1943,8 @@ erts_alloc_message_heap(Uint size, #else # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm VariableName[Size] +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type VariableName[1] # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm VariableName[Size] # define UseTmpHeap(Size,Proc) /* Nothing */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index f619c6f88b..df5f8b22a3 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -56,7 +56,6 @@ static erts_smp_tsd_key_t driver_list_last_error_key; /* Save last DDLL error o per thread basis (for BC interfaces) */ Port* erts_port; /* The port table */ -erts_smp_atomic_t erts_ports_alive; erts_smp_atomic_t erts_bytes_out; /* No bytes sent out of the system */ erts_smp_atomic_t erts_bytes_in; /* No bytes gotten into the system */ @@ -82,6 +81,9 @@ static void driver_monitor_unlock_pdl(Port *p); #define DRV_MONITOR_UNLOCK_PDL(Port) /* nothing */ #endif +#define ERL_SMALL_IO_BIN_LIMIT (4*ERL_ONHEAP_BIN_LIMIT) +#define SMALL_WRITE_VEC 16 + static ERTS_INLINE ErlIOQueue* drvport2ioq(ErlDrvPort drvport) { @@ -190,7 +192,7 @@ typedef struct line_buf_context { static erts_smp_spinlock_t get_free_port_lck; static Uint last_port_num; static Uint port_num_mask; -erts_smp_atomic_t erts_ports_snapshot; /* Identifies the _next_ snapshot (not the ongoing) */ +erts_smp_atomic32_t erts_ports_snapshot; /* Identifies the _next_ snapshot (not the ongoing) */ static ERTS_INLINE void @@ -421,10 +423,9 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, new_name = (char*) erts_alloc(ERTS_ALC_T_PORT_NAME, sys_strlen(name)+1); sys_strcpy(new_name, name); erts_smp_runq_lock(runq); - erts_smp_atomic_inc(&erts_ports_alive); erts_smp_port_state_lock(prt); prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; - prt->snapshot = (Uint32) erts_smp_atomic_read(&erts_ports_snapshot); + prt->snapshot = erts_smp_atomic32_read(&erts_ports_snapshot); old_name = prt->name; prt->name = new_name; #ifdef ERTS_SMP @@ -954,13 +955,14 @@ do { \ int _bitoffs; \ int _bitsize; \ ERTS_GET_REAL_BIN(obj, _real, _offset, _bitoffs, _bitsize); \ - ASSERT(_bitsize == 0); \ + if (_bitsize != 0) goto L_type_error; \ if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \ _bitoffs == 0) { \ b_size += _size; \ + if (b_size < _size) goto L_overflow_error; \ in_clist = 0; \ v_size++; \ - if (_size >= bin_limit) { \ + if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \ p_in_clist = 0; \ p_v_size++; \ } else { \ @@ -972,6 +974,7 @@ do { \ } \ } else { \ c_size += _size; \ + if (c_size < _size) goto L_overflow_error; \ if (!in_clist) { \ in_clist = 1; \ v_size++; \ @@ -986,29 +989,30 @@ do { \ /* -** Size of a io list in bytes -** return -1 if error -** returns: - Total size of io list -** vsize - SysIOVec size needed for a writev -** csize - Number of bytes not in binary (in the common binary) -** pvsize - SysIOVec size needed if packing small binaries -** pcsize - Number of bytes in the common binary if packing -*/ + * Returns 0 if successful and a non-zero value otherwise. + * + * Return values through pointers: + * *vsize - SysIOVec size needed for a writev + * *csize - Number of bytes not in binary (in the common binary) + * *pvsize - SysIOVec size needed if packing small binaries + * *pcsize - Number of bytes in the common binary if packing + * *total_size - Total size of iolist in bytes + */ static int -io_list_vec_len(Eterm obj, int* vsize, int* csize, - int bin_limit, /* small binaries limit */ - int * pvsize, int * pcsize) +io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize, + Uint* pvsize, Uint* pcsize, Uint* total_size) { DECLARE_ESTACK(s); Eterm* objp; - int v_size = 0; - int c_size = 0; - int b_size = 0; - int in_clist = 0; - int p_v_size = 0; - int p_c_size = 0; - int p_in_clist = 0; + Uint v_size = 0; + Uint c_size = 0; + Uint b_size = 0; + Uint in_clist = 0; + Uint p_v_size = 0; + Uint p_c_size = 0; + Uint p_in_clist = 0; + Uint total; goto L_jump_start; /* avoid a push */ @@ -1022,6 +1026,9 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, if (is_byte(obj)) { c_size++; + if (c_size == 0) { + goto L_overflow_error; + } if (!in_clist) { in_clist = 1; v_size++; @@ -1061,32 +1068,31 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, } } + total = c_size + b_size; + if (total < c_size) { + goto L_overflow_error; + } + *total_size = total; + DESTROY_ESTACK(s); - if (vsize != NULL) - *vsize = v_size; - if (csize != NULL) - *csize = c_size; - if (pvsize != NULL) - *pvsize = p_v_size; - if (pcsize != NULL) - *pcsize = p_c_size; - return c_size + b_size; + *vsize = v_size; + *csize = c_size; + *pvsize = p_v_size; + *pcsize = p_c_size; + return 0; L_type_error: + L_overflow_error: DESTROY_ESTACK(s); - return -1; + return 1; } -#define ERL_SMALL_IO_BIN_LIMIT (4*ERL_ONHEAP_BIN_LIMIT) -#define SMALL_WRITE_VEC 16 - - /* write data to a port */ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) { char *buf; erts_driver_t *drv = p->drv_ptr; - int size; + Uint size; int fpe_was_unmasked; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); @@ -1094,10 +1100,10 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) p->caller = caller_id; if (drv->outputv != NULL) { - int vsize; - int csize; - int pvsize; - int pcsize; + Uint vsize; + Uint csize; + Uint pvsize; + Uint pcsize; int blimit; SysIOVec iv[SMALL_WRITE_VEC]; ErlDrvBinary* bv[SMALL_WRITE_VEC]; @@ -1106,9 +1112,8 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) ErlDrvBinary* cbin; ErlIOVec ev; - if ((size = io_list_vec_len(list, &vsize, &csize, - ERL_SMALL_IO_BIN_LIMIT, - &pvsize, &pcsize)) < 0) { + if (io_list_vec_len(list, &vsize, &csize, + &pvsize, &pcsize, &size)) { goto bad_value; } /* To pack or not to pack (small binaries) ...? */ @@ -1183,7 +1188,7 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) else { ASSERT(r == -1); /* Overflow */ erts_free(ERTS_ALC_T_TMP, buf); - if ((size = io_list_len(list)) < 0) { + if (erts_iolist_size(list, &size)) { goto bad_value; } @@ -1274,7 +1279,6 @@ void init_io(void) erts_smp_atomic_init(&erts_bytes_out, 0); erts_smp_atomic_init(&erts_bytes_in, 0); - erts_smp_atomic_init(&erts_ports_alive, 0); for (i = 0; i < erts_max_ports; i++) { erts_port_task_init_sched(&erts_port[i].sched); @@ -1296,7 +1300,7 @@ void init_io(void) erts_port[i].port_data_lock = NULL; } - erts_smp_atomic_init(&erts_ports_snapshot, (erts_aint_t) 0); + erts_smp_atomic32_init(&erts_ports_snapshot, (erts_aint32_t) 0); last_port_num = 0; erts_smp_spinlock_init(&get_free_port_lck, "get_free_port"); @@ -2147,7 +2151,7 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) byte* to_port = NULL; /* Buffer to write to port. */ /* Initialization is for shutting up warning about use before set. */ - int to_len = 0; /* Length of buffer. */ + Uint to_len = 0; /* Length of buffer. */ int must_free = 0; /* True if the buffer should be freed. */ char port_result[ERL_ONHEAP_BIN_LIMIT]; /* Default buffer for result from port. */ char* port_resp; /* Pointer to result buffer. */ @@ -2192,7 +2196,7 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) } else { ASSERT(r == -1); /* Overflow */ erts_free(ERTS_ALC_T_TMP, (void *) to_port); - if ((to_len = io_list_len(iolist)) < 0) { /* Type error */ + if (erts_iolist_size(iolist, &to_len)) { /* Type error */ return THE_NON_VALUE; } must_free = 1; diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c index 21d6ce9304..3e9243c77d 100644 --- a/erts/emulator/beam/safe_hash.c +++ b/erts/emulator/beam/safe_hash.c @@ -99,7 +99,7 @@ static void rehash(SafeHash* h, int grow_limit) erts_free(h->type, (void *) old_tab); } /*else already done */ - erts_smp_atomic_set(&h->is_rehashing, 0); + erts_smp_atomic_set_relb(&h->is_rehashing, 0); } diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 6b4f3b3b36..a17de717bc 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3021,13 +3021,25 @@ int io_list_to_buf(Eterm obj, char* buf, int len) return -1; } -int io_list_len(Eterm obj) +/* + * Return 0 if successful, and non-zero if unsuccessful. + */ +int erts_iolist_size(Eterm obj, Uint* sizep) { Eterm* objp; - Sint len = 0; + Uint size = 0; DECLARE_ESTACK(s); goto L_again; +#define SAFE_ADD(Var, Val) \ + do { \ + Uint valvar = (Val); \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } while (0) + while (!ESTACK_ISEMPTY(s)) { obj = ESTACK_POP(s); L_again: @@ -3037,9 +3049,12 @@ int io_list_len(Eterm obj) /* Head */ obj = CAR(objp); if (is_byte(obj)) { - len++; + size++; + if (size == 0) { + goto L_overflow_error; + } } else if (is_binary(obj) && binary_bitsize(obj) == 0) { - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_list(obj)) { ESTACK_PUSH(s, CDR(objp)); goto L_iter_list; /* on head */ @@ -3051,23 +3066,29 @@ int io_list_len(Eterm obj) if (is_list(obj)) goto L_iter_list; /* on tail */ else if (is_binary(obj) && binary_bitsize(obj) == 0) { - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_not_nil(obj)) { goto L_type_error; } } else if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */ - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_not_nil(obj)) { goto L_type_error; } } +#undef SAFE_ADD DESTROY_ESTACK(s); - return len; + *sizep = size; + return ERTS_IOLIST_OK; + + L_overflow_error: + DESTROY_ESTACK(s); + return ERTS_IOLIST_OVERFLOW; L_type_error: DESTROY_ESTACK(s); - return -1; + return ERTS_IOLIST_TYPE; } /* return 0 if item is not a non-empty flat list of bytes */ diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index 19cd32c68f..2660f74a82 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -1,9 +1,8 @@ /* * %CopyrightBegin% - - * + * * Copyright Ericsson AB 2001-2011. All Rights Reserved. - * + * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the @@ -37,14 +36,25 @@ #include "hipe_arch.h" #include "hipe_stack.h" -BIF_RETTYPE hipe_bifs_show_estack_1(BIF_ALIST_1) +static void proc_unlock(Process* c_p, Process* rp) { + ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; + if (rp == c_p) { + locks &= ~ERTS_PROC_LOCK_MAIN; + } + if (rp && locks) { + erts_smp_proc_unlock(rp, locks); + } +} + +BIF_RETTYPE hipe_bifs_show_estack_1(BIF_ALIST_1) +{ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_ARG_1, ERTS_PROC_LOCKS_ALL); if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_estack(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -55,7 +65,7 @@ BIF_RETTYPE hipe_bifs_show_heap_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_heap(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -66,7 +76,7 @@ BIF_RETTYPE hipe_bifs_show_nstack_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_nstack(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -82,7 +92,7 @@ BIF_RETTYPE hipe_bifs_show_pcb_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_pcb(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 650861b54b..bced90785d 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -587,9 +587,9 @@ static void print_params(FILE *fp, void (*print_param)(FILE*,const struct rts_pa (*print_param)(fp, &rts_params[i]); } -static int do_c(FILE *fp) +static int do_c(FILE *fp, const char* this_exe) { - fprintf(fp, "/* File: hipe_literals.h, generated by hipe_mkliterals */\n"); + fprintf(fp, "/* File: hipe_literals.h, generated by %s */\n", this_exe); fprintf(fp, "#ifndef __HIPE_LITERALS_H__\n"); fprintf(fp, "#define __HIPE_LITERALS_H__\n\n"); print_literals(fp, c_define_literal); @@ -603,9 +603,9 @@ static int do_c(FILE *fp) return 0; } -static int do_e(FILE *fp) +static int do_e(FILE *fp, const char* this_exe) { - fprintf(fp, "%%%% File: hipe_literals.hrl, generated by hipe_mkliterals"); + fprintf(fp, "%%%% File: hipe_literals.hrl, generated by %s", this_exe); fprintf(fp, "\n\n"); print_literals(fp, e_define_literal); fprintf(fp, "\n"); @@ -622,9 +622,9 @@ int main(int argc, const char **argv) compute_crc(); if (argc == 2) { if (strcmp(argv[1], "-c") == 0) - return do_c(stdout); + return do_c(stdout, argv[0]); if (strcmp(argv[1], "-e") == 0) - return do_e(stdout); + return do_e(stdout, argv[0]); } fprintf(stderr, "usage: %s [-c | -e] > output-file\n", argv[0]); return 1; diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 218bd79584..71b374527e 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1137,6 +1137,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) restart: +#ifdef ERTS_BREAK_REQUESTED + if (ERTS_BREAK_REQUESTED) + erts_do_break_handling(); +#endif + /* Figure out timeout value */ if (do_wait) { erts_time_remaining(&wait_time); diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index ffa3a6328c..eaef6680dd 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -134,7 +134,16 @@ static int mmap_fd; #define CAN_PARTLY_DESTROY 0 #endif -static const ErtsMsegOpt_t default_opt = ERTS_MSEG_DEFAULT_OPT_INITIALIZER; +const ErtsMsegOpt_t erts_mseg_default_opt = { + 1, /* Use cache */ + 1, /* Preserv data */ + 0, /* Absolute shrink threshold */ + 0 /* Relative shrink threshold */ +#if HALFWORD_HEAP + ,0 /* need low memory */ +#endif +}; + typedef struct cache_desc_t_ { void *seg; @@ -605,18 +614,10 @@ mseg_clear_cache(MemKind* mk) INC_CC(clear_cache); } -static ERTS_INLINE MemKind* type2mk(ErtsAlcType_t atype) +static ERTS_INLINE MemKind* memkind(const ErtsMsegOpt_t *opt) { #if HALFWORD_HEAP - switch (atype) { - case ERTS_ALC_A_ETS: - case ERTS_ALC_A_BINARY: - case ERTS_ALC_A_FIXED_SIZE: - case ERTS_ALC_A_DRIVER: - return &hi_mem; - default: - return &low_mem; - } + return opt->low_mem ? &low_mem : &hi_mem; #else return &the_mem; #endif @@ -628,7 +629,7 @@ mseg_alloc(ErtsAlcType_t atype, Uint *size_p, const ErtsMsegOpt_t *opt) Uint max, min, diff_size, size; cache_desc_t *cd, *cand_cd; void *seg; - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); INC_CC(alloc); @@ -742,7 +743,7 @@ static void mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, const ErtsMsegOpt_t *opt) { - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); cache_desc_t *cd; ERTS_MSEG_DEALLOC_STAT(mk,size); @@ -800,7 +801,7 @@ static void * mseg_realloc(ErtsAlcType_t atype, void *seg, Uint old_size, Uint *new_size_p, const ErtsMsegOpt_t *opt) { - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); void *new_seg; Uint new_size; @@ -1372,7 +1373,7 @@ erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, const ErtsMsegOpt_t *opt) void * erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p) { - return erts_mseg_alloc_opt(atype, size_p, &default_opt); + return erts_mseg_alloc_opt(atype, size_p, &erts_mseg_default_opt); } void @@ -1387,7 +1388,7 @@ erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, Uint size, void erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size) { - erts_mseg_dealloc_opt(atype, seg, size, &default_opt); + erts_mseg_dealloc_opt(atype, seg, size, &erts_mseg_default_opt); } void * @@ -1405,7 +1406,7 @@ void * erts_mseg_realloc(ErtsAlcType_t atype, void *seg, Uint old_size, Uint *new_size_p) { - return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p, &default_opt); + return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p, &erts_mseg_default_opt); } void diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index d8053eb0d9..fbb66ee33b 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -60,15 +60,12 @@ typedef struct { int preserv; UWord abs_shrink_th; UWord rel_shrink_th; +#if HALFWORD_HEAP + int low_mem; +#endif } ErtsMsegOpt_t; -#define ERTS_MSEG_DEFAULT_OPT_INITIALIZER \ -{ \ - 1, /* Use cache */ \ - 1, /* Preserv data */ \ - 0, /* Absolute shrink threshold */ \ - 0 /* Relative shrink threshold */ \ -} +extern const ErtsMsegOpt_t erts_mseg_default_opt; void *erts_mseg_alloc(ErtsAlcType_t, Uint *); void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, const ErtsMsegOpt_t *); diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 3ae5b8d747..f5c785d683 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -766,7 +766,7 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) short filter; int fd = (int) ebuf[i].ident; - switch ((int) ebuf[i].udata) { + switch ((int) (long) ebuf[i].udata) { /* * Since we use a lazy update approach EV_DELETE will @@ -805,7 +805,7 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) if (fd == (int) ebuf[j].ident) { ebuf[j].udata = (void *) ERTS_POLL_KQ_OP_HANDLED; if (!(ebuf[j].flags & EV_ERROR)) { - switch ((int) ebuf[j].udata) { + switch ((int) (long) ebuf[j].udata) { case ERTS_POLL_KQ_OP_ADD2_W: filter = EVFILT_WRITE; goto rm_add_fb; @@ -823,7 +823,8 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) } } /* The other add succeded... */ - filter = (((int) ebuf[i].udata == ERTS_POLL_KQ_OP_ADD2_W) + filter = ((((int) (long) ebuf[i].udata) + == ERTS_POLL_KQ_OP_ADD2_W) ? EVFILT_READ : EVFILT_WRITE); rm_add_fb: diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 7662f190ef..074e2e247f 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -693,6 +693,7 @@ static void *break_waiter(void *param) ResetEvent(harr[0]); erts_mtx_lock(&break_waiter_lock); erts_atomic32_set(&break_waiter_state,BREAK_WAITER_GOT_BREAK); + ERTS_THR_MEMORY_BARRIER; SetEvent(break_happened_event); erts_mtx_unlock(&break_waiter_lock); break; @@ -700,6 +701,7 @@ static void *break_waiter(void *param) ResetEvent(harr[1]); erts_mtx_lock(&break_waiter_lock); erts_atomic32_set(&break_waiter_state,BREAK_WAITER_GOT_HALT); + ERTS_THR_MEMORY_BARRIER; SetEvent(break_happened_event); erts_mtx_unlock(&break_waiter_lock); break; diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 7e409f053e..4e82381fba 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -23,12 +23,12 @@ %% Tests binaries and the BIFs: %% list_to_binary/1 %% iolist_to_binary/1 -%% bitstr_to_list/1 +%% list_to_bitstring/1 %% binary_to_list/1 %% binary_to_list/3 %% binary_to_term/1 %% binary_to_term/2 -%% bitstr_to_list/1 +%% bitstring_to_list/1 %% term_to_binary/1 %% erlang:external_size/1 %% size(Binary) @@ -275,12 +275,33 @@ bad_list_to_binary(Config) when is_list(Config) -> ?line test_bad_bin(fun(X, Y) -> X*Y end), ?line test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), ?line test_bad_bin([fun(X) -> X + 1 end]), + + %% Test iolists that do not fit in the address space. + %% Unfortunately, it would be too slow to test in a 64-bit emulator. + case erlang:system_info(wordsize) of + 4 -> huge_iolists(); + _ -> ok + end. + +huge_iolists() -> + FourGigs = 1 bsl 32, + ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + [1 bsl N || N <- lists:seq(33, 37)], + ?line Base = <<0:(1 bsl 20)/unit:8>>, + [begin + L = build_iolist(Sz, Base), + ?line {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), + ?line {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])), + ?line {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), + ?line {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) + end || Sz <- Sizes], ok. test_bad_bin(List) -> {'EXIT',{badarg,_}} = (catch list_to_binary(List)), {'EXIT',{badarg,_}} = (catch iolist_to_binary(List)), - {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)). + {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)), + {'EXIT',{badarg,_}} = (catch iolist_size(List)). bad_binary_to_list(doc) -> "Tries binary_to_list/1,3 with bad arguments."; bad_binary_to_list(Config) when is_list(Config) -> @@ -516,18 +537,65 @@ external_size_1(Term, Size0, Limit) when Size0 < Limit -> external_size_1(_, _, _) -> ok. t_iolist_size(Config) when is_list(Config) -> - %% Build a term whose external size only fits in a big num (on 32-bit CPU). - Bin = iolist_to_binary(lists:seq(0, 254)), - ?line ok = t_iolist_size_1(Bin, 0, 16#7FFFFFFF), - ?line ok = t_iolist_size_1(make_unaligned_sub_binary(Bin), 0, 16#7FFFFFFF). + ?line Seed = now(), + ?line io:format("Seed: ~p", [Seed]), + ?line random:seed(Seed), + ?line Base = <<0:(1 bsl 20)/unit:8>>, + ?line Powers = [1 bsl N || N <- lists:seq(2, 37)], + ?line Sizes0 = [[N - random:uniform(N div 2), + lists:seq(N-2, N+2), + N+N div 2, + N + random:uniform(N div 2)] || + N <- Powers], + %% Test sizes around 1^32 more thoroughly. + FourGigs = 1 bsl 32, + ?line Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0, + ?line Sizes2 = lists:flatten(Sizes1), + ?line Sizes = lists:usort(Sizes2), + io:format("~p sizes:", [length(Sizes)]), + io:format("~p\n", [Sizes]), + ?line [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes], + ok. -t_iolist_size_1(IOList, Size0, Limit) when Size0 < Limit -> - case iolist_size(IOList) of - Size when is_integer(Size), Size0 < Size -> - io:format("~p", [Size]), - t_iolist_size_1([IOList|IOList], Size, Limit) +build_iolist(N, Base) when N < 16 -> + case random:uniform(3) of + 1 -> + <<Bin:N/binary,_/binary>> = Base, + Bin; + _ -> + lists:seq(1, N) + end; +build_iolist(N, Base) when N =< byte_size(Base) -> + case random:uniform(3) of + 1 -> + <<Bin:N/binary,_/binary>> = Base, + Bin; + 2 -> + <<Bin:N/binary,_/binary>> = Base, + [Bin]; + 3 -> + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L]; + 1 -> + L = build_iolist(N div 2, Base), + [L,L,45] + end end; -t_iolist_size_1(_, _, _) -> ok. +build_iolist(N0, Base) -> + Small = random:uniform(15), + Seq = lists:seq(1, Small), + N = N0 - Small, + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L|Seq]; + 1 -> + L = build_iolist(N div 2, Base), + [47,L,L|Seq] + end. + bad_binary_to_term_2(doc) -> "OTP-4053."; bad_binary_to_term_2(suite) -> []; @@ -1183,34 +1251,7 @@ deep(Config) when is_list(Config) -> deep_roundtrip(T) -> B = term_to_binary(T), - true = deep_eq(T, binary_to_term(B)). - -%% -%% FIXME: =:= runs out of stack. -%% -deep_eq([H1|T1], [H2|T2]) -> - deep_eq(H1, H2) andalso deep_eq(T1, T2); -deep_eq(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> - deep_eq_tup(T1, T2, tuple_size(T1)); -deep_eq(T1, T2) when is_function(T1), is_function(T2) -> - {uniq,U1} = erlang:fun_info(T1, uniq), - {index,I1} = erlang:fun_info(T1, index), - {arity,A1} = erlang:fun_info(T1, arity), - {env,E1} = erlang:fun_info(T1, env), - {uniq,U2} = erlang:fun_info(T2, uniq), - {index,I2} = erlang:fun_info(T2, index), - {arity,A2} = erlang:fun_info(T2, arity), - {env,E2} = erlang:fun_info(T2, env), - U1 =:= U2 andalso I1 =:= I2 andalso A1 =:= A2 andalso - deep_eq(E1, E2); -deep_eq(T1, T2) -> - T1 =:= T2. - -deep_eq_tup(_T1, _T2, 0) -> - true; -deep_eq_tup(T1, T2, N) -> - deep_eq(element(N, T1), element(N, T2)) andalso - deep_eq_tup(T1, T2, N-1). + T = binary_to_term(B). obsolete_funs(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index c1a048be75..a062cea117 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -498,7 +498,9 @@ do_false_dependency(Init, Code) -> ?line unlink(Pid), exit(Pid, kill), ?line true = erlang:purge_module(cpbugx), ?line true = erlang:delete_module(cpbugx), + ?line code:is_module_native(cpbugx), % test is_module_native on deleted code ?line true = erlang:purge_module(cpbugx), + ?line code:is_module_native(cpbugx), % test is_module_native on purged code ok. false_dependency_loop(Parent, Init, SendInitAck) -> diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 7600a44988..520e3e8c76 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -38,7 +38,7 @@ timer_change/1, timer_delay/1, queue_echo/1, - fun_to_port/1, + outputv_errors/1, driver_unloaded/1, io_ready_exit/1, use_fallback_pollset/1, @@ -129,7 +129,7 @@ end_per_testcase(Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [fun_to_port, outputv_echo, queue_echo, {group, timer}, + [outputv_errors, outputv_echo, queue_echo, {group, timer}, driver_unloaded, io_ready_exit, use_fallback_pollset, bad_fd_in_pollset, driver_event, fd_change, steal_control, otp_6602, 'driver_system_info_ver1.0', @@ -165,37 +165,89 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - -fun_to_port(doc) -> "Test sending a fun to port with an outputv-capable driver."; -fun_to_port(Config) when is_list(Config) -> +outputv_errors(doc) -> "Test sending bad types to port with an outputv-capable driver."; +outputv_errors(Config) when is_list(Config) -> ?line Path = ?config(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, outputv_drv), - ?line fun_to_port_1(fun() -> 33 end), - ?line fun_to_port_1([fun() -> 42 end]), - ?line fun_to_port_1([1|fun() -> 42 end]), - L = build_io_list(65536), - ?line fun_to_port_1([L,fun() -> 42 end]), - ?line fun_to_port_1([L|fun() -> 42 end]), + outputv_bad_types(fun(T) -> + ?line outputv_errors_1(T), + ?line outputv_errors_1([1|T]), + ?line L = [1,2,3], + ?line outputv_errors_1([L,T]), + ?line outputv_errors_1([L|T]) + end), + outputv_errors_1(42), + + %% Test iolists that do not fit in the address space. + %% Unfortunately, it would be too slow to test in a 64-bit emulator. + case erlang:system_info(wordsize) of + 4 -> outputv_huge_iolists(); + _ -> ok + end. + +outputv_bad_types(Test) -> + Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end, + [1|2],<<1:1>>,<<1:9>>,<<1:15>>], + _ = [Test(Type) || Type <- Types], ok. -fun_to_port_1(Term) -> - Port = open_port({spawn,outputv_drv}, []), +outputv_huge_iolists() -> + FourGigs = 1 bsl 32, + ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + [1 bsl N || N <- lists:seq(33, 37)], + ?line Base = <<0:(1 bsl 20)/unit:8>>, + [begin + ?line L = build_iolist(Sz, Base), + ?line outputv_errors_1(L) + end || Sz <- Sizes], + ok. + +outputv_errors_1(Term) -> + Port = open_port({spawn_driver,outputv_drv}, []), {'EXIT',{badarg,_}} = (catch port_command(Port, Term)), port_close(Port). -build_io_list(0) -> []; -build_io_list(1) -> [7]; -build_io_list(N) -> - L = build_io_list(N div 2), +build_iolist(N, Base) when N < 16 -> + case random:uniform(3) of + 1 -> + <<Bin:N/binary,_/binary>> = Base, + Bin; + _ -> + lists:seq(1, N) + end; +build_iolist(N, Base) when N =< byte_size(Base) -> + case random:uniform(3) of + 1 -> + <<Bin:N/binary,_/binary>> = Base, + Bin; + 2 -> + <<Bin:N/binary,_/binary>> = Base, + [Bin]; + 3 -> + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L]; + 1 -> + L = build_iolist(N div 2, Base), + [L,L,45] + end + end; +build_iolist(N0, Base) -> + Small = random:uniform(15), + Seq = lists:seq(1, Small), + N = N0 - Small, case N rem 2 of - 0 -> [L|L]; - 1 -> [7,L|L] + 0 -> + L = build_iolist(N div 2, Base), + [L,L|Seq]; + 1 -> + L = build_iolist(N div 2, Base), + [47,L,L|Seq] end. - - outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."]; outputv_echo(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(10)), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 36bae908aa..f68e712268 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -50,7 +50,8 @@ processes_last_call_trap/1, processes_gc_trap/1, processes_term_proc_list/1, otp_7738_waiting/1, otp_7738_suspended/1, - otp_7738_resume/1]). + otp_7738_resume/1, + garb_other_running/1]). -export([prio_server/2, prio_client/2]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -72,7 +73,7 @@ all() -> bad_register, garbage_collect, process_info_messages, process_flag_badarg, process_flag_heap_size, spawn_opt_heap_size, otp_6237, {group, processes_bif}, - {group, otp_7738}]. + {group, otp_7738}, garb_other_running]. groups() -> [{t_exit_2, [], @@ -2116,6 +2117,41 @@ otp_7738_test(Type) -> end, ?line ok. +gor(Reds, Stop) -> + receive + {From, reds} -> + From ! {reds, Reds, self()}, + gor(Reds+1, Stop); + {From, Stop} -> + From ! {stopped, Stop, Reds, self()} + after 0 -> + gor(Reds+1, Stop) + end. + +garb_other_running(Config) when is_list(Config) -> + ?line Stop = make_ref(), + ?line {Pid, Mon} = spawn_monitor(fun () -> gor(0, Stop) end), + ?line Reds = lists:foldl(fun (_, OldReds) -> + ?line erlang:garbage_collect(Pid), + ?line receive after 1 -> ok end, + ?line Pid ! {self(), reds}, + ?line receive + {reds, NewReds, Pid} -> + ?line true = (NewReds > OldReds), + ?line NewReds + end + end, + 0, + lists:seq(1, 10000)), + ?line receive after 1 -> ok end, + ?line Pid ! {self(), Stop}, + ?line receive + {stopped, Stop, StopReds, Pid} -> + ?line true = (StopReds > Reds) + end, + ?line receive {'DOWN', Mon, process, Pid, normal} -> ok end, + ?line ok. + %% Internal functions wait_until(Fun) -> diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 60b3af7db7..90d3be9448 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -394,6 +394,7 @@ int main(int argc, char **argv) int print_args_exit = 0; int print_qouted_cmd_exit = 0; erts_cpu_info_t *cpuinfo = NULL; + char* emu_name; #ifdef __WIN32__ this_module_handle = module; @@ -566,6 +567,7 @@ int main(int argc, char **argv) usage("+MYm"); } emu = add_extra_suffixes(emu, emu_type); + emu_name = strsave(emu); erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); emu = strsave(tmpStr); @@ -682,6 +684,9 @@ int main(int argc, char **argv) verbose = 1; } else if (strcmp(argv[i], "-emu_args_exit") == 0) { print_args_exit = 1; + } else if (strcmp(argv[i], "-emu_name_exit") == 0) { + printf("%s\n", emu_name); + exit(0); } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) { print_qouted_cmd_exit = 1; } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */ @@ -1970,6 +1975,11 @@ initial_argv_massage(int *argc, char ***argv) */ vix = 0; + + av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); + if (av) + avv[vix++].argv = av; + av = build_args_from_env("ERL_AFLAGS"); if (av) avv[vix++].argv = av; @@ -1984,10 +1994,6 @@ initial_argv_massage(int *argc, char ***argv) if (av) avv[vix++].argv = av; - av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); - if (av) - avv[vix++].argv = av; - av = build_args_from_env("ERL_ZFLAGS"); if (av) avv[vix++].argv = av; diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 0355f2629f..0b2d6512ea 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -62,13 +62,21 @@ cxargs_add() { done } +eeargs= +eeargs_add() { + while [ $# -gt 0 ]; do + cargs="$cargs $1" + eeargs="$eeargs $1" + shift + done +} + core= GDB= GDBBP= GDBARGS= TYPE= -EMU_TYPE= debug= run_valgrind=no @@ -127,34 +135,37 @@ while [ $# -gt 0 ]; do ;; "-smp") shift - cargs="$cargs -smp" - EMU_TYPE=.smp + if [ $# -le 0 ]; then + eeargs_add -smp + else + case $1 in + disable) + shift + eeargs_add -smpdisable + ;; + enable) + shift + eeargs_add -smp + ;; + *) + eeargs_add -smp + esac + fi + ;; + "-smpdisable") + shift + eeargs_add -smpdisable ;; "-lcnt") shift cargs="$cargs -lcnt" TYPE=.lcnt ;; - "-frag") - shift - cargs="$cargs -frag" - EMU_TYPE=.frag - ;; - "-smp_frag") - shift - cargs="$cargs -smp_frag" - EMU_TYPE=.smp_frag - ;; "-gprof") shift cargs="$cargs -gprof" TYPE=.gprof ;; - "-hybrid") - shift - cargs="$cargs -hybrid" - EMU_TYPE=.hybrid - ;; "-debug") shift cargs="$cargs -debug" @@ -180,11 +191,6 @@ while [ $# -gt 0 ]; do # shift # GDB=xxgdb # ;; - "-shared") - shift - cargs="$cargs -shared" - TYPE=.shared - ;; "-purify") shift cargs="$cargs -purify" @@ -222,7 +228,9 @@ PATH=$BINDIR:$ROOTDIR/bin:$PATH EXEC=$BINDIR/erlexec PROGNAME="$PROGNAME $cargs" -EMU=$EMU$TYPE$EMU_TYPE +EMU="$EMU$TYPE" +EMU_NAME=`$EXEC -emu_name_exit $eeargs` + if [ $run_valgrind != yes ]; then xargs="$xargs -pz $PRELOADED --" fi @@ -248,9 +256,9 @@ if [ "x$GDB" = "x" ]; then valgrind_log= else if [ $valmajor -gt 2 -a $valminor -gt 4 ]; then - valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log.$$" + valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log.$$" else - valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log" + valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log" fi fi if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then @@ -263,9 +271,9 @@ if [ "x$GDB" = "x" ]; then early_beam_args=`echo $beam_args | sed "s|^\(.*-progname\).*$|\1|g"` late_beam_args=`echo $beam_args | sed "s|^$pre_beam_args.*\(-- -home.*\)$|\1|g"` - exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED + exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED else - exec $EXEC $xargs ${1+"$@"} + exec $EXEC $eeargs $xargs ${1+"$@"} fi else if [ "x$EMACS" = "x" ]; then @@ -300,5 +308,5 @@ else (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \ (comint-send-input)" # Fire up gdb in emacs... - exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU\") $gdbcmd)" + exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU_NAME\") $gdbcmd)" fi diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh index 328811a0d7..bc4260ecba 100755 --- a/erts/etc/win32/nsis/find_redist.sh +++ b/erts/etc/win32/nsis/find_redist.sh @@ -139,8 +139,7 @@ fi #echo $BPATH_LIST for BP in $BPATH_LIST; do - #echo "BP=$BP" - for verdir in "sdk v2.0" "sdk v3.5" "v6.0A" "v7.0A" "v7.1"; do + for verdir in "sdk v2.0" "sdk v3.5" "v6.0A" "v7.0" "v7.0A" "v7.1"; do BPATH=$BP fail=false allow_fail=false @@ -171,6 +170,18 @@ for BP in $BPATH_LIST; do fi done +# shortcut for locating vcredist_x86.exe is to put it into $ERL_TOP +if [ -f $ERL_TOP/vcredist_x86.exe ]; then + echo $ERL_TOP/vcredist_x86.exe + exit 0 +fi + +# or $ERL_TOP/.. to share across multiple builds +if [ -f $ERL_TOP/../vcredist_x86.exe ]; then + echo $ERL_TOP/../vcredist_x86.exe + exit 0 +fi + echo "Failed to locate vcredist_x86.exe because directory structure was unexpected" >&2 exit 3 diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index 45818079ea..400cc7dafd 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -40,7 +40,7 @@ extern int erts_printf_ulong(fmtfn_t, void*, char, int, int, unsigned long); extern int erts_printf_slong(fmtfn_t, void*, char, int, int, signed long); extern int erts_printf_double(fmtfn_t, void *, char, int, int, double); -extern int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long); +extern int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*); #endif diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index d56693dbf8..2fc82c99a8 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -146,13 +146,13 @@ ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { -#ifdef AO_HAVE_fetch_and_add - return ((ETHR_AINT_T__) AO_fetch_and_add(&var->counter, (AO_t) incr)) + incr; +#ifdef AO_HAVE_fetch_and_add_full + return ((ETHR_AINT_T__) AO_fetch_and_add_full(&var->counter, (AO_t) incr)) + incr; #else while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp + (AO_t) incr; - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) new; } #endif @@ -167,8 +167,8 @@ ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { -#ifdef AO_HAVE_fetch_and_add1 - return ((ETHR_AINT_T__) AO_fetch_and_add1(&var->counter)) + 1; +#ifdef AO_HAVE_fetch_and_add1_full + return ((ETHR_AINT_T__) AO_fetch_and_add1_full(&var->counter)) + 1; #else return ETHR_NATMC_FUNC__(add_return)(var, 1); #endif @@ -183,8 +183,8 @@ ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { -#ifdef AO_HAVE_fetch_and_sub1 - return ((ETHR_AINT_T__) AO_fetch_and_sub1(&var->counter)) - 1; +#ifdef AO_HAVE_fetch_and_sub1_full + return ((ETHR_AINT_T__) AO_fetch_and_sub1_full(&var->counter)) - 1; #else return ETHR_NATMC_FUNC__(add_return)(var, -1); #endif @@ -202,7 +202,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp & ((AO_t) mask); - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) exp; } } @@ -213,7 +213,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp | ((AO_t) mask); - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) exp; } } @@ -225,7 +225,7 @@ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, { ETHR_AINT_T__ act; do { - if (AO_compare_and_swap(&var->counter, (AO_t) exp, (AO_t) new)) + if (AO_compare_and_swap_full(&var->counter, (AO_t) exp, (AO_t) new)) return exp; act = (ETHR_AINT_T__) AO_load(&var->counter); } while (act == exp); @@ -237,7 +237,7 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new) { while (1) { AO_t exp = AO_load(&var->counter); - if (AO_compare_and_swap(&var->counter, exp, (AO_t) new)) + if (AO_compare_and_swap_full(&var->counter, exp, (AO_t) new)) return (ETHR_AINT_T__) exp; } } @@ -265,7 +265,6 @@ ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) return ((ETHR_AINT_T__) AO_fetch_and_add1_acquire(&var->counter)) + 1; #else ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(add_return)(var, 1); - ETHR_MEMORY_BARRIER; return res; #endif } @@ -287,7 +286,6 @@ ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) #ifdef AO_HAVE_fetch_and_sub1_release return ((ETHR_AINT_T__) AO_fetch_and_sub1_release(&var->counter)) - 1; #else - ETHR_MEMORY_BARRIER; return ETHR_NATMC_FUNC__(dec_return)(var); #endif } @@ -314,7 +312,6 @@ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, return act; #else ETHR_AINT_T__ act = ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); - ETHR_MEMORY_BARRIER; return act; #endif } @@ -333,7 +330,6 @@ ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, } while (act == exp); return act; #else - ETHR_MEMORY_BARRIER; return ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); #endif } diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h index 00380dbf07..16182f8b01 100644 --- a/erts/include/internal/sparc32/atomic.h +++ b/erts/include/internal/sparc32/atomic.h @@ -95,7 +95,7 @@ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old+incr; @@ -105,7 +105,7 @@ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old+incr; } @@ -144,7 +144,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old & mask; @@ -154,7 +154,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } @@ -163,7 +163,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old | mask; @@ -173,7 +173,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } @@ -182,7 +182,7 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) { ETHR_AINT_T__ old, new; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad" : : : "memory"); do { old = var->counter; new = val; @@ -192,20 +192,20 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); } while (__builtin_expect(old != new, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); __asm__ __volatile__( ETHR_CAS__ " [%2], %1, %0" : "=&r"(new) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return new; } @@ -213,13 +213,11 @@ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ * Atomic ops with at least specified barriers. */ -/* TODO: relax acquire barriers */ - static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(read)(var); - __asm__ __volatile__("membar #LoadLoad|#LoadStore|#StoreLoad|#StoreStore" : : : "memory"); + __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } @@ -234,21 +232,18 @@ static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(inc_return)(var); - __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } static ETHR_INLINE void ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); ETHR_NATMC_FUNC__(dec)(var); } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); return ETHR_NATMC_FUNC__(dec_return)(var); } @@ -256,14 +251,12 @@ static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); - __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); } diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h index 48e4c0c6c8..0c7b597a6b 100644 --- a/erts/include/internal/tile/atomic.h +++ b/erts/include/internal/tile/atomic.h @@ -65,25 +65,35 @@ ethr_native_atomic32_read(ethr_native_atomic32_t *var) static ETHR_INLINE void ethr_native_atomic32_add(ethr_native_atomic32_t *var, ethr_sint32_t incr) { + ETHR_MEMORY_BARRIER; atomic_add(&var->counter, incr); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE void ethr_native_atomic32_inc(ethr_native_atomic32_t *var) { + ETHR_MEMORY_BARRIER; atomic_increment(&var->counter); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE void ethr_native_atomic32_dec(ethr_native_atomic32_t *var) { + ETHR_MEMORY_BARRIER; atomic_decrement(&var->counter); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_add_return(ethr_native_atomic32_t *var, ethr_sint32_t incr) { - return atomic_exchange_and_add(&var->counter, incr) + incr; + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_exchange_and_add(&var->counter, incr) + incr; + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t @@ -101,18 +111,27 @@ ethr_native_atomic32_dec_return(ethr_native_atomic32_t *var) static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_and_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - return atomic_and_val(&var->counter, mask); + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_and_val(&var->counter, mask); + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_or_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - return atomic_or_val(&var->counter, mask); + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_or_val(&var->counter, mask); + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_xchg(ethr_native_atomic32_t *var, ethr_sint32_t val) { + ETHR_MEMORY_BARRIER; return atomic_exchange_acq(&var->counter, val); } @@ -121,6 +140,7 @@ ethr_native_atomic32_cmpxchg(ethr_native_atomic32_t *var, ethr_sint32_t new, ethr_sint32_t expected) { + ETHR_MEMORY_BARRIER; return atomic_compare_and_exchange_val_acq(&var->counter, new, expected); } @@ -139,9 +159,7 @@ ethr_native_atomic32_read_acqb(ethr_native_atomic32_t *var) static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_inc_return_acqb(ethr_native_atomic32_t *var) { - ethr_sint32_t res = ethr_native_atomic32_inc_return(var); - ETHR_MEMORY_BARRIER; - return res; + return ethr_native_atomic32_inc_return(var); } static ETHR_INLINE void @@ -154,14 +172,12 @@ ethr_native_atomic32_set_relb(ethr_native_atomic32_t *var, ethr_sint32_t val) static ETHR_INLINE void ethr_native_atomic32_dec_relb(ethr_native_atomic32_t *var) { - ETHR_MEMORY_BARRIER; ethr_native_atomic32_dec(var); } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_dec_return_relb(ethr_native_atomic32_t *var) { - ETHR_MEMORY_BARRIER; return ethr_native_atomic32_dec_return(var); } @@ -178,7 +194,6 @@ ethr_native_atomic32_cmpxchg_relb(ethr_native_atomic32_t *var, ethr_sint32_t new, ethr_sint32_t exp) { - ETHR_MEMORY_BARRIER; return ethr_native_atomic32_cmpxchg(var, new, exp); } diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 968d563325..fba3fd723c 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -25,7 +25,7 @@ * width: [0-9]+ | '*' * precision: [0-9]+ | '*' * length: hh | h | l | ll | L | j | t | b<sz> - * conversion: d,i | o,u,x,X | e,E | f,F | g,G | a,A | c | s | T | + * conversion: d,i | o,u,x,X | e,E | f,F | g,G | a,A | c | s | T | R | * p | n | % * sz: 8 | 16 | 32 | 64 | p | e */ @@ -101,7 +101,7 @@ #endif #define FMTC_d 0x0000 -#define FMTC_i 0x0001 +#define FMTC_R 0x0001 #define FMTC_o 0x0002 #define FMTC_u 0x0003 #define FMTC_x 0x0004 @@ -165,7 +165,7 @@ static char heX[] = "0123456789ABCDEF"; #define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0)) #define USIGN(X) ((X) == 0 ? 0 : 1) -int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long) = NULL; +int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*) = NULL; static int noop_fn(void *vfp, char* buf, size_t len) @@ -183,8 +183,8 @@ static int fmt_fld(fmtfn_t fn,void* arg, int len; /* format the prefix */ - if ((sign || (fmt & (FMTF_sgn|FMTF_blk))) && - (((fmt & FMTC_MASK) == FMTC_d) || ((fmt & FMTC_MASK) == FMTC_i))) { + if ((sign || (fmt & (FMTF_sgn|FMTF_blk))) + && (fmt & FMTC_MASK) == FMTC_d) { if (sign < 0) *pp++ = '-'; else if ((fmt & FMTF_sgn)) @@ -245,7 +245,6 @@ static int fmt_long(fmtfn_t fn,void* arg,int sign,unsigned long uval, switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: case FMTC_u: break; case FMTC_o: @@ -298,7 +297,6 @@ static int fmt_long_long(fmtfn_t fn,void* arg,int sign, switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: case FMTC_u: break; case FMTC_o: @@ -622,7 +620,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) /* specifier */ switch(*ptr) { case 'd': ptr++; fmt |= FMTC_d; break; - case 'i': ptr++; fmt |= FMTC_i; break; + case 'i': ptr++; fmt |= FMTC_d; break; case 'o': ptr++; fmt |= FMTC_o; break; case 'u': ptr++; fmt |= FMTC_u; break; case 'x': ptr++; fmt |= FMTC_x; break; @@ -637,6 +635,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) case 'p': ptr++; fmt |= FMTC_p; break; case 'n': ptr++; fmt |= FMTC_n; break; case 'T': ptr++; fmt |= FMTC_T; break; + case 'R': ptr++; fmt |= FMTC_R; break; case '%': FMT(fn,arg,ptr,1,count); ptr++; @@ -650,7 +649,6 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: switch(fmt & FMTL_MASK) { case FMTL_hh: { signed char tval = (signed char) va_arg(ap,int); @@ -814,9 +812,12 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) default: *va_arg(ap,int*) = count; break; } break; - case FMTC_T: { + case FMTC_T: /* Eterm */ + case FMTC_R: { /* Eterm, Eterm* base (base ignored if !HALFWORD_HEAP) */ long prec; unsigned long eterm; + unsigned long* eterm_base; + if (!erts_printf_eterm_func) return -EINVAL; if (precision < 0) @@ -826,14 +827,16 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) else prec = (long) precision; eterm = va_arg(ap, unsigned long); + eterm_base = ((fmt & FMTC_MASK) == FMTC_R) ? + va_arg(ap, unsigned long*) : NULL; if (width > 0 && !(fmt & FMTF_adj)) { - res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec); + res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec, eterm_base); if (res < 0) return res; if (width > res) BLANKS(fn, arg, width - res, count); } - res = (*erts_printf_eterm_func)(fn, arg, eterm, prec); + res = (*erts_printf_eterm_func)(fn, arg, eterm, prec, eterm_base); if (res < 0) return res; count += res; @@ -924,7 +927,7 @@ erts_printf_slong(fmtfn_t fn, void *arg, char conv, int pad, int width, unsigned long ul_val; switch (conv) { case 'd': fmt |= FMTC_d; break; - case 'i': fmt |= FMTC_i; break; + case 'i': fmt |= FMTC_d; break; case 'o': fmt |= FMTC_o; break; case 'x': fmt |= FMTC_x; break; case 'X': fmt |= FMTC_X; break; diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index 77769afcd4..ccff9892c6 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/asn1/test/test_inline.erl b/lib/asn1/test/test_inline.erl index dfa3c134ae..b7ec0d8921 100644 --- a/lib/asn1/test/test_inline.erl +++ b/lib/asn1/test/test_inline.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -173,8 +173,8 @@ mi_encdec(N,Val) -> m_encdec(0,_) -> ok; m_encdec(N,Val) -> - {ok,B}='Mod1':encode('L',Val), - {ok,_R}='Mod1':decode('L',B), + {ok,B}='Mod':encode('L',Val), + {ok,_R}='Mod':decode('L',B), m_encdec(N-1,Val). diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index fef1222fcb..15c7e2a9f2 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index f2af932aef..830c89ae84 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -68,7 +68,7 @@ to be an error if the module name in the source code is not the same as the basename of the output file.</p> - <p>Here follows first all elements of <c>Options</c> that in + <p><marker id="type-option"/>Here follows first all elements of <c>Options</c> that in some way control the behavior of the compiler.</p> <taglist> <tag><c>basic_validation</c></tag> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 25a6db4ce0..5757d0c1cb 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml index 3aa169a135..93e447848a 100644 --- a/lib/debugger/doc/src/notes.xml +++ b/lib/debugger/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index f132a50e0d..0dadf647c3 100755 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2010</year> + <year>2006</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml index c18a126264..630271b115 100644 --- a/lib/edoc/doc/src/notes.xml +++ b/lib/edoc/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2007</year><year>2010</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript index 840fed6dd5..982afece7f 100644 --- a/lib/erl_docgen/priv/bin/specs_gen.escript +++ b/lib/erl_docgen/priv/bin/specs_gen.escript @@ -2,7 +2,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ %%% "-I<dir>" Directory to be searched when including a file. %%% "-module Module" %%% Module name to use when there is no File argument. -%%% A temporary file will be created. +%%% A empty specifications file will be created. %%% Exactly one of -module Module and File must be given. %%% %%% The name of the generated file is "specs_<module>.xml". Its exact @@ -67,34 +67,73 @@ usage() -> halt(1). call_edoc(FileSpec, InclFs, Dir) -> - Incl = [{includes, InclFs}], - Pre = [{preprocess, true}], - Choice = [{dialyzer_specs, all}], - DirOpt = [{dir, Dir}], - Pretty = [{pretty_print, erl_pp}], - Layout = [{layout, otp_specs}, - {file_suffix, ".specs"}, - {stylesheet, ""}], - Warn = [{report_missing_type, false}, - {report_type_mismatch, false}], - OptionList = (DirOpt ++ Choice ++ Pre ++ Warn ++ Pretty ++ Layout ++ Incl), - {File, TmpFile} = case FileSpec of - {file, File0} -> - {File0, false}; - {module, Module} -> - {create_tmp_file(Dir, Module), true} - end, - try edoc:files([File], OptionList) of - ok -> - clean_up(Dir, File, TmpFile), - rename(Dir, File) + ReadOpts = [{includes, InclFs}, {preprocess, true}], + ExtractOpts = [{report_missing_type, false}], + LayoutOpts = [{pretty_printer, erl_pp}, {layout, otp_specs}], + File = case FileSpec of + {file, File0} -> File0; + {module, Module0} -> Module0 + end, + try + Fs = case FileSpec of + {file, _} -> + Fs0 = read_file(File, ReadOpts), + clauses(Fs0); + {module, Module} -> + [{attribute,0,module,list_to_atom(Module)}] + end, + Doc = extract(File, Fs, ExtractOpts), + Text = edoc:layout(Doc, LayoutOpts), + ok = write_text(Text, File, Dir), + rename(Dir, File) catch _:_ -> io:format("EDoc could not process file '~s'\n", [File]), - clean_up(Dir, File, TmpFile), + clean_up(Dir), halt(3) end. +read_file(File, Opts) -> + edoc:read_source(File, Opts). + +extract(File, Forms, Opts) -> + Env = edoc_lib:get_doc_env([], [], [], _Opts=[]), + {_Module, Doc} = edoc_extract:source(Forms, File, Env, Opts), + Doc. + +clauses(Fs) -> + clauses(Fs, no). + +clauses([], no) -> + []; +clauses([F | Fs], Spec) -> + case F of + {attribute,_,spec,_} -> + clauses(Fs, F); + {function,_,_N,_A,_Cls} when Spec =/= no-> + {attribute,_,spec,{Name,FunTypes}} = Spec, + %% [throw({no,Name,{_N,_A}}) || Name =/= {_N,_A}], + %% EDoc doesn't care if a function appears more than once; + %% this is how overloaded specs are handled: + (lists:append([[setelement(4, Spec, {Name,[T]}),F] || + T <- FunTypes]) + ++ clauses(Fs, no)); + _ -> + [F | clauses(Fs, Spec)] + end. + +write_text(Text, File, Dir) -> + Base = filename:basename(File, ".erl"), + OutFile = filename:join(Dir, Base) ++ ".specs", + case file:write_file(OutFile, Text) of + ok -> + ok; + {error, R} -> + R1 = file:format_error(R), + io:format("could not write file '~s': ~s\n", [File, R1]), + halt(2) + end. + rename(Dir, F) -> Mod = filename:basename(F, ".erl"), Old = filename:join(Dir, Mod ++ ".specs"), @@ -108,22 +147,10 @@ rename(Dir, F) -> halt(2) end. -clean_up(Dir, File, TmpFile) -> - [file:delete(File) || TmpFile], +clean_up(Dir) -> _ = [file:delete(filename:join(Dir, F)) || F <- ["packages-frame.html", "overview-summary.html", "modules-frame.html", "index.html", "erlang.png", "edoc-info"]], ok. - -create_tmp_file(Dir, Module) -> - TmpFile = filename:join(Dir, Module++".erl"), - case file:write_file(TmpFile, "-module(" ++ Module ++ ").\n") of - ok -> - TmpFile; - {error, R} -> - R1 = file:format_error(R), - io:format("could not write file '~s': ~s\n", [TmpFile, R1]), - halt(2) - end. diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index c6375ea621..982572aeef 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -22,12 +22,15 @@ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:exsl="http://exslt.org/common" + extension-element-prefixes="exsl" xmlns:fn="http://www.w3.org/2005/02/xpath-functions"> <xsl:include href="db_html_params.xsl"/> <!-- Start of Dialyzer type/spec tags. - See also the template matching "name" and the template "menu.funcs" + See also the templates matching "name" and "seealso" as well as + the template "menu.funcs" --> <xsl:param name="specs_file" select="''"/> @@ -38,49 +41,58 @@ <xsl:key name="mod2app" match="module" use="@name"/> <xsl:template name="err"> + <xsl:param name="f"/> <xsl:param name="m"/> <xsl:param name="n"/> <xsl:param name="a"/> <xsl:param name="s"/> <xsl:message terminate="yes"> - Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> - <xsl:value-of - select="$n"/>/<xsl:value-of - select="$a"/>: <xsl:value-of select="$s"/> + Error <xsl:if test="$f != ''">in <xsl:value-of select ="$f"/>:</xsl:if> + <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> + <xsl:value-of select="$n"/> + <xsl:if test="$a != ''">/<xsl:value-of + select ="$a"/></xsl:if>: <xsl:value-of select="$s"/> </xsl:message> </xsl:template> - <xsl:template name="spec_name"> + <xsl:template name="find_spec"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mod" select="@mod"/> <xsl:variable name="name" select="@name"/> <xsl:variable name="arity" select="@arity"/> - <xsl:variable name="clause" select="@clause"/> + <xsl:variable name="clause_i" select="@clause_i"/> <xsl:variable name="spec0" select= "$i/specs/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> - <xsl:variable name="spec" select="$spec0[string-length($clause) = 0 - or position() = $clause]"/> - <xsl:if test="count($spec) = 0"> + <xsl:variable name="spec" select="$spec0[string-length($clause_i) = 0 + or position() = $clause_i]"/> + + <xsl:if test="count($spec) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($spec) > 1">ambiguous spec</xsl:when> + <xsl:when test="count($spec) = 0">unknown spec</xsl:when> + </xsl:choose> + </xsl:variable> <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> <xsl:with-param name="m" select="$mod"/> <xsl:with-param name="n" select="$name"/> <xsl:with-param name="a" select="$arity"/> - <xsl:with-param name="s">unknown spec</xsl:with-param> + <xsl:with-param name="s" select="$why"/> </xsl:call-template> </xsl:if> + <xsl:copy-of select="$spec"/> + </xsl:template> - <xsl:variable name="arity_clause"> - <xsl:choose> - <xsl:when test="string-length(@clause) > 0"> - <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@arity"/> - </xsl:otherwise> - </xsl:choose> + <xsl:template name="spec_name"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> <xsl:choose> <xsl:when test="ancestor::cref"> @@ -89,72 +101,189 @@ </xsl:message> </xsl:when> <xsl:when test="ancestor::erlref"> - <a name="{$name}-{$arity_clause}"></a> - <xsl:choose> - <xsl:when test="string(@with_guards) = 'no'"> - <xsl:apply-templates select="$spec/contract/clause/head"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template name="contract"> - <xsl:with-param name="contract" select="$spec/contract"/> - </xsl:call-template> - </xsl:otherwise> + <xsl:choose> + <xsl:when test="preceding-sibling::name[position() = 1 + and @name = $name and @arity = $arity]"> + <!-- Avoid duplicated anchors.--> + </xsl:when> + <xsl:otherwise> + <a name="{$name}-{$arity}"></a> + </xsl:otherwise> </xsl:choose> + + <xsl:variable name="global_types" select="ancestor::erlref/datatypes"/> + <xsl:variable name="local_types" + select="../type[string-length(@name) > 0]"/> + <xsl:apply-templates select="$spec/contract/clause/head"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> </xsl:when> </xsl:choose> </xsl:template> - <xsl:template name="contract"> - <xsl:param name="contract"/> - <xsl:call-template name="clause"> - <xsl:with-param name="clause" select="$contract/clause"/> - </xsl:call-template> - </xsl:template> - - <xsl:template name="clause"> - <xsl:param name="clause"/> - <xsl:variable name="type_desc" select="../type_desc"/> - <xsl:for-each select="$clause"> - <xsl:apply-templates select="head"/> - <xsl:if test="count(guard) > 0"> - <xsl:call-template name="guard"> - <xsl:with-param name="guard" select="guard"/> - <xsl:with-param name="type_desc" select="$type_desc"/> - </xsl:call-template> - </xsl:if> - </xsl:for-each> - </xsl:template> - <xsl:template match="head"> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> <span class="bold_code"> - <xsl:apply-templates/> + <xsl:apply-templates mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> </span> <br/> </xsl:template> - <xsl:template name="guard"> - <xsl:param name="guard"/> + <!-- The *last* <name name="..." arity=".."/> --> + <xsl:template match="name" mode="types"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + + <xsl:variable name="global_types" select="ancestor::erlref/datatypes"/> + <xsl:variable name="type_desc" select="../type_desc"/> + <!-- $type is data types to be presented as guards ("local types") --> + <xsl:variable name="type" + select="../type[string-length(@name) > 0 + or string-length(@variable) > 0]"/> + <xsl:variable name="type_variables" + select ="$type[string-length(@variable) > 0]"/> + <xsl:variable name="local_types" + select ="$type[string-length(@name) > 0]"/> + <xsl:variable name="output_subtypes" select="count($type_variables) = 0"/> + + <!-- It is assumed there is no support for overloaded specs + (there is no spec with more than one clause) --> + <xsl:if test="count($clause/guard) > 0 or count($type) > 0"> + <div class="REFBODY"><p>Types:</p> + + <xsl:choose> + <xsl:when test="$output_subtypes"> + <xsl:call-template name="subtype"> + <xsl:with-param name="subtype" select="$clause/guard/subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="type_variables"> + <xsl:with-param name="type_variables" select="$type_variables"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + <xsl:with-param name="fname" select="$name"/> + <xsl:with-param name="arity" select="$arity"/> + </xsl:call-template> + + </xsl:otherwise> + </xsl:choose> + + <xsl:call-template name="local_type"> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:call-template> + </div> + + </xsl:if> + </xsl:template> + + <!-- Handle <type variable="..." name_i="..."/> --> + <xsl:template name="type_variables"> + <xsl:param name="type_variables"/> <xsl:param name="type_desc"/> - <div class="REFBODY"><p>Types:</p> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + <xsl:param name="fname"/> + <xsl:param name="arity"/> + + <xsl:variable name="names" select="../name[string-length(@arity) > 0]"/> + <xsl:for-each select="$type_variables"> + <xsl:variable name="name_i"> + <xsl:choose> + <xsl:when test="string-length(@name_i) > 0"> + <xsl:value-of select="@name_i"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="count($names)"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable name="spec0"> + <xsl:for-each select="$names[position() = $name_i]"> + <xsl:call-template name="find_spec"/> + </xsl:for-each> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + <xsl:variable name="variable" select="@variable"/> + <xsl:variable name="subtype" + select="$clause/guard/subtype[typename = $variable]"/> + + <xsl:if test="count($subtype) = 0"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="ancestor::erlref/module"/> + <xsl:with-param name="n" select="$fname"/> + <xsl:with-param name="a" select="$arity"/> + <xsl:with-param name="s">unknown type variable <xsl:value-of select="$variable"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + <xsl:call-template name="subtype"> - <xsl:with-param name="subtype" select="$guard/subtype"/> - <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="subtype" select="$subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> </xsl:call-template> - </div> + </xsl:for-each> </xsl:template> <xsl:template name="subtype"> <xsl:param name="subtype"/> <xsl:param name="type_desc"/> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + <xsl:for-each select="$subtype"> <xsl:variable name="tname" select="typename"/> - <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/> <div class="REFTYPES"> <span class="bold_code"> - <xsl:apply-templates select="string"/> + <xsl:apply-templates select="string" mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> </span> </div> - <xsl:apply-templates select="$type_desc[@name = $tname]"/> + <xsl:apply-templates select="$type_desc[@variable = $tname]"/> + </xsl:for-each> + </xsl:template> + + <xsl:template name="local_type"> + <xsl:param name="type_desc"/> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + + <xsl:for-each select="$local_types"> + <div class="REFTYPES"> + <xsl:call-template name="type_name"> + <xsl:with-param name="mode" select="'local_type'"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:call-template> + </div> + <xsl:variable name="tname" select="@name"/> + <xsl:variable name="tnvars" select="@n_vars"/> + <xsl:apply-templates select= + "$type_desc[@name = $tname + and (@n_vars = $tnvars + or string-length(@n_vars) = 0 and + string-length($tnvars) = 0)]"/> </xsl:for-each> </xsl:template> @@ -193,130 +322,175 @@ <xsl:apply-templates select="desc"/> </xsl:template> - <xsl:template match="typehead"> - <span class="bold_code"> - <xsl:apply-templates/> - </span><br/> - </xsl:template> - - <!-- local_defs --> - <xsl:template match="local_defs"> - <div class="REFBODY"> - <xsl:apply-templates> - </xsl:apply-templates> - </div> - </xsl:template> - - <xsl:template match="local_def"> - <div class="REFTYPES"> - <span class="bold_code"> - <xsl:apply-templates/> - </span> - </div> - </xsl:template> + <!-- The "mode" attribute of apply has been used to separate the case + when datatypes are copied into specifications' subtypes. + A local type has no anchor. There are no links to local types + from local types or guards/head of the same specification. + --> <xsl:template name="type_name"> + <xsl:param name="mode"/> <!-- '' if <datatype> --> + <xsl:param name="local_types" select="/.."/> + <xsl:param name="global_types" select="/.."/> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mod" select="@mod"/> <xsl:variable name="name" select="@name"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length(@n_vars) > 0"> - <xsl:value-of select="@n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> + <xsl:variable name="n_vars" select="@n_vars"/> <xsl:choose> <xsl:when test="string-length($name) > 0"> <xsl:variable name="type" select= "$i/specs/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars + [name=$name + and (string-length($n_vars) = 0 or n_vars = $n_vars) and (string-length($mod) = 0 or module = $mod)]"/> <xsl:if test="count($type) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($type) > 1">ambiguous type</xsl:when> + <xsl:when test="count($type) = 0">unknown type</xsl:when> + </xsl:choose> + </xsl:variable> <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> <xsl:with-param name="m" select="$mod"/> <xsl:with-param name="n" select="$name"/> <xsl:with-param name="a" select="$n_vars"/> - <xsl:with-param name="s">unknown type</xsl:with-param> + <xsl:with-param name="s" select="$why"/> </xsl:call-template> </xsl:if> - <xsl:apply-templates select="$type/typedecl"/> + <xsl:choose> + <xsl:when test="$mode = ''"> + <xsl:apply-templates select="$type/typedecl"/> + </xsl:when> + <xsl:when test="$mode = 'local_type'"> + <xsl:apply-templates select="$type/typedecl" mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> + </xsl:when> + </xsl:choose> </xsl:when> - <xsl:otherwise> + <xsl:otherwise> <!-- <datatype> with <name> --> <span class="bold_code"> - <xsl:value-of select="."/> + <xsl:apply-templates/> </span> </xsl:otherwise> </xsl:choose> </xsl:template> + <xsl:template match="typehead"> + <span class="bold_code"> + <xsl:apply-templates/> + </span><br/> + </xsl:template> + + <xsl:template match="typehead" mode="local_type"> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + <span class="bold_code"> + <xsl:apply-templates mode="local_type"> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="global_types" select="$global_types"/> + </xsl:apply-templates> + </span><br/> + </xsl:template> + + <!-- Not used right now --> + <!-- local_defs --> + <xsl:template match="local_defs"> + <div class="REFBODY"> + <xsl:apply-templates> + </xsl:apply-templates> + </div> + </xsl:template> + + <!-- Not used right now --> + <xsl:template match="local_def"> + <div class="REFTYPES"> + <span class="bold_code"> + <xsl:apply-templates/> + </span> + </div> + </xsl:template> + <!-- Used both in <datatype> and in <func>! --> <xsl:template match="anno"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="anno" select="normalize-space(text())"/> <xsl:variable name="namespec" - select="ancestor::desc/preceding-sibling::name"/> + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0"> <xsl:call-template name="err"> - <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>) + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="s">cannot find tag 'name' (anno <xsl:value-of select="$anno"/>) </xsl:with-param> </xsl:call-template> </xsl:if> - <xsl:variable name="mod" select="$namespec/@mod"/> - <xsl:variable name="name" select="$namespec/@name"/> - <xsl:variable name="arity" select="$namespec/@arity"/> - <xsl:variable name="clause" select="$namespec/@clause"/> - <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length($tmp_n_vars) > 0"> - <xsl:value-of select="$tmp_n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> + <!-- Search "local types" as well --> + <xsl:variable name="local_types" + select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0]"/> + <xsl:variable name="has_anno_in_local_type"> + <xsl:for-each select="$local_types"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> + </xsl:variable> + + <xsl:variable name="has_anno"> + <xsl:for-each select="$namespec"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> </xsl:variable> + + <xsl:if test="$has_anno = '' and $has_anno_in_local_type = ''"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="m" select="$namespec/@mod"/> + <xsl:with-param name="n" select="$namespec/@name"/> + <xsl:with-param name="a" select="'-'"/> + <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + <xsl:value-of select="$anno"/> + </xsl:template> + + <xsl:template name="anno_name"> + <xsl:param name="curModule"/> + <xsl:param name="anno"/> + <xsl:variable name="mod" select="@mod"/> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="n_vars" select="@n_vars"/> + <xsl:variable name="clause_i" select="@clause_i"/> + <xsl:variable name="spec0" select= "$i/specs/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> <xsl:variable name="spec_annos" select= - "$spec0[string-length($clause) = 0 - or position() = $clause]/anno[.=$anno]"/> + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> <xsl:variable name="type_annos" select= "$i/specs/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars + [name=$name + and (string-length($n_vars) = 0 or n_vars=$n_vars) and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/> - - <xsl:if test="count($spec_annos) = 0 - and count($type_annos) = 0 - and string-length($specs_file) > 0"> - <xsl:variable name="n"> - <xsl:choose> - <xsl:when test="string-length($arity) = 0"> - <xsl:value-of select="$n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="$arity"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:call-template name="err"> - <xsl:with-param name="m" select="$mod"/> - <xsl:with-param name="n" select="$name"/> - <xsl:with-param name="a" select="$n"/> - <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> - </xsl:with-param> - </xsl:call-template> + <xsl:if test="count($spec_annos) != 0 + or count($type_annos) != 0 + or string-length($specs_file) = 0"> + <xsl:value-of select="true()"/> </xsl:if> - <xsl:value-of select="$anno"/> </xsl:template> <!-- Used for indentation of formatted types and specs --> @@ -324,6 +498,50 @@ <xsl:text> </xsl:text> </xsl:template> + <xsl:template match="nbsp" mode="local_type"> + <xsl:apply-templates select="."/> + </xsl:template> + + <xsl:template match="br" mode="local_type"> + <xsl:apply-templates select="."/> + </xsl:template> + + <xsl:template match="marker" mode="local_type"> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + <!-- Craete no anchor --> + <!-- It would be possible to create a link to the global type + (if there is one), but that would mean even more code... + --> + <xsl:apply-templates/> + </xsl:template> + + <!-- Does not look at @n_vars --> + <xsl:template match="seealso" mode="local_type"> + <xsl:param name="local_types"/> + <xsl:param name="global_types"/> + + <xsl:variable name="filepart"><xsl:value-of select="substring-before(@marker, '#')"/></xsl:variable> + <xsl:variable name="linkpart"><xsl:value-of select="translate(substring-after(@marker, '#'), '/', '-')"/></xsl:variable> + + <xsl:choose> + <xsl:when test="string-length($filepart) > 0"> + <xsl:call-template name="seealso"/> + </xsl:when> + <xsl:when test="count($local_types[concat('type-', @name) = $linkpart]) = 0"> + <xsl:call-template name="seealso"/> + </xsl:when> + <xsl:when test="count($global_types/datatype/name[concat('type-', @name) = $linkpart]) > 0"> + <!-- The type is both local and global; link to the global type --> + <xsl:call-template name="seealso"/> + </xsl:when> + <xsl:otherwise> + <!-- No link to local type --> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + <!-- End of Dialyzer type/spec tags --> <!-- Page layout --> @@ -1178,14 +1396,7 @@ <xsl:choose> <xsl:when test="string-length(@arity) > 0"> <!-- Dialyzer spec --> - <xsl:choose> - <xsl:when test="string-length(@clause) > 0"> - <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@arity"/> - </xsl:otherwise> - </xsl:choose> + <xsl:value-of select="@arity"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="calc-arity"> @@ -1221,11 +1432,19 @@ </xsl:choose> </xsl:variable> - <li title="{$fname}-{$arity}"> - <a href="{$basename}.html#{$fname}-{$arity}"> - <xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/> - </a> - </li> + <xsl:choose> + <xsl:when test="preceding-sibling::name[position() = 1 + and @name = $fname and @arity = $arity]"> + <!-- Skip. Only works for Dialyzer specs. --> + </xsl:when> + <xsl:otherwise> + <li title="{$fname}-{$arity}"> + <a href="{$basename}.html#{$fname}-{$arity}"> + <xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/> + </a> + </li> + </xsl:otherwise> + </xsl:choose> </xsl:when> </xsl:choose> @@ -1470,7 +1689,10 @@ <xsl:template match="func"> <xsl:param name="partnum"/> - <p><xsl:apply-templates select="name"/></p> + <p><xsl:apply-templates select="name"/> + <xsl:apply-templates + select="name[string-length(@arity) > 0 and position()=last()]" + mode="types"/></p> <xsl:apply-templates select="fsummary|type|desc"> <xsl:with-param name="partnum" select="$partnum"/> @@ -1478,7 +1700,6 @@ </xsl:template> - <xsl:template match="name"> <xsl:choose> <!-- @arity is mandatory when referring to a specification --> @@ -1488,6 +1709,11 @@ <xsl:when test="ancestor::datatype"> <xsl:call-template name="type_name"/> </xsl:when> + <xsl:when test="string-length(text()) = 0 and ancestor::erlref"> + <xsl:message terminate="yes"> + Error <xsl:value-of select="@name"/>: arity is mandatory when referring to specifications! + </xsl:message> + </xsl:when> <xsl:otherwise> <xsl:call-template name="name"/> </xsl:otherwise> @@ -1556,12 +1782,17 @@ <xsl:template match="type"> <xsl:param name="partnum"/> - <div class="REFBODY"><p>Types:</p> + <!-- The case where @name != 0 is taken care of in "type_name" --> + <xsl:if test="string-length(@name) = 0 and string-length(@variable) = 0"> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </div> + <div class="REFBODY"><p>Types:</p> + + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </div> + + </xsl:if> </xsl:template> @@ -1612,7 +1843,10 @@ </xsl:template> <xsl:template match="seealso"> + <xsl:call-template name="seealso"/> + </xsl:template> + <xsl:template name="seealso"> <xsl:variable name="filepart"><xsl:value-of select="substring-before(@marker, '#')"/></xsl:variable> <xsl:variable name="linkpart"><xsl:value-of select="translate(substring-after(@marker, '#'), '/', '-')"/></xsl:variable> @@ -1633,16 +1867,16 @@ <xsl:variable name="app" select="$m2a/mod2app/module[@name=$filepart]"/> --> - <xsl:variable name="reftext" select="text()"/> + <xsl:variable name="this" select="."/> <xsl:for-each select="$m2a"> <xsl:variable name="app" select="key('mod2app', $filepart)"/> <xsl:choose> <xsl:when test="string-length($app) > 0"> - <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html');"><xsl:value-of select="$reftext"/></a></span> + <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html#{$linkpart}');"><xsl:value-of select="$this"/></a></span> </xsl:when> <xsl:otherwise> <!-- Unknown application; no link --> - <xsl:value-of select="$reftext"/> + <xsl:value-of select="$this"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 2a8fb9fe3e..25b62f68c5 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2010. All Rights Reserved. + # Copyright Ericsson AB 2009-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -21,13 +21,16 @@ --> <xsl:stylesheet version="1.0" - xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:exsl="http://exslt.org/common" + extension-element-prefixes="exsl"> <xsl:preserve-space elements="code pre"/> <xsl:strip-space elements="*"/> <xsl:output method="text" encoding="UTF-8" indent="no"/> - <!-- Start of Dialyzer type/spec tags. See also the template matching "name" + <!-- Start of Dialyzer type/spec tags. See also the templates + matching "name" and "seealso" --> <!-- Note: specs data for *one* module (as opposed to html and pdf) --> @@ -35,38 +38,58 @@ <xsl:variable name="i" select="document($specs_file)"></xsl:variable> <xsl:template name="err"> + <xsl:param name="f"/> <xsl:param name="m"/> <xsl:param name="n"/> <xsl:param name="a"/> <xsl:param name="s"/> <xsl:message terminate="yes"> - Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> - <xsl:value-of - select="$n"/>/<xsl:value-of - select="$a"/>: <xsl:value-of select="$s"/> + Error <xsl:if test="$f != ''">in <xsl:value-of select ="$f"/>:</xsl:if> + <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> + <xsl:value-of select="$n"/> + <xsl:if test="$a != ''">/<xsl:value-of + select ="$a"/></xsl:if>: <xsl:value-of select="$s"/> </xsl:message> </xsl:template> - <xsl:template name="spec_name"> + <xsl:template name="find_spec"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mod" select="@mod"/> <xsl:variable name="name" select="@name"/> <xsl:variable name="arity" select="@arity"/> - <xsl:variable name="clause" select="@clause"/> + <xsl:variable name="clause_i" select="@clause_i"/> <xsl:variable name="spec0" select= "$i/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> - <xsl:variable name="spec" select="$spec0[string-length($clause) = 0 - or position() = $clause]"/> - <xsl:if test="count($spec) = 0"> + <xsl:variable name="spec" select="$spec0[string-length($clause_i) = 0 + or position() = $clause_i]"/> + + <xsl:if test="count($spec) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($spec) > 1">ambiguous spec</xsl:when> + <xsl:when test="count($spec) = 0">unknown spec</xsl:when> + </xsl:choose> + </xsl:variable> <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> <xsl:with-param name="m" select="$mod"/> <xsl:with-param name="n" select="$name"/> <xsl:with-param name="a" select="$arity"/> - <xsl:with-param name="s">unknown spec</xsl:with-param> + <xsl:with-param name="s" select="$why"/> </xsl:call-template> </xsl:if> + <xsl:copy-of select="$spec"/> + </xsl:template> + + <xsl:template name="spec_name"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> <xsl:choose> <xsl:when test="ancestor::cref"> @@ -75,42 +98,12 @@ </xsl:message> </xsl:when> <xsl:when test="ancestor::erlref"> - <xsl:choose> - <xsl:when test="string(@with_guards) = 'no'"> - <xsl:apply-templates select="$spec/contract/clause/head"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template name="contract"> - <xsl:with-param name="contract" select="$spec/contract"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> + <xsl:apply-templates select="$spec/contract/clause/head"/> <xsl:text> .br</xsl:text> </xsl:when> </xsl:choose> </xsl:template> - <xsl:template name="contract"> - <xsl:param name="contract"/> - <xsl:call-template name="clause"> - <xsl:with-param name="clause" select="$contract/clause"/> - </xsl:call-template> - </xsl:template> - - <xsl:template name="clause"> - <xsl:param name="clause"/> - <xsl:variable name="type_desc" select="../type_desc"/> - <xsl:for-each select="$clause"> - <xsl:apply-templates select="head"/> - <xsl:if test="count(guard) > 0"> - <xsl:call-template name="guard"> - <xsl:with-param name="guard" select="guard"/> - <xsl:with-param name="type_desc" select="$type_desc"/> - </xsl:call-template> - </xsl:if> - </xsl:for-each> - </xsl:template> - <xsl:template match="head"> <xsl:text> .nf </xsl:text> <xsl:text> .B </xsl:text> @@ -119,29 +112,141 @@ <xsl:text> .fi</xsl:text> </xsl:template> - <xsl:template name="guard"> - <xsl:param name="guard"/> + <!-- The *last* <name name="..." arity=".."/> --> + <xsl:template match="name" mode="types"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + + <xsl:variable name="type_desc" select="../type_desc"/> + <!-- $type is data types to be presented as guards ("local types") --> + <xsl:variable name="type" + select="../type[string-length(@name) > 0 + or string-length(@variable) > 0]"/> + <xsl:variable name="type_variables" + select ="$type[string-length(@variable) > 0]"/> + <xsl:variable name="local_types" + select ="$type[string-length(@name) > 0]"/> + <xsl:variable name="output_subtypes" select="count($type_variables) = 0"/> + + <!-- It is assumed there is no support for overloaded specs + (there is no spec with more than one clause) --> + <xsl:if test="count($clause/guard) > 0 or count($type) > 0"> + <xsl:text> .RS</xsl:text> + <xsl:text> .TP</xsl:text> + <xsl:text> Types</xsl:text> + + <xsl:choose> + <xsl:when test="$output_subtypes"> + <xsl:call-template name="subtype"> + <xsl:with-param name="subtype" select="$clause/guard/subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="type_variables"> + <xsl:with-param name="type_variables" select="$type_variables"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="fname" select="$name"/> + <xsl:with-param name="arity" select="$arity"/> + </xsl:call-template> + + </xsl:otherwise> + </xsl:choose> + + <xsl:call-template name="local_type"> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + </xsl:call-template> + <xsl:text> .RE</xsl:text> + + </xsl:if> + </xsl:template> + + <!-- Handle <type variable="..." name_i="..."/> --> + <xsl:template name="type_variables"> + <xsl:param name="type_variables"/> <xsl:param name="type_desc"/> - <xsl:text> .RS</xsl:text> - <xsl:text> .TP</xsl:text> - <xsl:text> Types</xsl:text> - <xsl:call-template name="subtype"> - <xsl:with-param name="subtype" select="$guard/subtype"/> - <xsl:with-param name="type_desc" select="$type_desc"/> - </xsl:call-template> - <xsl:text> .RE</xsl:text> + <xsl:param name="local_types"/> + <xsl:param name="fname"/> + <xsl:param name="arity"/> + + <xsl:variable name="names" select="../name[string-length(@arity) > 0]"/> + <xsl:for-each select="$type_variables"> + <xsl:variable name="name_i"> + <xsl:choose> + <xsl:when test="string-length(@name_i) > 0"> + <xsl:value-of select="@name_i"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="count($names)"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable name="spec0"> + <xsl:for-each select="$names[position() = $name_i]"> + <xsl:call-template name="find_spec"/> + </xsl:for-each> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + <xsl:variable name="variable" select="@variable"/> + <xsl:variable name="subtype" + select="$clause/guard/subtype[typename = $variable]"/> + + <xsl:if test="count($subtype) = 0"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="ancestor::erlref/module"/> + <xsl:with-param name="n" select="$fname"/> + <xsl:with-param name="a" select="$arity"/> + <xsl:with-param name="s">unknown type variable <xsl:value-of select="$variable"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + + <xsl:call-template name="subtype"> + <xsl:with-param name="subtype" select="$subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + </xsl:call-template> + </xsl:for-each> </xsl:template> <xsl:template name="subtype"> <xsl:param name="subtype"/> <xsl:param name="type_desc"/> + <xsl:for-each select="$subtype"> <xsl:variable name="tname" select="typename"/> - <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/> - <xsl:text> </xsl:text> - <xsl:apply-templates select="string"/> - <xsl:text> .br</xsl:text> - <xsl:apply-templates select="$type_desc[@name = $tname]"/> + <xsl:text> </xsl:text> + <xsl:apply-templates select="string"/> + <xsl:text> .br</xsl:text> + <xsl:apply-templates select="$type_desc[@variable = $tname]"/> + </xsl:for-each> + </xsl:template> + + <xsl:template name="local_type"> + <xsl:param name="type_desc"/> + <xsl:param name="local_types"/> + + <xsl:for-each select ="$local_types"> + <xsl:text> </xsl:text> + <xsl:call-template name="type_name"> + <xsl:with-param name="mode" select="'local_type'"/> + </xsl:call-template> + <xsl:text> .br</xsl:text> + <xsl:variable name="tname" select="@name"/> + <xsl:variable name="tnvars" select="@n_vars"/> + <xsl:apply-templates select= + "$type_desc[@name = $tname + and (@n_vars = $tnvars + or string-length(@n_vars) = 0 and + string-length($tnvars) = 0)]"/> </xsl:for-each> </xsl:template> @@ -164,59 +269,46 @@ <xsl:apply-templates/> </xsl:template> - <xsl:template match="typehead"> - <xsl:text> .nf </xsl:text> - <xsl:text> .B </xsl:text> - <xsl:apply-templates/> - <xsl:text> .br</xsl:text> - <xsl:text> .fi</xsl:text> - </xsl:template> - - <xsl:template match="local_defs"> - <xsl:text> .RS</xsl:text> - <xsl:apply-templates/> - <xsl:text> .RE</xsl:text> - </xsl:template> - - <xsl:template match="local_def"> - <xsl:text> </xsl:text> - <xsl:apply-templates/> - <xsl:text> .br</xsl:text> - </xsl:template> - <xsl:template name="type_name"> + <xsl:param name="mode"/> <!-- '' if <datatype> --> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mod" select="@mod"/> <xsl:variable name="name" select="@name"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length(@n_vars) > 0"> - <xsl:value-of select="@n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> + <xsl:variable name="n_vars" select="@n_vars"/> <xsl:choose> <xsl:when test="string-length($name) > 0"> <xsl:variable name="type" select= "$i/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars + [name=$name + and (string-length($n_vars) = 0 or n_vars = $n_vars) and (string-length($mod) = 0 or module = $mod)]"/> <xsl:if test="count($type) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($type) > 1">ambiguous type</xsl:when> + <xsl:when test="count($type) = 0">unknown type</xsl:when> + </xsl:choose> + </xsl:variable> <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> <xsl:with-param name="m" select="$mod"/> <xsl:with-param name="n" select="$name"/> <xsl:with-param name="a" select="$n_vars"/> - <xsl:with-param name="s">unknown type</xsl:with-param> + <xsl:with-param name="s" select="$why"/> </xsl:call-template> </xsl:if> - <xsl:apply-templates select="$type/typedecl"/> + <xsl:choose> + <xsl:when test="$mode = ''"> + <xsl:apply-templates select="$type/typedecl"/> + </xsl:when> + <xsl:when test="$mode = 'local_type'"> + <xsl:apply-templates select="$type/typedecl" mode="local_type"/> + </xsl:when> + </xsl:choose> </xsl:when> - <xsl:otherwise> + <xsl:otherwise> <!-- <datatype> with <name> --> <xsl:text> .nf </xsl:text> <xsl:text> .B </xsl:text> <xsl:apply-templates/> @@ -226,68 +318,109 @@ </xsl:choose> </xsl:template> + <xsl:template match="typehead"> + <xsl:text> .nf </xsl:text> + <xsl:text> .B </xsl:text> + <xsl:apply-templates/> + <xsl:text> .br</xsl:text> + <xsl:text> .fi</xsl:text> + </xsl:template> + + <xsl:template match="typehead" mode="local_type"> + <xsl:text>.nf </xsl:text> + <xsl:apply-templates/> + <xsl:text> .fi</xsl:text> + </xsl:template> + + <!-- Not used right now --> + <xsl:template match="local_defs"> + <xsl:text> .RS</xsl:text> + <xsl:apply-templates/> + <xsl:text> .RE</xsl:text> + </xsl:template> + + <xsl:template match="local_def"> + <xsl:text> </xsl:text> + <xsl:apply-templates/> + <xsl:text> .br</xsl:text> + </xsl:template> + <!-- Used both in <datatype> and in <func>! --> <xsl:template match="anno"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="anno" select="normalize-space(text())"/> <xsl:variable name="namespec" - select="ancestor::desc/preceding-sibling::name"/> + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0"> <xsl:call-template name="err"> - <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>) + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="s">cannot find tag 'name' (anno <xsl:value-of select="$anno"/>) </xsl:with-param> </xsl:call-template> </xsl:if> - <xsl:variable name="mod" select="$namespec/@mod"/> - <xsl:variable name="name" select="$namespec/@name"/> - <xsl:variable name="arity" select="$namespec/@arity"/> - <xsl:variable name="clause" select="$namespec/@clause"/> - <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length($tmp_n_vars) > 0"> - <xsl:value-of select="$tmp_n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> + <!-- Search "local types" as well --> + <xsl:variable name="local_types" + select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0]"/> + <xsl:variable name="has_anno_in_local_type"> + <xsl:for-each select="$local_types"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> + </xsl:variable> + + <xsl:variable name="has_anno"> + <xsl:for-each select="$namespec"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> </xsl:variable> + + <xsl:if test="$has_anno = '' and $has_anno_in_local_type = ''"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="m" select="$namespec/@mod"/> + <xsl:with-param name="n" select="$namespec/@name"/> + <xsl:with-param name="a" select="'-'"/> + <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + <xsl:value-of select="$anno"/> + </xsl:template> + + <xsl:template name="anno_name"> + <xsl:param name="curModule"/> + <xsl:param name="anno"/> + <xsl:variable name="mod" select="@mod"/> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="n_vars" select="@n_vars"/> + <xsl:variable name="clause_i" select="@clause_i"/> + <xsl:variable name="spec0" select= "$i/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> <xsl:variable name="spec_annos" select= - "$spec0[string-length($clause) = 0 - or position() = $clause]/anno[.=$anno]"/> + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> <xsl:variable name="type_annos" select= "$i/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars + [name=$name + and (string-length($n_vars) = 0 or n_vars=$n_vars) and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/> - - <xsl:if test="count($spec_annos) = 0 - and count($type_annos) = 0 - and string-length($specs_file) > 0"> - <xsl:variable name="n"> - <xsl:choose> - <xsl:when test="string-length($arity) = 0"> - <xsl:value-of select="$n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="$arity"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:call-template name="err"> - <xsl:with-param name="m" select="$mod"/> - <xsl:with-param name="n" select="$name"/> - <xsl:with-param name="a" select="$n"/> - <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> - </xsl:with-param> - </xsl:call-template> + <xsl:if test="count($spec_annos) != 0 + or count($type_annos) != 0 + or string-length($specs_file) = 0"> + <xsl:value-of select="true()"/> </xsl:if> - <xsl:value-of select="$anno"/> </xsl:template> <!-- Used for indentation of formatted types and specs --> @@ -542,6 +675,9 @@ <xsl:template match="func"> <xsl:text> .LP</xsl:text> <xsl:apply-templates select="name"/> + <xsl:apply-templates + select="name[string-length(@arity) > 0 and position()=last()]" + mode="types"/> <xsl:apply-templates select="fsummary|type|desc"/> </xsl:template> @@ -554,6 +690,11 @@ <xsl:when test="ancestor::datatype"> <xsl:call-template name="type_name"/> </xsl:when> + <xsl:when test="string-length(text()) = 0 and ancestor::erlref"> + <xsl:message terminate="yes"> + Error <xsl:value-of select="@name"/>: arity is mandatory when referring to specifications! + </xsl:message> + </xsl:when> <xsl:otherwise> <xsl:call-template name="name"/> </xsl:otherwise> @@ -569,11 +710,14 @@ <!-- Type --> <xsl:template match="type"> - <xsl:text> .RS</xsl:text> - <xsl:text> .TP</xsl:text> - <xsl:text> Types</xsl:text> - <xsl:apply-templates/> - <xsl:text> .RE</xsl:text> + <!-- The case where @name != 0 is taken care of in "type_name" --> + <xsl:if test="string-length(@name) = 0 and string-length(@variable) = 0"> + <xsl:text> .RS</xsl:text> + <xsl:text> .TP</xsl:text> + <xsl:text> Types</xsl:text> + <xsl:apply-templates/> + <xsl:text> .RE</xsl:text> + </xsl:if> </xsl:template> @@ -622,7 +766,7 @@ <xsl:text>></xsl:text> </xsl:template> - <!-- Do not noramlize any text within pre and code tags. --> + <!-- Do not normalize any text within pre and code tags. --> <xsl:template match="pre/text()"> <xsl:call-template name="replace-string"> <xsl:with-param name="text" select="." /> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index f500cd3fee..5119e3e36a 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -21,6 +21,8 @@ --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:exsl="http://exslt.org/common" + extension-element-prefixes="exsl" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="xml" indent="yes"/> @@ -28,45 +30,66 @@ <xsl:include href="db_pdf_params.xsl"/> <!-- Start of Dialyzer type/spec tags. - See also the template matching "name" and the template "bookmarks6" + See also the templates matching "name" and "seealso" as well as + the template "bookmarks6" --> <xsl:param name="specs_file" select="''"/> <xsl:variable name="i" select="document($specs_file)"></xsl:variable> <xsl:template name="err"> + <xsl:param name="f"/> <xsl:param name="m"/> <xsl:param name="n"/> <xsl:param name="a"/> <xsl:param name="s"/> <xsl:message terminate="yes"> - Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> - <xsl:value-of - select="$n"/>/<xsl:value-of - select="$a"/>: <xsl:value-of select="$s"/> + Error <xsl:if test="$f != ''">in <xsl:value-of select ="$f"/>:</xsl:if> + <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if> + <xsl:value-of select="$n"/> + <xsl:if test="$a != ''">/<xsl:value-of + select ="$a"/></xsl:if>: <xsl:value-of select="$s"/> </xsl:message> </xsl:template> - <xsl:template name="spec_name"> + <xsl:template name="find_spec"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="mod" select="@mod"/> <xsl:variable name="name" select="@name"/> <xsl:variable name="arity" select="@arity"/> - <xsl:variable name="clause" select="@clause"/> + <xsl:variable name="clause_i" select="@clause_i"/> <xsl:variable name="spec0" select= "$i/specs/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> - <xsl:variable name="spec" select="$spec0[string-length($clause) = 0 - or position() = $clause]"/> - <xsl:if test="count($spec) = 0"> + <xsl:variable name="spec" select="$spec0[string-length($clause_i) = 0 + or position() = $clause_i]"/> + + <xsl:if test="count($spec) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($spec) > 1">ambiguous spec</xsl:when> + <xsl:when test="count($spec) = 0">unknown spec</xsl:when> + </xsl:choose> + </xsl:variable> <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> <xsl:with-param name="m" select="$mod"/> <xsl:with-param name="n" select="$name"/> <xsl:with-param name="a" select="$arity"/> - <xsl:with-param name="s">unknown spec</xsl:with-param> + <xsl:with-param name="s" select="$why"/> </xsl:call-template> </xsl:if> + <xsl:copy-of select="$spec"/> + </xsl:template> + + <xsl:template name="spec_name"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> <xsl:choose> <xsl:when test="ancestor::cref"> @@ -76,78 +99,175 @@ </xsl:when> <xsl:when test="ancestor::erlref"> <fo:block id="{generate-id()}"> - <xsl:choose> - <xsl:when test="string(@with_guards) = 'no'"> - <xsl:apply-templates select="$spec/contract/clause/head"/> - </xsl:when> - <xsl:otherwise> - <xsl:call-template name="contract"> - <xsl:with-param name="contract" select="$spec/contract"/> - </xsl:call-template> - </xsl:otherwise> - </xsl:choose> + <xsl:apply-templates select="$spec/contract/clause/head"/> </fo:block> </xsl:when> </xsl:choose> </xsl:template> - <xsl:template name="contract"> - <xsl:param name="contract"/> - <xsl:call-template name="clause"> - <xsl:with-param name="clause" select="$contract/clause"/> - </xsl:call-template> + <xsl:template match="head"> + <fo:block xsl:use-attribute-sets="function-name"> + <xsl:apply-templates/> + </fo:block> </xsl:template> - <xsl:template name="clause"> - <xsl:param name="clause"/> + <!-- The *last* <name name="..." arity=".."/> --> + <xsl:template match="name" mode="types"> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="spec0"> + <xsl:call-template name="find_spec"/> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + <xsl:variable name="type_desc" select="../type_desc"/> - <xsl:for-each select="$clause"> - <xsl:apply-templates select="head"/> - <xsl:if test="count(guard) > 0"> - <xsl:call-template name="guard"> - <xsl:with-param name="guard" select="guard"/> + <!-- $type is data types to be presented as guards ("local types") --> + <xsl:variable name="type" + select="../type[string-length(@name) > 0 + or string-length(@variable) > 0]"/> + <xsl:variable name="type_variables" + select ="$type[string-length(@variable) > 0]"/> + <xsl:variable name="local_types" + select ="$type[string-length(@name) > 0]"/> + <xsl:variable name="output_subtypes" select="count($type_variables) = 0"/> + + <!-- It is assumed there is no support for overloaded specs + (there is no spec with more than one clause) --> + <xsl:if test="count($clause/guard) > 0 or count($type) > 0"> + <fo:block> + <xsl:text>Types:</xsl:text> + </fo:block> + <fo:list-block xsl:use-attribute-sets="type-listblock"> + <xsl:choose> + <xsl:when test="$output_subtypes"> + <xsl:call-template name="subtype"> + <xsl:with-param name="subtype" select="$clause/guard/subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="type_variables"> + <xsl:with-param name="type_variables" select="$type_variables"/> + <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> + <xsl:with-param name="fname" select="$name"/> + <xsl:with-param name="arity" select="$arity"/> + </xsl:call-template> + + </xsl:otherwise> + </xsl:choose> + + <xsl:call-template name="local_type"> <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="local_types" select="$local_types"/> </xsl:call-template> - </xsl:if> - </xsl:for-each> + </fo:list-block> + </xsl:if> </xsl:template> - <xsl:template match="head"> - <fo:block xsl:use-attribute-sets="function-name"> - <xsl:apply-templates/> - </fo:block> - </xsl:template> + <!-- Handle <type variable="..." name_i="..."/> --> + <xsl:template name="type_variables"> + <xsl:param name="type_variables"/> + <xsl:param name="type_desc"/> + <xsl:param name="local_types"/> + <xsl:param name="fname"/> + <xsl:param name="arity"/> + + <xsl:variable name="names" select="../name[string-length(@arity) > 0]"/> + <xsl:for-each select="$type_variables"> + <xsl:variable name="name_i"> + <xsl:choose> + <xsl:when test="string-length(@name_i) > 0"> + <xsl:value-of select="@name_i"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="count($names)"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:variable name="spec0"> + <xsl:for-each select="$names[position() = $name_i]"> + <xsl:call-template name="find_spec"/> + </xsl:for-each> + </xsl:variable> + <xsl:variable name="spec" select="exsl:node-set($spec0)/spec"/> + <xsl:variable name="clause" select="$spec/contract/clause"/> + <xsl:variable name="variable" select="@variable"/> + <xsl:variable name="subtype" + select="$clause/guard/subtype[typename = $variable]"/> + + <xsl:if test="count($subtype) = 0"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="ancestor::erlref/module"/> + <xsl:with-param name="n" select="$fname"/> + <xsl:with-param name="a" select="$arity"/> + <xsl:with-param name="s">unknown type variable <xsl:value-of select="$variable"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> - <xsl:template name="guard"> - <fo:block> - <xsl:text>Types:</xsl:text> - </fo:block> - <fo:list-block xsl:use-attribute-sets="type-listblock"> <xsl:call-template name="subtype"> - <xsl:with-param name="subtype" select="$guard/subtype"/> - <xsl:with-param name="type_desc" select="$type_desc"/> + <xsl:with-param name="subtype" select="$subtype"/> + <xsl:with-param name="type_desc" select="$type_desc"/> </xsl:call-template> - </fo:list-block> + </xsl:for-each> </xsl:template> + <!-- Substituted + <fo:block xsl:use-attribute-sets="function-name"> + for + <fo:block font-weight="bold"> + to get proper indentation (a monospace font) + --> <xsl:template name="subtype"> <xsl:param name="subtype"/> <xsl:param name="type_desc"/> + <xsl:for-each select="$subtype"> <xsl:variable name="tname" select="typename"/> - <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/> <fo:list-item xsl:use-attribute-sets="type-listitem"> <fo:list-item-label end-indent="label-end()"> <fo:block> </fo:block> </fo:list-item-label> <fo:list-item-body start-indent="body-start()" format="justify"> - <fo:block font-weight="bold"> - <xsl:apply-templates select="string"/> + <fo:block xsl:use-attribute-sets="function-name"> + <xsl:apply-templates select="string"/> </fo:block> </fo:list-item-body> </fo:list-item> - <xsl:apply-templates select="$type_desc[@name = $tname]"/> + <xsl:apply-templates select="$type_desc[@variable = $tname]"/> + </xsl:for-each> + </xsl:template> + + <xsl:template name="local_type"> + <xsl:param name="type_desc"/> + <xsl:param name="local_types"/> + + <xsl:for-each select="$local_types"> + <fo:list-item xsl:use-attribute-sets="type-listitem"> + <fo:list-item-label end-indent="label-end()"> + <fo:block> + </fo:block> + </fo:list-item-label> + <fo:list-item-body start-indent="body-start()" format="justify"> + <!-- <fo:block font-weight="bold"> + (use function-name in "typehead" instead) --> + <xsl:call-template name="type_name"> + <xsl:with-param name="mode" select="'local_type'"/> + </xsl:call-template> + <!-- </fo:block> --> + </fo:list-item-body> + </fo:list-item> + <xsl:variable name="tname" select="@name"/> + <xsl:variable name="tnvars" select="@n_vars"/> + <xsl:apply-templates select= + "$type_desc[@name = $tname + and (@n_vars = $tnvars + or string-length(@n_vars) = 0 and + string-length($tnvars) = 0)]"/> </xsl:for-each> </xsl:template> @@ -182,6 +302,53 @@ <xsl:apply-templates select="desc"/> </xsl:template> + <xsl:template name="type_name"> + <xsl:param name="mode"/> <!-- '' if <datatype> --> + <xsl:variable name="curModule" select="ancestor::erlref/module"/> + <xsl:variable name="mod" select="@mod"/> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="n_vars" select="@n_vars"/> + + <xsl:choose> + <xsl:when test="string-length($name) > 0"> + <xsl:variable name="type" select= + "$i/specs/module[@name=$curModule]/type + [name=$name + and (string-length($n_vars) = 0 or n_vars = $n_vars) + and (string-length($mod) = 0 or module = $mod)]"/> + + <xsl:if test="count($type) != 1"> + <xsl:variable name="why"> + <xsl:choose> + <xsl:when test="count($type) > 1">ambiguous type</xsl:when> + <xsl:when test="count($type) = 0">unknown type</xsl:when> + </xsl:choose> + </xsl:variable> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="m" select="$mod"/> + <xsl:with-param name="n" select="$name"/> + <xsl:with-param name="a" select="$n_vars"/> + <xsl:with-param name="s" select="$why"/> + </xsl:call-template> + </xsl:if> + <xsl:choose> + <xsl:when test="$mode = ''"> + <xsl:apply-templates select="$type/typedecl"/> + </xsl:when> + <xsl:when test="$mode = 'local_type'"> + <xsl:apply-templates select="$type/typedecl" mode="local_type"/> + </xsl:when> + </xsl:choose> + </xsl:when> + <xsl:otherwise> + <fo:inline font-weight="bold" xsl:use-attribute-sets="type-listitem"> + <xsl:value-of select="."/> + </fo:inline> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + <!-- Like <head>... --> <xsl:template match="typehead"> <fo:block xsl:use-attribute-sets="function-name"> @@ -189,6 +356,20 @@ </fo:block> </xsl:template> + <!-- Substituted + <fo:block xsl:use-attribute-sets="function-name"> + for + <fo:block font-weight="bold"> + to get proper indentation (a monospace font) + --> + + <xsl:template match="typehead" mode="local_type"> + <fo:block xsl:use-attribute-sets="function-name"> + <xsl:apply-templates/> + </fo:block> + </xsl:template> + + <!-- Not used right now --> <!-- Like <guard>, except "Types:"... --> <xsl:template match="local_defs"> <fo:list-block xsl:use-attribute-sets="type-listblock"> @@ -211,108 +392,82 @@ </fo:list-item> </xsl:template> - <xsl:template name="type_name"> - <xsl:variable name="curModule" select="ancestor::erlref/module"/> - <xsl:variable name="mod" select="@mod"/> - <xsl:variable name="name" select="@name"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length(@n_vars) > 0"> - <xsl:value-of select="@n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - - <xsl:choose> - <xsl:when test="string-length($name) > 0"> - <xsl:variable name="type" select= - "$i/specs/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars - and (string-length($mod) = 0 or module = $mod)]"/> - - <xsl:if test="count($type) != 1"> - <xsl:call-template name="err"> - <xsl:with-param name="m" select="$mod"/> - <xsl:with-param name="n" select="$name"/> - <xsl:with-param name="a" select="$n_vars"/> - <xsl:with-param name="s">unknown type</xsl:with-param> - </xsl:call-template> - </xsl:if> - <xsl:apply-templates select="$type/typedecl"/> - </xsl:when> - <xsl:otherwise> - <fo:inline font-weight="bold" xsl:use-attribute-sets="type-listitem"> - <xsl:value-of select="."/> - </fo:inline> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - <!-- Used both in <datatype> and in <func>! --> <xsl:template match="anno"> <xsl:variable name="curModule" select="ancestor::erlref/module"/> <xsl:variable name="anno" select="normalize-space(text())"/> <xsl:variable name="namespec" - select="ancestor::desc/preceding-sibling::name"/> + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0"> <xsl:call-template name="err"> - <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>) + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="s">cannot find tag 'name' (anno <xsl:value-of select="$anno"/>) </xsl:with-param> </xsl:call-template> </xsl:if> - <xsl:variable name="mod" select="$namespec/@mod"/> - <xsl:variable name="name" select="$namespec/@name"/> - <xsl:variable name="arity" select="$namespec/@arity"/> - <xsl:variable name="clause" select="$namespec/@clause"/> - <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/> - <xsl:variable name="n_vars"> - <xsl:choose> - <xsl:when test="string-length($tmp_n_vars) > 0"> - <xsl:value-of select="$tmp_n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="0"/> - </xsl:otherwise> - </xsl:choose> + <!-- Search "local types" as well --> + <xsl:variable name="local_types" + select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0]"/> + <xsl:variable name="has_anno_in_local_type"> + <xsl:for-each select="$local_types"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> + </xsl:variable> + + <xsl:variable name="has_anno"> + <xsl:for-each select="$namespec"> + <xsl:call-template name="anno_name"> + <xsl:with-param name="curModule" select="$curModule"/> + <xsl:with-param name="anno" select="$anno"/> + </xsl:call-template> + </xsl:for-each> </xsl:variable> + + <xsl:if test="$has_anno = '' and $has_anno_in_local_type = ''"> + <xsl:call-template name="err"> + <xsl:with-param name="f" select="$curModule"/> + <xsl:with-param name="m" select="$namespec/@mod"/> + <xsl:with-param name="n" select="$namespec/@name"/> + <xsl:with-param name="a" select="'-'"/> + <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> + </xsl:with-param> + </xsl:call-template> + </xsl:if> + <xsl:value-of select="$anno"/> + </xsl:template> + + <xsl:template name="anno_name"> + <xsl:param name="curModule"/> + <xsl:param name="anno"/> + <xsl:variable name="mod" select="@mod"/> + <xsl:variable name="name" select="@name"/> + <xsl:variable name="arity" select="@arity"/> + <xsl:variable name="n_vars" select="@n_vars"/> + <xsl:variable name="clause_i" select="@clause_i"/> + <xsl:variable name="spec0" select= "$i/specs/module[@name=$curModule]/spec [name=$name and arity=$arity and (string-length($mod) = 0 or module = $mod)]"/> <xsl:variable name="spec_annos" select= - "$spec0[string-length($clause) = 0 - or position() = $clause]/anno[.=$anno]"/> + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> <xsl:variable name="type_annos" select= "$i/specs/module[@name=$curModule]/type - [name=$name and n_vars=$n_vars + [name=$name + and (string-length($n_vars) = 0 or n_vars=$n_vars) and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/> - - <xsl:if test="count($spec_annos) = 0 - and count($type_annos) = 0 - and string-length($specs_file) > 0"> - <xsl:variable name="n"> - <xsl:choose> - <xsl:when test="string-length($arity) = 0"> - <xsl:value-of select="$n_vars"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="$arity"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:call-template name="err"> - <xsl:with-param name="m" select="$mod"/> - <xsl:with-param name="n" select="$name"/> - <xsl:with-param name="a" select="$n"/> - <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/> - </xsl:with-param> - </xsl:call-template> + <xsl:if test="count($spec_annos) != 0 + or count($type_annos) != 0 + or string-length($specs_file) = 0"> + <xsl:value-of select="true()"/> </xsl:if> - <xsl:value-of select="$anno"/> </xsl:template> <!-- Used for indentation of formatted types and specs --> @@ -1227,6 +1382,9 @@ <xsl:param name="partnum"/> <xsl:apply-templates select="name"/> + <xsl:apply-templates + select="name[string-length(@arity) > 0 and position()=last()]" + mode="types"/> <xsl:apply-templates select="fsummary|type|desc"> <xsl:with-param name="partnum" select="$partnum"/> @@ -1245,6 +1403,11 @@ <xsl:when test="ancestor::datatype"> <xsl:call-template name="type_name"/> </xsl:when> + <xsl:when test="string-length(text()) = 0 and ancestor::erlref"> + <xsl:message terminate="yes"> + Error <xsl:value-of select="@name"/>: arity is mandatory when referring to specifications! + </xsl:message> + </xsl:when> <xsl:otherwise> <fo:block xsl:use-attribute-sets="function-name"> <xsl:call-template name="name"> @@ -1276,15 +1439,20 @@ <xsl:template match="type"> <xsl:param name="partnum"/> - <fo:block> - <xsl:text>Types:</xsl:text> - </fo:block> + <!-- The case where @name != 0 is taken care of in "type_name" --> + <xsl:if test="string-length(@name) = 0 and string-length(@variable) = 0"> - <fo:list-block xsl:use-attribute-sets="type-listblock"> - <xsl:apply-templates> - <xsl:with-param name="partnum" select="$partnum"/> - </xsl:apply-templates> - </fo:list-block> + <fo:block> + <xsl:text>Types:</xsl:text> + </fo:block> + + <fo:list-block xsl:use-attribute-sets="type-listblock"> + <xsl:apply-templates> + <xsl:with-param name="partnum" select="$partnum"/> + </xsl:apply-templates> + </fo:list-block> + + </xsl:if> </xsl:template> @@ -1431,6 +1599,7 @@ </xsl:template> + <!-- Does not look at @n_vars --> <xsl:template match="seealso"> <fo:inline font-style="italic"> <xsl:apply-templates/> diff --git a/lib/erl_docgen/src/otp_specs.erl b/lib/erl_docgen/src/otp_specs.erl index 728ddb2e6e..edb437a942 100644 --- a/lib/erl_docgen/src/otp_specs.erl +++ b/lib/erl_docgen/src/otp_specs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -380,6 +380,8 @@ t_type([E=#xmlElement{name = float}]) -> t_float(E); t_type([#xmlElement{name = nil}]) -> t_nil(); +t_type([#xmlElement{name = paren, content = Es}]) -> + t_paren(Es); t_type([#xmlElement{name = list, content = Es}]) -> t_list(Es); t_type([#xmlElement{name = nonempty_list, content = Es}]) -> @@ -416,6 +418,9 @@ t_float(E) -> t_nil() -> ["[]"]. +t_paren(Es) -> + ["("] ++ t_utype(get_elem(type, Es)) ++ [")"]. + t_list(Es) -> ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"]. @@ -532,6 +537,8 @@ ot_type([E=#xmlElement{name = float}]) -> ot_float(E); ot_type([#xmlElement{name = nil}]) -> ot_nil(); +ot_type([#xmlElement{name = paren, content = Es}]) -> + ot_paren(Es); ot_type([#xmlElement{name = list, content = Es}]) -> ot_list(Es); ot_type([#xmlElement{name = nonempty_list, content = Es}]) -> @@ -582,6 +589,9 @@ ot_float(E) -> ot_nil() -> {nil,0}. +ot_paren(Es) -> + {paren_type,0,[ot_utype(get_elem(type, Es))]}. + ot_list(Es) -> {type,0,list,[ot_utype(get_elem(type, Es))]}. @@ -697,5 +707,7 @@ annos_type([#xmlElement{name = union, content = Es}]) -> lists:flatmap(fun annos_elem/1, Es); annos_type([E=#xmlElement{name = typevar}]) -> annos_elem(E); +annos_type([#xmlElement{name = paren, content = Es}]) -> + annos(get_elem(type, Es)); annos_type(_) -> []. diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index 784ba78d3e..d83a8307e4 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c index fdf689e191..41d4fa3138 100644 --- a/lib/erl_interface/src/legacy/erl_connect.c +++ b/lib/erl_interface/src/legacy/erl_connect.c @@ -293,8 +293,13 @@ static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg) emsg->msg = NULL; if (msg.from.node[0] != '\0') emsg->from = erl_mk_pid(msg.from.node, msg.from.num, msg.from.serial, msg.from.creation); + else + emsg->from = NULL; if (msg.to.node[0] != '\0') emsg->to = erl_mk_pid(msg.to.node, msg.to.num, msg.to.serial, msg.to.creation); + else + emsg->to = NULL; + memcpy(emsg->to_name, msg.toname, MAXATOMLEN+1); return r; } diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl index aada184a76..86f46f25d0 100644 --- a/lib/et/src/et_wx_contents_viewer.erl +++ b/lib/et/src/et_wx_contents_viewer.erl @@ -464,7 +464,8 @@ create_window(S) -> wxFrame:setMenuBar(Frame,Bar), create_file_menu(Bar), Editor = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, 0 - bor ?wxDEFAULT + bor ?wxDEFAULT + bor ?wxTE_RICH2 %% Needed on Windows bor ?wxTE_MULTILINE bor ?wxTE_READONLY bor ?wxTE_DONTWRAP}]), @@ -483,6 +484,7 @@ create_window(S) -> wxFrame:connect(Frame, close_window, [{skip,true}]), wxFrame:setFocus(Frame), wxPanel:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Panel), wxFrame:show(Frame), S2#state{menu_data = HideData++SearchData++FilterData, editor = Editor, frame = Frame}. diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl index d42f8c0c86..7d4286ed9d 100644 --- a/lib/et/src/et_wx_viewer.erl +++ b/lib/et/src/et_wx_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -846,9 +846,6 @@ handle_info(#wx{event = #wxSize{size = {OldW, OldH}}} = Wx, S) -> refresh_main_window(S4) end, noreply(S6); -handle_info(#wx{event = #wxFocus{}}, S) -> - wxWindow:setFocus(S#state.canvas), % Get keyboard focus - noreply(S); handle_info(#wx{event = #wxMouse{type = enter_window}}, S) -> wxWindow:setFocus(S#state.canvas), % Get keyboard focus noreply(S); @@ -1252,7 +1249,6 @@ create_main_window(S) -> Self ! Ev end}]), wxPanel:connect(Canvas, key_down), - wxPanel:connect(Canvas, kill_focus), wxPanel:connect(Canvas, enter_window, [{skip, true}]), wxFrame:connect(Frame, command_menu_selected), wxFrame:connect(Frame, close_window), diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index 434bfac64c..5c06e5e558 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2010</year> + <year>2006</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 8f68087871..f6b6827e93 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -76,38 +76,37 @@ socket_opt() = See the Options used by gen_tcp(3) and <p>For more information about HTTP see rfc 2616</p> <code type="none"><![CDATA[ -method() = head | get | put | post | trace | options | delete -request() = {url(), headers()} | - {url(), headers(), content_type(), body()} -url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" -status_line() = {http_version(), status_code(), reason_phrase()} -http_version() = string() ex: "HTTP/1.1" -status_code() = integer() -reason_phrase() = string() -content_type() = string() -headers() = [header()] -header() = {field(), value()} -field() = string() -value() = string() -body() = string() | binary() -filename() = string() +method() = head | get | put | post | trace | options | delete +request() = {url(), headers()} | + {url(), headers(), content_type(), body()} +url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" +status_line() = {http_version(), status_code(), reason_phrase()} +http_version() = string() ex: "HTTP/1.1" +status_code() = integer() +reason_phrase() = string() +content_type() = string() +headers() = [header()] +header() = {field(), value()} +field() = string() +value() = string() +body() = string() | + binary() | + {fun(accumulator()) -> body_processing_result(), + accumulator()} | + {chunkify, + fun(accumulator()) -> body_processing_result(), + accumulator()} +body_processing_result() = eof | {ok, iolist(), accumulator()} +accumulator() = term() +filename() = string() ]]></code> </section> <section> <title>SSL DATA TYPES </title> - <p>Some type definitions relevant when using https, - for details <seealso marker="ssl:ssl">ssl(3)</seealso>: </p> - <code type="none"><![CDATA[ -ssl_options() = {verify, code()} | - {depth, depth()} | - {certfile, path()} | - {keyfile, path()} | - {password, string()} | - {cacertfile, path()} | - {ciphers, string()} - ]]></code> + <p>See <seealso marker="ssl:ssl">ssl(3)</seealso> for information + about ssl options (<c>ssloptions()</c>). </p> </section> <section> @@ -142,8 +141,9 @@ ssl_options() = {verify, code()} | <fsummary>Sends a get HTTP-request</fsummary> <type> <v>Url = url() </v> - <v>Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() </v> + <v>Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() </v> + <v>Body = string() | binary()</v> <v>Profile = profile()</v> <v>Reason = term() </v> </type> @@ -166,9 +166,9 @@ ssl_options() = {verify, code()} | <v>http_options() = [http_option()]</v> <v>http_option() = {timeout, timeout()} | {connect_timeout, timeout()} | - {ssl, ssl_options()} | - {ossl, ssl_options()} | - {essl, ssl_options()} | + {ssl, ssloptions()} | + {ossl, ssloptions()} | + {essl, ssloptions()} | {autoredirect, boolean()} | {proxy_auth, {userstring(), passwordstring()}} | {version, http_version()} | @@ -191,8 +191,9 @@ ssl_options() = {verify, code()} | <v>Function = atom() </v> <v>Args = list() </v> <v>body_format() = string | binary </v> - <v>Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() </v> + <v>Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() </v> + <v>Body = string() | binary()</v> <v>Profile = profile() </v> <v>Reason = {connect_failed, term()} | {send_failed, term()} | term() </v> @@ -226,7 +227,7 @@ ssl_options() = {verify, code()} | <tag><c><![CDATA[ssl]]></c></tag> <item> <p>This is the default ssl config option, currently defaults to - <c>ossl</c>, see below. </p> + <c>essl</c>, see below. </p> <p>Defaults to <c>[]</c>. </p> </item> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 6470b6fac7..edacb73b65 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -154,7 +154,7 @@ <c>ossl</c> specifically uses the OpenSSL based (old) SSL. <c>essl</c> specifically uses the Erlang based (new) SSL. When using <c>ssl</c> it <em>currently</em> defaults to - <c>ossl</c>. </p> + <c>essl</c>. </p> <p>Defaults to <c>ip_comm</c>. </p> </item> @@ -794,14 +794,14 @@ bytes <marker id="sdir_prop"></marker> <p> Here follows the valid properties for security directories</p> <taglist> - <tag>{security_data_file, path()}</tag> + <tag>{data_file, path()}</tag> <item> Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to store persistent data for the mod_security module. </item> - <tag>{security_max_retries, integer()}</tag> + <tag>{max_retries, integer()}</tag> <item> Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user @@ -811,13 +811,13 @@ bytes server will return 401 (Unauthorized), for security reasons. Defaults to 3 may also be set to infinity.</item> - <tag>{security_block_time, integer()}</tag> + <tag>{block_time, integer()}</tag> <item> Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. Defaults to 60</item> - <tag>{security_fail_expire_time, integer()}</tag> + <tag>{fail_expire_time, integer()}</tag> <item> Specifies the number of minutes a failed user authentication @@ -825,7 +825,7 @@ bytes time, his previous failed authentications are forgotten. Defaults to 30</item> - <tag>{security_auth_timeout, integer()}</tag> + <tag>{auth_timeout, integer()}</tag> <item> Specifies the number of seconds a successful user diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml index c367d7fa77..a2bf42320f 100644 --- a/lib/inets/doc/src/inets.xml +++ b/lib/inets/doc/src/inets.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -32,8 +32,10 @@ <modulesummary>The inets services API</modulesummary> <description> <p>This module provides the most basic API to the - clients and servers, that are part of the Inets application, - such as start and stop. </p> + clients and servers, that are part of the Inets application, + such as start and stop. </p> + + <marker id="common_data_types"></marker> </description> <section> @@ -42,7 +44,10 @@ this module: </p> <p><c> service() = ftpc | tftp | httpc | httpd</c></p> <p><c> property() = atom() </c></p> + <marker id="functions"></marker> + <marker id="services"></marker> </section> + <funcs> <func> <name>services() -> [{Service, Pid}]</name> @@ -54,11 +59,13 @@ <desc> <p>Returns a list of currently running services.</p> <note> - <p>Services started as <c>stand_alone</c> will not - be listed.</p> + <p>Services started as <c>stand_alone</c> will not be listed.</p> </note> + + <marker id="services_info"></marker> </desc> </func> + <func> <name>services_info() -> [{Service, Pid, Info}]</name> <fsummary>Returns a list of currently running services where @@ -73,11 +80,13 @@ </type> <desc> <p>Returns a list of currently running services where each - service is described by a [{Option, Value}] list. The - information given in the list is specific for each service - and it is probable that each service will have its own info - function that gives you even more details about the - service.</p> + service is described by a [{Option, Value}] list. The + information given in the list is specific for each service + and it is probable that each service will have its own info + function that gives you even more details about the + service.</p> + + <marker id="service_names"></marker> </desc> </func> @@ -89,6 +98,8 @@ </type> <desc> <p>Returns a list of available service names.</p> + + <marker id="start"></marker> </desc> </func> @@ -101,18 +112,24 @@ </type> <desc> <p>Starts the Inets application. Default type - is temporary. See also - <seealso marker="kernel:application">application(3)</seealso></p> + is temporary. See also + <seealso marker="kernel:application">application(3)</seealso>. </p> + + <marker id="stop"></marker> </desc> </func> + <func> <name>stop() -> ok </name> <fsummary>Stops the inets application.</fsummary> <desc> <p>Stops the inets application. See also - <seealso marker="kernel:application">application(3)</seealso></p> + <seealso marker="kernel:application">application(3)</seealso>. </p> + + <marker id="start2"></marker> </desc> </func> + <func> <name>start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name> <name>start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name> @@ -144,8 +161,11 @@ some sense the calling process has now become the top supervisor.</p> </note> + + <marker id="stop2"></marker> </desc> </func> + <func> <name>stop(Service, Reference) -> ok | {error, Reason} </name> <fsummary>Stops a started service of the inets application or takes @@ -157,9 +177,11 @@ </type> <desc> <p>Stops a started service of the inets application or takes - down a "stand_alone-service" gracefully. When the - <c>stand_alone</c> option is used in start, - only the pid is a valid argument to stop.</p> + down a "stand_alone-service" gracefully. When the + <c>stand_alone</c> option is used in start, + only the pid is a valid argument to stop.</p> + + <marker id="see_also"></marker> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index e81308a502..9674cd9a88 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,16 +32,19 @@ <modulesummary>Erlang Server Interface </modulesummary> <description> <p>This module defines the API - Erlang Server Interface (ESI). - Which is a more efficient way of writing erlang scripts - for your Inets web server than writing them as common CGI scripts.</p> + Which is a more efficient way of writing erlang scripts + for your Inets web server than writing them as common CGI scripts.</p> + + <marker id="deliver"></marker> </description> + <funcs> <func> <name>deliver(SessionID, Data) -> ok | {error, Reason}</name> <fsummary>Sends Data back to client.</fsummary> <type> <v>SessionID = term()</v> - <v>Data = string() | io_list()</v> + <v>Data = string() | io_list() | binary()</v> <v>Reason = term()</v> </type> <desc> @@ -51,13 +54,15 @@ parts of the content to the user.</p> <p>Sends data from a Erl Scheme script back to the client.</p> - <note><p>Note - that if any HTTP-header fields should be added by the - script they must be in the first call to deliver/2 and the - data in the call must be a string. Do not - assume anything about the data type of SessionID, the - SessionID must be the value given as input to the esi - call back function that you implemented.</p></note> + <note> + <p>Note that if any HTTP-header fields should be added by the + script they must be in the first call to deliver/2 and the + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of + SessionID, the SessionID must be the value given as input to + the esi call back function that you implemented.</p> + </note> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml index 2a871d29d8..a3c91dca5b 100644 --- a/lib/inets/doc/src/mod_security.xml +++ b/lib/inets/doc/src/mod_security.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1998</year><year>2010</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..0926df8581 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,140 @@ <file>notes.xml</file> </header> + <section><title>Inets 5.6</title> + + <section><title>Improvements and New Features</title> +<!-- + <p>-</p> +--> + <list> + <item> + <p>[httpc] Add support for upload body streaming (PUT and POST).</p> + <p>For more info, + see the definition of the <c>Body</c> argument of the + <seealso marker="httpc#request2">request/4,5</seealso> + function. </p> + <p>Filipe David Manana</p> + <p>Own Id: OTP-9094</p> + </item> + + <item> + <p>[ftp] Added (type) spec for all exported functions.</p> + <p>Own Id: OTP-9114 Aux Id: seq11799</p> + </item> + + <item> + <p>[httpd] + <seealso marker="mod_esi#deliver">mod_esi:deliver/2</seealso> + made to accept binary data. </p> + <p>Bernard Duggan</p> + <p>Own Id: OTP-9123</p> + </item> + + <item> + <p>[httpd] Prevent XSS in error pages. + Prevent user controlled input from being interpreted + as HTML in error pages by encoding the reserved HTML + characters. </p> + <p>Michael Santos</p> + <p>Own Id: OTP-9124</p> + </item> + + <item> + <p>[httpd] Improved error messages. </p> + <p>Ricardo Catalinas Jim�nez</p> + <p>Own Id: OTP-9157</p> + </item> + + <item> + <p>[httpd] Extended support for file descriptors. + In order to be able to bind to a privileged port + without running the erlang VM as root, the support + for using file descriptors has been improved. + It is now possible to add the file descriptor to the config + (option fd) when calling the + <seealso marker="inets#start2">inets:start(httpd, ...)</seealso> + function. </p> + <p>Attila Rajmund Nohl</p> + <p>Own Id: OTP-9202</p> + <p>Aux Id: seq11819</p> + </item> + + <item> + <p>The default ssl kind has now been changed to <c>essl</c>. </p> + <p><c>ossl</c> will work for as long as the ssl application + supports it. </p> + <p>See the httpd + <seealso marker="httpd#comm_prop">socket_type</seealso> + communication property or the httpc + <seealso marker="httpc#request2">request/4,5</seealso> function + for more info. </p> + <p>Own Id: OTP-9230</p> + <p>*** POTENTIAL INCOMPATIBILITY ***</p> + </item> + + </list> + </section> + + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpd] Wrong + <seealso marker="httpd#sec_prop">security property</seealso> + names used in documentation. </p> + <p><c>security_data_file</c> used instead of <c>data_file</c>. </p> + <p><c>security_max_retries</c> used instead of <c>max_retries</c>. </p> + <p><c>security_block_time</c> used instead of <c>block_time</c>. </p> + <p><c>security_fail_expire_time</c> used instead of <c>fail_expire_time</c>. </p> + <p><c>security_auth_timeout</c> used instead of <c>auth_timeout</c>. </p> + <p>Garrett Smith</p> + <p>Own Id: OTP-9131</p> + </item> + + <item> + <p>[httpd] Fix timeout message generated by mod_esi. + When a mod_esi request times out, the code to send a + timeout response was incorrect and generated an + internal server error as well as an invalid response + line. </p> + <p>Bernard Duggan</p> + <p>Own Id: OTP-9158</p> + </item> + + <item> + <p>[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash. </p> + <p>This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions. </p> + <p>Own Id: OTP-9246</p> + </item> + + <item> + <p>[httpc] Issuing a request (<c>httpc:request</c>) to an + host with the ssl option + <c>{ip, {127,0,0,1}}</c> results in an handler crash. + The reason was that the connect call resulted in an exit with + reason <c>badarg</c> + (this was the same for both <c>ssl</c> and <c>gen_tcp</c>). </p> + <p>Exits was not catched. This has now been improved. </p> + <p>Own Id: OTP-9289</p> + <p>Aux Id: seq11845</p> + </item> + + </list> + </section> + + </section> <!-- 5.6 --> + + <section><title>Inets 5.5.2</title> <section><title>Improvements and New Features</title> @@ -80,7 +214,8 @@ are URL-encoded. Added support in http-client to use URL-encoding. Also added the missing include directory for the inets application.</p> - <p>Own Id: OTP-8940 Aux Id: seq11735 </p> + <p>Own Id: OTP-8940</p> + <p>Aux Id: seq11735</p> </item> </list> </section> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 5ad74851c8..fe6cb0c191 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -92,6 +92,12 @@ }). +-type shortage_reason() :: 'etnospc' | 'epnospc'. +-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'. +-type common_reason() :: 'econn' | 'eclosed' | term(). +-type file_write_error_reason() :: term(). % See file:write for more info + + %%%========================================================================= %%% API - CLIENT FUNCTIONS %%%========================================================================= @@ -106,6 +112,9 @@ %% Description: Start an ftp client and connect to a host. %%-------------------------------------------------------------------------- +-spec open(Host :: string() | inet:ip_address()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open({option_list, Options}) when is_list(Options) -> try @@ -126,6 +135,9 @@ open({option_list, Options}) when is_list(Options) -> open(Host) -> open(Host, []). +-spec open(Host :: string() | inet:ip_address(), Opts :: list()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open(Host, Port) when is_integer(Port) -> open(Host, [{port, Port}]); @@ -161,12 +173,24 @@ open(Host, Opts) when is_list(Opts) -> %% %% Description: Login with or without a supplied account name. %%-------------------------------------------------------------------------- +-spec user(Pid :: pid(), + User :: string(), + Pass :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass) -> call(Pid, {user, User, Pass}, atom). +-spec user(Pid :: pid(), + User :: string(), + Pass :: string(), + Acc :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass, Acc) -> call(Pid, {user, User, Pass, Acc}, atom). + %%-------------------------------------------------------------------------- %% account(Pid, Acc) -> ok | {error, eacct} %% Pid = pid() @@ -174,9 +198,14 @@ user(Pid, User, Pass, Acc) -> %% %% Description: Set a user Account. %%-------------------------------------------------------------------------- + +-spec account(Pid :: pid(), Acc :: string()) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + account(Pid, Acc) -> call(Pid, {account, Acc}, atom). + %%-------------------------------------------------------------------------- %% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn} %% Pid = pid() @@ -184,19 +213,30 @@ account(Pid, Acc) -> %% %% Description: Get the current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec pwd(Pid :: pid()) -> + {'ok', Dir :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + pwd(Pid) -> call(Pid, pwd, ctrl). + %%-------------------------------------------------------------------------- -%% lpwd(Pid) -> {ok, Dir} | {error, elogin} +%% lpwd(Pid) -> {ok, Dir} %% Pid = pid() %% Dir = string() %% %% Description: Get the current working directory at local server. %%-------------------------------------------------------------------------- + +-spec lpwd(Pid :: pid()) -> + {'ok', Dir :: string()}. + lpwd(Pid) -> call(Pid, lpwd, string). + %%-------------------------------------------------------------------------- %% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid() @@ -204,9 +244,14 @@ lpwd(Pid) -> %% %% Description: Change current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec cd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + cd(Pid, Dir) -> call(Pid, {cd, Dir}, atom). + %%-------------------------------------------------------------------------- %% lcd(Pid, Dir) -> ok | {error, epath} %% Pid = pid() @@ -214,9 +259,14 @@ cd(Pid, Dir) -> %% %% Description: Change current working directory for the local client. %%-------------------------------------------------------------------------- + +-spec lcd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason()}. + lcd(Pid, Dir) -> call(Pid, {lcd, Dir}, string). + %%-------------------------------------------------------------------------- %% ls(Pid) -> Result %% ls(Pid, <Dir>) -> Result @@ -229,11 +279,22 @@ lcd(Pid, Dir) -> %% %% Description: Returns a list of files in long format. %%-------------------------------------------------------------------------- + +-spec ls(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid) -> ls(Pid, ""). + +-spec ls(Pid :: pid(), Dir :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid, Dir) -> call(Pid, {dir, long, Dir}, string). + %%-------------------------------------------------------------------------- %% nlist(Pid) -> Result %% nlist(Pid, Pathname) -> Result @@ -246,21 +307,37 @@ ls(Pid, Dir) -> %% %% Description: Returns a list of files in short format %%-------------------------------------------------------------------------- + +-spec nlist(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid) -> nlist(Pid, ""). + +-spec nlist(Pid :: pid(), Pathname :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid, Dir) -> call(Pid, {dir, short, Dir}, string). + %%-------------------------------------------------------------------------- -%% rename(Pid, CurrFile, NewFile) -> ok | {error, epath} | {error, elogin} -%% | {error, econn} +%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin} +%% | {error, econn} %% Pid = pid() %% CurrFile = NewFile = string() %% %% Description: Rename a file at remote server. %%-------------------------------------------------------------------------- -rename(Pid, CurrFile, NewFile) -> - call(Pid, {rename, CurrFile, NewFile}, string). + +-spec rename(Pid :: pid(), Old :: string(), New :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + +rename(Pid, Old, New) -> + call(Pid, {rename, Old, New}, string). + %%-------------------------------------------------------------------------- %% delete(Pid, File) -> ok | {error, epath} | {error, elogin} | @@ -270,9 +347,14 @@ rename(Pid, CurrFile, NewFile) -> %% %% Description: Remove file at remote server. %%-------------------------------------------------------------------------- + +-spec delete(Pid :: pid(), File :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + delete(Pid, File) -> call(Pid, {delete, File}, string). + %%-------------------------------------------------------------------------- %% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -280,9 +362,14 @@ delete(Pid, File) -> %% %% Description: Make directory at remote server. %%-------------------------------------------------------------------------- + +-spec mkdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + mkdir(Pid, Dir) -> call(Pid, {mkdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -290,9 +377,14 @@ mkdir(Pid, Dir) -> %% %% Description: Remove directory at remote server. %%-------------------------------------------------------------------------- + +-spec rmdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + rmdir(Pid, Dir) -> call(Pid, {rmdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn} %% Pid = pid() @@ -300,23 +392,41 @@ rmdir(Pid, Dir) -> %% %% Description: Set transfer type. %%-------------------------------------------------------------------------- + +-spec type(Pid :: pid(), Type :: ascii | binary) -> + 'ok' | + {'error', Reason :: 'etype' | restriction_reason() | common_reason()}. + type(Pid, Type) -> call(Pid, {type, Type}, atom). + %%-------------------------------------------------------------------------- -%% recv(Pid, RemoteFileName <LocalFileName>) -> ok | {error, epath} | +%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} | %% {error, elogin} | {error, econn} %% Pid = pid() %% RemoteFileName = LocalFileName = string() %% %% Description: Transfer file from remote server. %%-------------------------------------------------------------------------- + +-spec recv(Pid :: pid(), RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | + common_reason() | + file_write_error_reason()}. + recv(Pid, RemotFileName) -> recv(Pid, RemotFileName, RemotFileName). +-spec recv(Pid :: pid(), + RemoteFileName :: string(), + LocalFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + recv(Pid, RemotFileName, LocalFileName) -> call(Pid, {recv, RemotFileName, LocalFileName}, atom). + %%-------------------------------------------------------------------------- %% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin} %% | {error, econn} @@ -326,9 +436,16 @@ recv(Pid, RemotFileName, LocalFileName) -> %% %% Description: Transfer file from remote server into binary. %%-------------------------------------------------------------------------- + +-spec recv_bin(Pid :: pid(), + RemoteFile :: string()) -> + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_bin(Pid, RemoteFile) -> call(Pid, {recv_bin, RemoteFile}, bin). + %%-------------------------------------------------------------------------- %% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -337,9 +454,15 @@ recv_bin(Pid, RemoteFile) -> %% %% Description: Start receive of chunks of remote file. %%-------------------------------------------------------------------------- + +-spec recv_chunk_start(Pid :: pid(), + RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk_start(Pid, RemoteFile) -> call(Pid, {recv_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason} %% Pid = pid() @@ -347,24 +470,47 @@ recv_chunk_start(Pid, RemoteFile) -> %% %% Description: Transfer file from remote server into binary in chunks %%-------------------------------------------------------------------------- + +-spec recv_chunk(Pid :: pid()) -> + 'ok' | + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk(Pid) -> call(Pid, recv_chunk, atom). + %%-------------------------------------------------------------------------- -%% send(Pid, LocalFileName <RemotFileName>) -> ok | {error, epath} -%% | {error, elogin} -%% | {error, econn} +%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Transfer file to remote server. %%-------------------------------------------------------------------------- + +-spec send(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName) -> send(Pid, LocalFileName, LocalFileName). +-spec send(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName, RemotFileName) -> call(Pid, {send, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -374,11 +520,19 @@ send(Pid, LocalFileName, RemotFileName) -> %% %% Description: Transfer a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {send_bin, Bin, RemoteFile}, atom); send_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -387,9 +541,14 @@ send_bin(_Pid, _Bin, _RemoteFile) -> %% %% Description: Start transfer of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + send_chunk_start(Pid, RemoteFile) -> call(Pid, {send_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | %% {error, epath} | {error, econn} @@ -398,9 +557,14 @@ send_chunk_start(Pid, RemoteFile) -> %% %% Description: Start append chunks of data to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: term()}. + append_chunk_start(Pid, RemoteFile) -> call(Pid, {append_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -409,11 +573,19 @@ append_chunk_start(Pid, RemoteFile) -> %% %% Purpose: Send chunk to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + send_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); send_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -422,11 +594,19 @@ send_chunk(_Pid, _Bin) -> %% %% Description: Append chunk to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + append_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); append_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -434,9 +614,17 @@ append_chunk(_Pid, _Bin) -> %% %% Description: End sending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- %% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -444,23 +632,47 @@ send_chunk_end(Pid) -> %% %% Description: End appending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- -%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} -%% | {error, elogin} | {error, econn} +%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Append the local file to the remote file %%-------------------------------------------------------------------------- + +-spec append(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: 'epath' | + 'elogin' | + 'etnospc' | + 'epnospc' | + 'efnamena' | common_reason()}. + append(Pid, LocalFileName) -> append(Pid, LocalFileName, LocalFileName). +-spec append(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + append(Pid, LocalFileName, RemotFileName) -> call(Pid, {append, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -470,27 +682,44 @@ append(Pid, LocalFileName, RemotFileName) -> %% %% Purpose: Append a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec append_bin(Pid :: pid(), + Bin :: binary(), + RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {append_bin, Bin, RemoteFile}, atom); append_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- -%% quote(Pid, Cmd) -> ok +%% quote(Pid, Cmd) -> list() %% Pid = pid() %% Cmd = string() %% %% Description: Send arbitrary ftp command. %%-------------------------------------------------------------------------- + +-spec quote(Pid :: pid(), Cmd :: string()) -> list(). + quote(Pid, Cmd) when is_list(Cmd) -> call(Pid, {quote, Cmd}, atom). + %%-------------------------------------------------------------------------- %% close(Pid) -> ok %% Pid = pid() %% %% Description: End the ftp session. %%-------------------------------------------------------------------------- + +-spec close(Pid :: pid()) -> 'ok'. + close(Pid) -> cast(Pid, close), ok. @@ -502,9 +731,13 @@ close(Pid) -> %% %% Description: Return diagnostics. %%-------------------------------------------------------------------------- + +-spec formaterror(Tag :: term()) -> string(). + formaterror(Tag) -> ftp_response:error_string(Tag). + info(Pid) -> call(Pid, info, list). diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 04fae13b20..b70b16f57f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,10 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} | +%% {chunkify, fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% SendFunResult = eof | {ok, iolist(), NewSendAcc} +%% SendAcc = NewSendAcc = term() %% %% Description: Sends a HTTP-request. The function can be both %% syncronus and asynchronous in the later case the function will @@ -426,26 +429,44 @@ service_info(Pid) -> handle_request(Method, Url, {Scheme, UserInfo, Host, Port, Path, Query}, - Headers, ContentType, Body, + Headers0, ContentType, Body0, HTTPOptions0, Options0, Profile) -> - Started = http_util:timestamp(), - NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers], + Started = http_util:timestamp(), + NewHeaders0 = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers0], try begin + ?hcrt("begin processing", [{started, Started}, + {new_headers, NewHeaders0}]), + + {NewHeaders, Body} = + case Body0 of + {chunkify, ProcessBody, Acc} + when is_function(ProcessBody, 1) -> + NewHeaders1 = ensure_chunked_encoding(NewHeaders0), + Body1 = {mk_chunkify_fun(ProcessBody), Acc}, + {NewHeaders1, Body1}; + {ProcessBody, _} + when is_function(ProcessBody, 1) -> + {NewHeaders0, Body0}; + _ when is_list(Body0) orelse is_binary(Body0) -> + {NewHeaders0, Body0}; + _ -> + throw({error, {bad_body, Body0}}) + end, + HTTPOptions = http_options(HTTPOptions0), Options = request_options(Options0), Sync = proplists:get_value(sync, Options), Stream = proplists:get_value(stream, Options), Host2 = header_host(Scheme, Host, Port), HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), - Receiver = proplists:get_value(receiver, Options), - SocketOpts = proplists:get_value(socket_opts, Options), - UrlEncodeBool = HTTPOptions#http_options.url_encode, - MaybeEscPath = url_encode(Path, UrlEncodeBool), - MaybeEscQuery = url_encode(Query, UrlEncodeBool), - AbsUri = url_encode(Url, UrlEncodeBool), + Receiver = proplists:get_value(receiver, Options), + SocketOpts = proplists:get_value(socket_opts, Options), + MaybeEscPath = maybe_encode_uri(HTTPOptions, Path), + MaybeEscQuery = maybe_encode_uri(HTTPOptions, Query), + AbsUri = maybe_encode_uri(HTTPOptions, Url), Request = #request{from = Receiver, scheme = Scheme, @@ -458,38 +479,71 @@ handle_request(Method, Url, settings = HTTPOptions, abs_uri = AbsUri, userinfo = UserInfo, - stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + stream = Stream, + headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, started = Started}, + case httpc_manager:request(Request, profile_name(Profile)) of {ok, RequestId} -> handle_answer(RequestId, Sync, Options); {error, Reason} -> + ?hcrd("request failed", [{reason, Reason}]), {error, Reason} end end catch error:{noproc, _} -> + ?hcrv("noproc", [{profile, Profile}]), {error, {not_started, Profile}}; throw:Error -> + ?hcrv("throw", [{error, Error}]), Error end. -url_encode(URI, true) -> +ensure_chunked_encoding(Hdrs) -> + Key = "transfer-encoding", + lists:keystore(Key, 1, Hdrs, {Key, "chunked"}). + +maybe_encode_uri(#http_options{url_encode = true}, URI) -> http_uri:encode(URI); -url_encode(URI, false) -> +maybe_encode_uri(_, URI) -> URI. +mk_chunkify_fun(ProcessBody) -> + fun(eof_body) -> + eof; + (Acc) -> + case ProcessBody(Acc) of + eof -> + {ok, <<"0\r\n\r\n">>, eof_body}; + {ok, Data, NewAcc} -> + {ok, mk_chunk_bin(Data), NewAcc} + end + end. + +mk_chunk_bin(Data) -> + Bin = iolist_to_binary(Data), + iolist_to_binary([hex_size(Bin), "\r\n", Bin, "\r\n"]). + +hex_size(Bin) -> + hd(io_lib:format("~.16B", [size(Bin)])). + + handle_answer(RequestId, false, _) -> {ok, RequestId}; handle_answer(RequestId, true, Options) -> receive {http, {RequestId, saved_to_file}} -> + ?hcrt("received saved-to-file", [{request_id, RequestId}]), {ok, saved_to_file}; {http, {RequestId, {_,_,_} = Result}} -> + ?hcrt("received answer", [{request_id, RequestId}, + {result, Result}]), return_answer(Options, Result); {http, {RequestId, {error, Reason}}} -> + ?hcrt("received error", [{request_id, RequestId}, + {reason, Reason}]), {error, Reason} end. diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index cb6f3e2841..1f0e012e7e 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,10 +29,10 @@ %%-------------------------------------------------------------------- %% Internal Application API -export([ - start_link/2, - connect_and_send/2, + start_link/4, + %% connect_and_send/2, send/2, - cancel/2, + cancel/3, stream/3, stream_next/1, info/1 @@ -51,7 +51,7 @@ -record(state, { request, % #request{} - session, % #tcp_session{} + session, % #session{} status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() @@ -94,13 +94,9 @@ %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -start_link(Options, ProfileName) -> - Args = [Options, ProfileName], - gen_server:start_link(?MODULE, Args, []). - -connect_and_send(Request, HandlerPid) -> - call({connect_and_send, Request}, HandlerPid). - +start_link(Parent, Request, Options, ProfileName) -> + {ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options, + ProfileName]])}. %%-------------------------------------------------------------------- %% Function: send(Request, Pid) -> ok @@ -122,8 +118,8 @@ send(Request, Pid) -> %% Description: Cancels a request. Intended to be called by the httpc %% manager process. %%-------------------------------------------------------------------- -cancel(RequestId, Pid) -> - cast({cancel, RequestId}, Pid). +cancel(RequestId, Pid, From) -> + cast({cancel, RequestId, From}, Pid). %%-------------------------------------------------------------------- @@ -229,16 +225,27 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed %% but we do not want that so errors will be handled by the process %% sending an init_error message to itself. %%-------------------------------------------------------------------- -init([Options, ProfileName]) -> - ?hcrv("init - starting", [{options, Options}, {profile, ProfileName}]), +init([Parent, Request, Options, ProfileName]) -> process_flag(trap_exit, true), - handle_verbose(Options#options.verbose), - State = #state{status = undefined, - options = Options, - profile_name = ProfileName}, - ?hcrd("init - started", []), - {ok, State}. + %% Do not let initial tcp-connection block the manager-process + proc_lib:init_ack(Parent, self()), + handle_verbose(Options#options.verbose), + Address = handle_proxy(Request#request.address, Options#options.proxy), + {ok, State} = + case {Address /= Request#request.address, Request#request.scheme} of + {true, https} -> + Error = https_through_proxy_is_not_currently_supported, + self() ! {init_error, + Error, httpc_response:error(Request, Error)}, + {ok, #state{request = Request, options = Options, + status = ssl_tunnel}}; + {_, _} -> + connect_and_send_first_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}) + end, + gen_server:enter_loop(?MODULE, [], State). %%-------------------------------------------------------------------- %% Function: handle_call(Request, From, State) -> {reply, Reply, State} | @@ -249,41 +256,6 @@ init([Options, ProfileName]) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- - - -%% This is the first request, the reason the proc was started -handle_call({connect_and_send, #request{address = Address0, - scheme = Scheme} = Request}, - _From, - #state{options = #options{proxy = Proxy}, - status = undefined, - session = undefined} = State) -> - ?hcrv("connect and send", [{address0, Address0}, {proxy, Proxy}]), - Address = handle_proxy(Address0, Proxy), - if - ((Address =/= Address0) andalso (Scheme =:= https)) -> - %% This is what we should do if and when ssl supports - %% "socket upgrading" - %%send_ssl_tunnel_request(Address, Request, - %% #state{options = Options, - %% status = ssl_tunnel}); - Reason = {failed_connecting, - https_through_proxy_is_not_currently_supported}, - %% Send a reply to the original caller - ErrorResponse = httpc_response:error(Request, Reason), - httpc_response:send(Request#request.from, ErrorResponse), - %% Reply to the manager - ErrorReply = {error, Reason}, - {stop, normal, ErrorReply, State}; - true -> - case connect_and_send_first_request(Address, Request, State) of - {ok, NewState} -> - {reply, ok, NewState}; - {stop, Error, NewState} -> - {stop, normal, Error, NewState} - end - end; - handle_call(#request{address = Addr} = Request, _, #state{status = Status, session = #session{type = pipeline} = Session, @@ -445,25 +417,27 @@ handle_call(info, _, State) -> %% handle_keep_alive_queue/2 on the other hand will just skip the %% request as if it was never issued as in this case the request will %% not have been sent. -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{request = #request{id = RequestId} = Request, profile_name = ProfileName, canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], request = Request#request{from = answer_sent}}}; -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{profile_name = ProfileName, + request = #request{id = CurrId}, canceled = Canceled} = State) -> - ?hcrv("cancel", [{request_id, RequestId}, + ?hcrv("cancel", [{request_id, RequestId}, + {curr_req_id, CurrId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; @@ -872,62 +846,55 @@ connect(SocketType, ToAddress, Opts3 = [IpFamily | Opts2], http_transport:connect(SocketType, ToAddress, Opts3, Timeout) end. - -connect_and_send_first_request(Address, - #request{settings = Settings, - headers = Headers, - address = OrigAddress, - scheme = Scheme} = Request, - #state{options = Options} = State) -> - - ?hcrd("connect", - [{address, Address}, {request, Request}, {options, Options}]), +connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> SocketType = socket_type(Request), - ConnTimeout = Settings#http_options.connect_timeout, + ConnTimeout = (Request#request.settings)#http_options.connect_timeout, + ?hcri("connect", + [{address, Address}, {request, Request}, {options, Options}]), case connect(SocketType, Address, Options, ConnTimeout) of {ok, Socket} -> - Session = #session{id = {OrigAddress, self()}, - scheme = Scheme, - socket = Socket, - socket_type = SocketType}, - ?hcrd("connected - now send first request", [{socket, Socket}]), + ClientClose = + httpc_request:is_client_closing( + Request#request.headers), + SessionType = httpc_manager:session_type(Options), + SocketType = socket_type(Request), + Session = #session{id = {Request#request.address, self()}, + scheme = Request#request.scheme, + socket = Socket, + socket_type = SocketType, + client_close = ClientClose, + type = SessionType}, + ?hcri("connected - now send first request", [{socket, Socket}]), + case httpc_request:send(Address, Session, Request) of ok -> - ?hcrd("first request sent", []), - ClientClose = - httpc_request:is_client_closing(Headers), - SessionType = httpc_manager:session_type(Options), - Session2 = - Session#session{client_close = ClientClose, - type = SessionType}, - TmpState = - State#state{request = Request, - session = Session2, - mfa = init_mfa(Request, State), - status_line = init_status_line(Request), - headers = undefined, - body = undefined, - status = new}, - ?hcrt("activate socket", []), - activate_once(Session), + ?hcri("first request sent", []), + TmpState = State#state{request = Request, + session = Session, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), NewState = activate_request_timeout(TmpState), {ok, NewState}; - - {error, Reason} = Error -> - ?hcrv("failed sending request", [{reason, Reason}]), - {stop, Error, - State#state{session = {send_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_sending, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request, + session = + #session{socket = Socket}}} end; - - {error, Reason} = Error -> - ?hcri("connect failed", [{reason, Reason}]), - {stop, Error, State#state{session = {connect_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_connecting, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request}} end. - handler_info(#state{request = Request, session = Session, status_line = _StatusLine, @@ -1167,12 +1134,12 @@ handle_response(#state{request = Request, {ok, Msg, Data} -> ?hcrd("handle response - ok", []), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), handle_queue(NewState, Data); {stop, Msg} -> ?hcrd("handle response - stop", [{msg, Msg}]), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), {stop, normal, NewState} end. @@ -1242,7 +1209,8 @@ handle_pipeline(#state{status = pipeline, %% See comment for handle_cast({cancel, RequestId}) {stop, normal, State#state{request = - NextRequest#request{from = answer_sent}}}; + NextRequest#request{from = answer_sent}, + pipeline = Pipeline}}; false -> ?hcrv("next request", [{request, NextRequest}]), NewSession = @@ -1443,6 +1411,7 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), + State#state{request = Request#request{from = answer_sent}, timers = Timers#timers{request_timers = @@ -1662,71 +1631,28 @@ handle_verbose(_) -> ok. -%%% Normaly I do not comment out code, I throw it away. But this might -%%% actually be used one day if ssl is improved. -%% send_ssl_tunnel_request(Address, Request = #request{address = {Host, Port}}, -%% State) -> -%% %% A ssl tunnel request is a special http request that looks like -%% %% CONNECT host:port HTTP/1.1 -%% SslTunnelRequest = #request{method = connect, scheme = http, -%% headers = -%% #http_request_h{ -%% host = Host, -%% address = Address, -%% path = Host ++ ":", -%% pquery = integer_to_list(Port), -%% other = [{ "Proxy-Connection", "keep-alive"}]}, -%% Ipv6 = (State#state.options)#options.ipv6, -%% SocketType = socket_type(SslTunnelRequest), -%% case http_transport:connect(SocketType, -%% SslTunnelRequest#request.address, Ipv6) of -%% {ok, Socket} -> -%% case httpc_request:send(Address, SslTunnelRequest, Socket) of -%% ok -> -%% Session = #tcp_session{id = -%% {SslTunnelRequest#request.address, -%% self()}, -%% scheme = -%% SslTunnelRequest#request.scheme, -%% socket = Socket}, -%% NewState = State#state{mfa = -%% {httpc_response, parse, -%% [State#state.max_header_size]}, -%% request = Request, -%% session = Session}, -%% http_transport:setopts(socket_type( -%% SslTunnelRequest#request.scheme), -%% Socket, -%% [{active, once}]), -%% {ok, NewState}; -%% {error, Reason} -> -%% self() ! {init_error, error_sending, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request, -%% session = #tcp_session{socket = -%% Socket}}} -%% end; -%% {error, Reason} -> -%% self() ! {init_error, error_connecting, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request}} -%% end. - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - +send_raw(#session{socket = Socket, socket_type = SocketType}, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send raw", [{acc, Acc}]), + send_raw(SocketType, Socket, ProcessBody, Acc); send_raw(#session{socket = Socket, socket_type = SocketType}, Body) -> http_transport:send(SocketType, Socket, Body). +send_raw(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{data, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + send_raw(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. call(Msg, Pid) -> @@ -1738,11 +1664,5 @@ call(Msg, Pid, Timeout) -> cast(Msg, Pid) -> gen_server:cast(Pid, Msg). - -%% to(To, Start) when is_integer(Start) andalso (Start >= 0) -> -%% http_util:timeout(To, Start); -%% to(To, _Start) -> -%% http_util:timeout(To, t()). - t() -> http_util:timestamp(). diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl index 2a69fd15d0..f7a0b014b3 100644 --- a/lib/inets/src/http_client/httpc_handler_sup.erl +++ b/lib/inets/src/http_client/httpc_handler_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ %% API -export([start_link/0]). --export([start_child/2]). +-export([start_child/1]). %% Supervisor callback -export([init/1]). @@ -34,11 +34,9 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -start_child(Options, Profile) -> - Args = [Options, Profile], +start_child(Args) -> supervisor:start_child(?MODULE, Args). - %%%========================================================================= %%% Supervisor callback %%%========================================================================= diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 591cb78c29..7f66b477eb 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ start_link/3, request/2, cancel_request/2, - request_canceled/2, + request_canceled/3, request_done/2, retry_request/2, redirect_request/2, @@ -66,6 +66,7 @@ state % State of the handler: initiating | started | operational | canceled }). +-define(DELAY, 500). %%==================================================================== %% Internal Application API @@ -158,7 +159,8 @@ cancel_request(RequestId, ProfileName) -> %% be called by the httpc handler process. %%-------------------------------------------------------------------- -request_canceled(RequestId, ProfileName) -> +request_canceled(RequestId, ProfileName, From) -> + gen_server:reply(From, ok), cast(ProfileName, {request_canceled, RequestId}). @@ -355,44 +357,32 @@ do_init(ProfileName, CookiesDir) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({request, Request}, _From, State) -> - ?hcrv("request", [{request, Request}]), +handle_call({request, Request}, _, State) -> + ?hcri("request", [{request, Request}]), case (catch handle_request(Request, State)) of - {ok, ReqId, NewState} -> - {reply, {ok, ReqId}, NewState}; - + {reply, Msg, NewState} -> + {reply, Msg, NewState}; Error -> - NewError = {error, {failed_process_request, Error}}, - {reply, NewError, State} + {stop, Error, httpc_response:error(Request, Error), State} end; - -handle_call({cancel_request, RequestId}, From, - #state{handler_db = HandlerDb} = State) -> - ?hcrv("cancel_request", [{request_id, RequestId}]), + +handle_call({cancel_request, RequestId}, From, State) -> + ?hcri("cancel_request", [{request_id, RequestId}]), case ets:lookup(State#state.handler_db, RequestId) of [] -> - ?hcrd("nothing to cancel", []), - Reply = ok, %% Nothing to cancel - {reply, Reply, State}; - - [#handler_info{handler = Pid}] when is_pid(Pid) -> - ?hcrd("found operational handler for this request", - [{handler, Pid}]), - httpc_handler:cancel(RequestId, Pid), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}}; - - [#handler_info{starter = Pid, state = HandlerState}] - when is_pid(Pid) -> - ?hcri("found *initiating* handler for this request", - [{starter, Pid}, {state, HandlerState}]), - ets:update_element(HandlerDb, RequestId, - {#handler_info.state, canceled}), + %% The request has allready compleated make sure + %% it is deliverd to the client process queue so + %% it can be thrown away by httpc:cancel_request + %% This delay is hopfully a temporary workaround. + %% Note that it will not not delay the manager, + %% only the client that called httpc:cancel_request + timer:apply_after(?DELAY, gen_server, reply, [From, ok]), + {noreply, State}; + [{_, Pid, _}] -> + httpc_handler:cancel(RequestId, Pid, From), {noreply, State#state{cancel = - [{RequestId, Pid, From} | + [{RequestId, Pid, From} | State#state.cancel]}} - end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -437,43 +427,16 @@ handle_call(Req, From, #state{profile_name = ProfileName} = State) -> %%-------------------------------------------------------------------- handle_cast({retry_or_redirect_request, {Time, Request}}, #state{profile_name = ProfileName} = State) -> - ?hcrv("retry or redirect request", [{time, Time}, {request, Request}]), - case timer:apply_after(Time, ?MODULE, retry_request, - [Request, ProfileName]) of - {ok, _} -> - {noreply, State}; - {error, Reason} -> - error_report(ProfileName, - "failed scheduling retry/redirect request" - "~n Time: ~p" - "~n Request: ~p" - "~n Reason: ~p", [Time, Request, Reason]), - {noreply, State} - end; + {ok, _} = timer:apply_after(Time, ?MODULE, retry_request, [Request, ProfileName]), + {noreply, State}; -handle_cast({retry_or_redirect_request, Request}, - #state{profile_name = Profile, - handler_db = HandlerDb} = State) -> - ?hcrv("retry or redirect request", [{request, Request}]), +handle_cast({retry_or_redirect_request, Request}, State) -> case (catch handle_request(Request, State)) of - {ok, _, NewState} -> + {reply, {ok, _}, NewState} -> {noreply, NewState}; - Error -> - ReqId = Request#request.id, - error_report(Profile, - "failed to retry or redirect request ~p" - "~n Error: ~p", [ReqId, Error]), - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{from = From}] -> - Error2 = httpc_response:error(Request, Error), - httpc_response:send(From, Error2), - ok; - - _ -> - ok - end, - {noreply, State} + httpc_response:error(Request, Error), + {stop, Error, State} end; handle_cast({request_canceled, RequestId}, State) -> @@ -482,7 +445,6 @@ handle_cast({request_canceled, RequestId}, State) -> case lists:keysearch(RequestId, 1, State#state.cancel) of {value, Entry = {RequestId, _, From}} -> ?hcrt("found in cancel", [{from, From}]), - gen_server:reply(From, ok), {noreply, State#state{cancel = lists:delete(Entry, State#state.cancel)}}; Else -> @@ -539,8 +501,6 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> "recived unknown message" "~n Msg: ~p", [Msg]), {noreply, State}. - - %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -548,39 +508,17 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling all non call/cast messages %%--------------------------------------------------------- - -handle_info({started, StarterPid, ReqId, HandlerPid}, State) -> - handle_started(StarterPid, ReqId, HandlerPid, State), - {noreply, State}; - -handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) -> - handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State), - {noreply, State}; - -handle_info({failed_starting_handler, StarterPid, ReqId, Res}, State) -> - handle_failed_starting_handler(StarterPid, ReqId, Res, State), - {noreply, State}; - -handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) -> - maybe_handle_terminating_starter(Pid, Reason, HandlerDb), +handle_info({'EXIT', _, _}, State) -> + %% Handled in DOWN {noreply, State}; - handle_info({'DOWN', _, _, Pid, _}, State) -> - - %% - %% Normally this should have been cleaned up already - %% (when receiving {request_done, PequestId}), but - %% just in case there is a glitch, cleanup anyway. - %% - - Pattern = #handler_info{handler = Pid, _ = '_'}, - ets:match_delete(State#state.handler_db, Pattern), + ets:match_delete(State#state.handler_db, {'_', Pid, '_'}), %% If there where any canceled request, handled by the %% the process that now has terminated, the %% cancelation can be viewed as sucessfull! - NewCanceledList = - lists:foldl(fun({_, HandlerPid, From} = Entry, Acc) -> + NewCanceldList = + lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) -> case HandlerPid of Pid -> gen_server:reply(From, ok), @@ -589,15 +527,13 @@ handle_info({'DOWN', _, _, Pid, _}, State) -> Acc end end, State#state.cancel, State#state.cancel), - {noreply, State#state{cancel = NewCanceledList}}; - -handle_info(Info, #state{profile_name = ProfileName} = State) -> - error_report(ProfileName, - "received unknown info" - "~n Info: ~p", [Info]), + {noreply, State#state{cancel = NewCanceldList}}; +handle_info(Info, State) -> + Report = io_lib:format("Unknown message in " + "httpc_manager:handle_info ~p~n", [Info]), + error_logger:error_report(Report), {noreply, State}. - %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> _ (ignored by gen_server) %% Description: Shutdown the httpc_handler @@ -655,224 +591,79 @@ get_handler_info(Tab) -> {Pid, State} <- Handlers2], Handlers3. - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When the handler has sucessfully been started, -%% this message (started) is sent. -%% - -handle_started(StarterPid, ReqId, HandlerPid, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = initiating} = HandlerInfo] -> - ?hcri("received started ack for initiating handler", []), - %% As a last resort, make sure we know when it exits, - %% in case it forgets to notify us. - %% We dont need to know the ref id? - erlang:monitor(process, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid, - state = started}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] -> - error_report(Profile, - "unexpected (started) message for handler (~p) in state " - "~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]), - ?hcri("received unexpected started message", [{state, State}]), - ok; - - [] -> - error_report(Profile, - "unknown handler ~p (~p) started for request ~w - canceling", - [HandlerPid, StarterPid, ReqId]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When that process terminates it sends -%% one of two messages. These ara handled by the two functions -%% below. -%% - -handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = started} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for started handler", []), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid, - state = operational}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = canceled} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for canceled handler", []), - httpc_handler:cancel(ReqId, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] when Result =/= ok -> - error_report(Profile, - "handler (~p, ~w) failed to connect and/or " - "send request ~p" - "~n Result: ~p", - [HandlerPid, State, ReqId, Result]), - ?hcri("received connect-and-send error", - [{result, Result}, {state, State}]), - %% We don't need to send a response to the original caller - %% because the handler already sent one in its terminate - %% function. - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - ?hcri("handler successfully started " - "for unknown request => canceling", - [{profile, Profile}, - {handler, HandlerPid}, - {request, ReqId}]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -handle_failed_starting_handler(_StarterPid, ReqId, Error, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = canceled}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - request_canceled(Profile, ReqId), % Fake signal from handler - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{from = From}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - Reason2 = - case Error of - {error, Reason} -> - {failed_connecting, Reason}; - _ -> - {failed_connecting, Error} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - error_report(Profile, - "failed starting handler for unknown request ~p" - "~n Error: ~p", [ReqId, Error]), - ok - end. - - -maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) -> - Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'}, - case ets:match_object(HandlerDb, Pattern) of - [#handler_info{id = ReqId, from = From, state = initiating}] -> - %% The starter process crashed before it could start the - %% the handler process, therefor we need to answer the - %% original caller. - ?hcri("starter process crashed bfore starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}]), - Reason2 = - case Reason of - {error, Error} -> - {failed_connecting, Error}; - _ -> - {failed_connecting, Reason} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{state = State} = HandlerInfo] -> - %% The starter process crashed after the handler was started. - %% The handler will answer to the original caller. - ?hcri("starter process crashed after starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - _ -> - ok - end. - - -%% ----- -%% Act as an HTTP/0.9 client that does not know anything -%% about persistent connections handle_request(#request{settings = - #http_options{version = "HTTP/0.9"}} = Request0, + #http_options{version = "HTTP/0.9"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = undefined}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - -%% ----- -%% Act as an HTTP/1.0 client that does not -%% use persistent connections + %% Act as an HTTP/0.9 client that does not know anything + %% about persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = undefined}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + handle_request(#request{settings = - #http_options{version = "HTTP/1.0"}} = Request0, + #http_options{version = "HTTP/1.0"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - - -%% ----- -handle_request(#request{method = Method, - address = Address, - scheme = Scheme} = Request0, - #state{options = Opts} = State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - SessionType = session_type(Opts), - case select_session(Method, Address, Scheme, SessionType, State) of + %% Act as an HTTP/1.0 client that does not + %% use persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + +handle_request(Request, State = #state{options = Options}) -> + + NewRequest = handle_cookies(generate_request_id(Request), State), + SessionType = session_type(Options), + case select_session(Request#request.method, + Request#request.address, + Request#request.scheme, SessionType, State) of {ok, HandlerPid} -> - pipeline_or_keep_alive(Request1, HandlerPid, State); + pipeline_or_keep_alive(NewRequest, HandlerPid, State); no_connection -> - create_handler_starter(Request1, State); - {no_session, OpenSessions} - when OpenSessions < Opts#options.max_sessions -> - create_handler_starter(Request1, State); + start_handler(NewRequest, State); + {no_session, OpenSessions} when OpenSessions + < Options#options.max_sessions -> + start_handler(NewRequest, State); {no_session, _} -> %% Do not start any more persistent connections %% towards this server. - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State) + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State) end, - {ok, Request1#request.id, State}. + {reply, {ok, NewRequest#request.id}, State}. + + +start_handler(Request, State) -> + {ok, Pid} = + case is_inets_manager() of + true -> + httpc_handler_sup:start_child([whereis(httpc_handler_sup), + Request, State#state.options, + State#state.profile_name]); + false -> + httpc_handler:start_link(self(), Request, State#state.options, + State#state.profile_name) + end, + ets:insert(State#state.handler_db, {Request#request.id, + Pid, Request#request.from}), + erlang:monitor(process, Pid). select_session(Method, HostPort, Scheme, SessionType, #state{options = #options{max_pipeline_length = MaxPipe, max_keep_alive_length = MaxKeepAlive}, session_db = SessionDb}) -> - ?hcrd("select session", [{session_type, SessionType}, - {max_pipeline_length, MaxPipe}, + ?hcrd("select session", [{session_type, SessionType}, + {max_pipeline_length, MaxPipe}, {max_keep_alive_length, MaxKeepAlive}]), case httpc_request:is_idempotent(Method) orelse (SessionType =:= keep_alive) of @@ -918,92 +709,17 @@ select_session(Candidates, Max) -> ?hcrd("select session - found one", [{handler, HandlerPid}]), {ok, HandlerPid} end. - -pipeline_or_keep_alive(#request{id = Id} = Request, HandlerPid, State) -> - ?hcrd("pipeline of keep-alive", [{id, Id}, {handler, HandlerPid}]), + +pipeline_or_keep_alive(Request, HandlerPid, State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ?hcrd("pipeline or keep-alive - successfully sent", []), - Entry = #handler_info{id = Id, - handler = HandlerPid, - state = operational}, - ets:insert(State#state.handler_db, Entry); - - _ -> %% timeout pipelining failed - ?hcrd("pipeline or keep-alive - failed sending -> " - "start a new handler", []), - create_handler_starter(Request, State) + ets:insert(State#state.handler_db, {Request#request.id, + HandlerPid, + Request#request.from}); + _ -> %timeout pipelining failed + start_handler(Request, State) end. - -create_handler_starter(#request{socket_opts = SocketOpts} = Request, - #state{options = Options} = State) - when is_list(SocketOpts) -> - %% The user provided us with (override) socket options - ?hcrt("create handler starter", [{socket_opts, SocketOpts}, {options, Options}]), - Options2 = Options#options{socket_opts = SocketOpts}, - create_handler_starter(Request#request{socket_opts = undefined}, - State#state{options = Options2}); - -create_handler_starter(#request{id = Id, - from = From} = Request, - #state{profile_name = ProfileName, - options = Options, - handler_db = HandlerDb} = _State) -> - ?hcrv("create handler starter", [{id, Id}, {profile, ProfileName}]), - IsInetsManager = is_inets_manager(), - ManagerPid = self(), - StarterFun = - fun() -> - ?hcrd("handler starter - start", - [{id, Id}, - {profile, ProfileName}, - {inets_manager, IsInetsManager}]), - Result1 = - case IsInetsManager of - true -> - httpc_handler_sup:start_child(Options, - ProfileName); - false -> - httpc_handler:start_link(Options, - ProfileName) - end, - ?hcrd("handler starter - maybe connect and send", - [{id, Id}, {profile, ProfileName}, {result, Result1}]), - case Result1 of - {ok, HandlerPid} -> - StartedMessage = - {started, self(), Id, HandlerPid}, - ManagerPid ! StartedMessage, - Result2 = httpc_handler:connect_and_send(Request, - HandlerPid), - ?hcrd("handler starter - connected and sent", - [{id, Id}, {profile, ProfileName}, - {handler, HandlerPid}, {result, Result2}]), - ConnAndSendMessage = - {connect_and_send, - self(), Id, HandlerPid, Result2}, - ManagerPid ! ConnAndSendMessage; - {error, Reason} -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Reason}, - ManagerPid ! StartFailureMessage; - _ -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Result1}, - ManagerPid ! StartFailureMessage - end - end, - Starter = erlang:spawn_link(StarterFun), - ?hcrd("create handler starter - started", [{id, Id}, {starter, Starter}]), - Entry = #handler_info{id = Id, - starter = Starter, - from = From, - state = initiating}, - ets:insert(HandlerDb, Entry), - ok. - - is_inets_manager() -> case get('$ancestors') of [httpc_profile_sup | _] -> @@ -1045,8 +761,6 @@ do_store_cookies([Cookie | Cookies], #state{cookie_db = CookieDb} = State) -> ok = httpc_cookie:insert(CookieDb, Cookie), do_store_cookies(Cookies, State). - - session_db_name(ProfileName) -> make_db_name(ProfileName, "__session_db"). @@ -1074,7 +788,6 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). - get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). @@ -1133,20 +846,6 @@ handle_verbose(trace) -> handle_verbose(_) -> ok. - error_report(Profile, F, A) -> Report = io_lib:format("HTTPC-MANAGER<~p> " ++ F ++ "~n", [Profile | A]), error_logger:error_report(Report). - - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index d4df97ad40..879053f0f2 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -79,36 +79,62 @@ send(SendAddr, Socket, SocketType, {settings, HttpOptions}, {userinfo, UserInfo}]), - TmpHeaders = handle_user_info(UserInfo, Headers), + TmpHdrs = handle_user_info(UserInfo, Headers), - {TmpHeaders2, Body} = - post_data(Method, TmpHeaders, Content, HeadersAsIs), + {TmpHdrs2, Body} = post_data(Method, TmpHdrs, Content, HeadersAsIs), - {NewHeaders, Uri} = case Address of - SendAddr -> - {TmpHeaders2, Path ++ Query}; - _Proxy -> - TmpHeaders3 = - handle_proxy(HttpOptions, TmpHeaders2), - {TmpHeaders3, AbsUri} - end, - - FinalHeaders = case NewHeaders of - HeaderList when is_list(HeaderList) -> - http_headers(HeaderList, []); - _ -> - http_request:http_headers(NewHeaders) - end, + {NewHeaders, Uri} = + case Address of + SendAddr -> + {TmpHdrs2, Path ++ Query}; + _Proxy -> + TmpHdrs3 = handle_proxy(HttpOptions, TmpHdrs2), + {TmpHdrs3, AbsUri} + end, + + FinalHeaders = + case NewHeaders of + HeaderList when is_list(HeaderList) -> + http_headers(HeaderList, []); + _ -> + http_request:http_headers(NewHeaders) + end, Version = HttpOptions#http_options.version, - Message = [method(Method), " ", Uri, " ", - version(Version), ?CRLF, - headers(FinalHeaders, Version), ?CRLF, Body], + do_send_body(SocketType, Socket, Method, Uri, Version, FinalHeaders, Body). + + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send", [{acc, Acc}]), + case do_send_body(SocketType, Socket, Method, Uri, Version, Headers, []) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, Acc); + Error -> + Error + end; +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> + ?hcrt("create message", [{body, Body}]), + Message = [method(Method), " ", Uri, " ", + version(Version), ?CRLF, + headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - - http_transport:send(SocketType, Socket, lists:append(Message)). + http_transport:send(SocketType, Socket, Message). + +do_send_body(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + case http_transport:send(SocketType, Socket, Data) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. %%------------------------------------------------------------------------- @@ -161,7 +187,6 @@ is_client_closing(Headers) -> %%%======================================================================== post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) when (Method =:= post) orelse (Method =:= put) -> - ContentLength = body_length(Body), NewBody = case Headers#http_request_h.expect of "100-continue" -> ""; @@ -170,14 +195,22 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) end, NewHeaders = case HeadersAsIs of - [] -> - Headers#http_request_h{'content-type' = - ContentType, - 'content-length' = - ContentLength}; - _ -> - HeadersAsIs - end, + [] -> + Headers#http_request_h{ + 'content-type' = ContentType, + 'content-length' = case body_length(Body) of + undefined -> + % on upload streaming the caller must give a + % value to the Content-Length header + % (or use chunked Transfer-Encoding) + Headers#http_request_h.'content-length'; + Len when is_list(Len) -> + Len + end + }; + _ -> + HeadersAsIs + end, {NewHeaders, NewBody}; @@ -190,7 +223,10 @@ body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); body_length(Body) when is_list(Body) -> - integer_to_list(length(Body)). + integer_to_list(length(Body)); + +body_length({DataFun, _Acc}) when is_function(DataFun, 1) -> + undefined. method(Method) -> http_util:to_upper(atom_to_list(Method)). diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl index 5440f214b5..2e924667c6 100644 --- a/lib/inets/src/http_lib/http_internal.hrl +++ b/lib/inets/src/http_lib/http_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,8 +28,8 @@ -define(HTTP_MAX_URI_SIZE, nolimit). -ifndef(HTTP_DEFAULT_SSL_KIND). --define(HTTP_DEFAULT_SSL_KIND, ossl). -%% -define(HTTP_DEFAULT_SSL_KIND, essl). +%% -define(HTTP_DEFAULT_SSL_KIND, ossl). +-define(HTTP_DEFAULT_SSL_KIND, essl). -endif. % -ifdef(HTTP_DEFAULT_SSL_KIND). diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 0024d19fc1..8cabfe3c71 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -23,7 +23,7 @@ -export([ start/1, connect/3, connect/4, - listen/2, listen/3, + listen/2, listen/3, listen/4, accept/2, accept/3, close/2, send/3, @@ -110,7 +110,17 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], ?hlrt("connect using gen_tcp", [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]), - gen_tcp:connect(Host, Port, Opts, Timeout); + try gen_tcp:connect(Host, Port, Opts, Timeout) of + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + catch + exit:{badarg, _} -> + {error, {eoptions, Opts}}; + exit:badarg -> + {error, {eoptions, Opts}} + end; %% Wrapper for backaward compatibillity connect({ssl, SslConfig}, Address, Opts, Timeout) -> @@ -123,7 +133,14 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout); + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end; connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, @@ -132,14 +149,22 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout). + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end. %%------------------------------------------------------------------------- -%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason} +%% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason} %% SocketType = ip_comm | {ssl, SSLConfig} %% Port = integer() -%% Socket = socket() +%% Socket = socket() +%% Fd = undefined | fd() %% %% Description: Sets up socket to listen on the port Port on the local %% host using either gen_tcp or ssl. In the gen_tcp case the port @@ -151,13 +176,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm, Addr, Port) -> - case (catch listen_ip_comm(Addr, Port)) of - {'EXIT', Reason} -> - {error, {exit, Reason}}; - Else -> - Else - end; +listen(ip_comm = SocketType, Addr, Port) -> + listen(SocketType, Addr, Port, undefined); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port) -> @@ -186,9 +206,17 @@ listen({essl, SSLConfig} = Ssl, Addr, Port) -> Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt], ssl:listen(Port, Opt2). +listen(ip_comm, Addr, Port, Fd) -> + case (catch listen_ip_comm(Addr, Port, Fd)) of + {'EXIT', Reason} -> + {error, {exit, Reason}}; + Else -> + Else + end. + -listen_ip_comm(Addr, Port) -> - {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port), +listen_ip_comm(Addr, Port, Fd) -> + {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts], @@ -223,29 +251,36 @@ listen_ip_comm(Addr, Port) -> ipfamily_default(Addr, Port) -> httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). -get_socket_info(Addr, Port) -> - Key = list_to_atom("httpd_" ++ integer_to_list(Port)), - BaseOpts = [{backlog, 128}, {reuseaddr, true}], +get_socket_info(Addr, Port, Fd0) -> + BaseOpts = [{backlog, 128}, {reuseaddr, true}], IpFamilyDefault = ipfamily_default(Addr, Port), - case init:get_argument(Key) of - {ok, [[Value]]} -> - {Fd, IpFamily} = - case string:tokens(Value, [$|]) of - [FdStr, IpFamilyStr] -> - Fd0 = fd_of(FdStr), - IpFamily0 = ip_family_of(IpFamilyStr), - {Fd0, IpFamily0}; - [FdStr] -> - {fd_of(FdStr), IpFamilyDefault}; - _ -> - throw({error, {bad_descriptor, Value}}) - end, + %% The presence of a file descriptor takes precedence + case get_fd(Port, Fd0, IpFamilyDefault) of + {Fd, IpFamily} -> {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily}; - error -> + undefined -> {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} end. +get_fd(Port, undefined = _Fd, IpFamilyDefault) -> + FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), + case init:get_argument(FdKey) of + {ok, [[Value]]} -> + case string:tokens(Value, [$|]) of + [FdStr, IpFamilyStr] -> + {fd_of(FdStr), ip_family_of(IpFamilyStr)}; + [FdStr] -> + {fd_of(FdStr), IpFamilyDefault}; + _ -> + throw({error, {bad_descriptor, Value}}) + end; + error -> + undefined + end; +get_fd(_Port, Fd, IpFamilyDefault) -> + {Fd, IpFamilyDefault}. + fd_of(FdStr) -> case (catch list_to_integer(FdStr)) of Fd when is_integer(Fd) -> diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index 4f1147176c..5e6b69ac5e 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -25,7 +25,8 @@ hexlist_to_integer/1, integer_to_hexlist/1, convert_month/1, is_hostname/1, - timestamp/0, timeout/2 + timestamp/0, timeout/2, + html_encode/1 ]). @@ -187,6 +188,13 @@ timeout(Timeout, Started) -> end. +html_encode(Chars) -> + Reserved = sets:from_list([$&, $<, $>, $\", $', $/]), + lists:append(lists:map(fun(Char) -> + char_to_html_entity(Char, Reserved) + end, Chars)). + + %%%======================================================================== %%% Internal functions %%%======================================================================== @@ -235,3 +243,11 @@ convert_to_ascii([Num | Reversed], Number) convert_to_ascii([Num | Reversed], Number) when (Num > 9) andalso (Num < 16) -> convert_to_ascii(Reversed, [Num + 55 | Number]). + +char_to_html_entity(Char, Reserved) -> + case sets:is_element(Char, Reserved) of + true -> + "&#" ++ integer_to_list(Char) ++ ";"; + false -> + [Char] + end. diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl index 7e21d9e158..ccc1f7874a 100644 --- a/lib/inets/src/http_server/httpd_file.erl +++ b/lib/inets/src/http_server/httpd_file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,22 +26,21 @@ handle_error(eacces, Op, ModData, Path) -> - handle_error(403, Op, ModData, Path,"Forbidden"); + handle_error(403, Op, ModData, Path, ": Forbidden"); handle_error(enoent, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path,"File not found"); + handle_error(404, Op, ModData, Path, ": File not found"); handle_error(enotdir, Op, ModData, Path) -> handle_error(404, Op, ModData, Path, - ": A component of the file name is not a directory"); + ": A component of the file name is not a directory"); handle_error(emfile, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": To many open files"); handle_error({enfile,_}, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": File table overflow"); handle_error(_Reason, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path, "File not found"). - -handle_error(StatusCode, Op, none, Path, Reason) -> - {StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)}; + handle_error(404, Op, ModData, Path, ": File not found"). +handle_error(StatusCode, Op, none, Path, Reason) -> + {StatusCode, none, ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}; handle_error(StatusCode, Op, ModData, Path, Reason) -> {StatusCode, ModData#mod.request_uri, - ?NICE("Can't " ++ Op ++ Path ++ Reason)}. + ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}. diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index f3ea3aa0e2..db1e2c627a 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -113,7 +113,7 @@ do_error_entry(ConfigDB, RemoteHost, undefined, Date, Reason) -> do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) -> case httpd_util:lookup(ConfigDB, error_log_format, pretty) of pretty -> - io_lib:format("[~s] access to ~s failed for ~s reason: ~n~p~n", + io_lib:format("[~s] access to ~s failed for ~s, reason: ~n~p~n", [Date, URI, RemoteHost, Reason]); compact -> io_lib:format( "[~s] access to ~s failed for ~s, reason: ~w~n", diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index a9db6e2058..c3b47ce390 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -240,13 +240,13 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Timeouts handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> - error_log("No request received on keep-alive connection" + error_log("No request received on keep-alive connection " "before server side timeout", ModData), %% No response should be sent! {stop, normal, State#state{response_sent = true}}; handle_info(timeout, #state{mod = ModData} = State) -> httpd_response:send_status(ModData, 408, "Request timeout"), - error_log("The client did not send the whole request before the" + error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index f94e5459c1..d028a19bf0 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -90,7 +90,7 @@ id(Address, Port) -> %%% Supervisor callback %%%========================================================================= init([HttpdServices]) -> - ?hdrd("starting", []), + ?hdrd("starting", [{httpd_service, HttpdServices}]), RestartStrategy = one_for_one, MaxR = 10, MaxT = 3600, @@ -182,24 +182,32 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> Error end. -httpd_child_spec(Config, AcceptTimeout, Debug, Addr, 0) -> - case start_listen(Addr, 0, Config) of - {Pid, {NewPort, NewConfig, ListenSocket}} -> - Name = {httpd_instance_sup, Addr, NewPort}, - StartFunc = {httpd_instance_sup, start_link, - [NewConfig, AcceptTimeout, - {Pid, ListenSocket}, Debug]}, - Restart = permanent, - Shutdown = infinity, - Modules = [httpd_instance_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}; - {Pid, {error, Reason}} -> - exit(Pid, normal), - {error, Reason} - end; - httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> + case Port == 0 orelse proplists:is_defined(fd, Config) of + true -> + httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); + false -> + httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) + end. + +httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) -> + case start_listen(Addr, Port, Config) of + {Pid, {NewPort, NewConfig, ListenSocket}} -> + Name = {httpd_instance_sup, Addr, NewPort}, + StartFunc = {httpd_instance_sup, start_link, + [NewConfig, AcceptTimeout, + {Pid, ListenSocket}, Debug]}, + Restart = permanent, + Shutdown = infinity, + Modules = [httpd_instance_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}; + {Pid, {error, Reason}} -> + exit(Pid, normal), + {error, Reason} + end. + +httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) -> Name = {httpd_instance_sup, Addr, Port}, StartFunc = {httpd_instance_sup, start_link, [Config, AcceptTimeout, Debug]}, @@ -224,7 +232,8 @@ listen(Address, Port, Config) -> SocketType = proplists:get_value(socket_type, Config, ip_comm), case http_transport:start(SocketType) of ok -> - case http_transport:listen(SocketType, Address, Port) of + Fd = proplists:get_value(fd, Config), + case http_transport:listen(SocketType, Address, Port, Fd) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), {ok, NewPort} = inet:port(ListenSocket), diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 789f12652b..c1aff65d5e 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -181,7 +181,7 @@ message(304, _URL,_) -> message(400,none,_) -> "Your browser sent a query that this server could not understand."; message(400,Msg,_) -> - "Your browser sent a query that this server could not understand. "++ maybe_encode(Msg); + "Your browser sent a query that this server could not understand. "++ http_util:html_encode(Msg); message(401,none,_) -> "This server could not verify that you are authorized to access the document you @@ -190,48 +190,48 @@ credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required."; message(403,RequestURI,_) -> - "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server."; + "You don't have permission to access "++ http_util:html_encode(RequestURI) ++" on this server."; message(404,RequestURI,_) -> - "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server."; + "The requested URL " ++ http_util:html_encode(RequestURI) ++ " was not found on this server."; message(408, Timeout, _) -> Timeout; message(412,none,_) -> - "The requested preconditions where false"; + "The requested preconditions were false"; message(413, Reason,_) -> - "Entity: " ++ Reason; + "Entity: " ++ http_util:html_encode(Reason); message(414,ReasonPhrase,_) -> - "Message "++ ReasonPhrase ++"."; + "Message "++ http_util:html_encode(ReasonPhrase) ++"."; message(416,ReasonPhrase,_) -> - ReasonPhrase; + http_util:html_encode(ReasonPhrase); message(500,_,ConfigDB) -> ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"), "The server encountered an internal error or " "misconfiguration and was unable to complete " "your request.<P>Please contact the server administrator " - ++ ServerAdmin ++ ", and inform them of the time the error occurred " + ++ http_util:html_encode(ServerAdmin) ++ ", and inform them of the time the error occurred " "and anything you might have done that may have caused the error."; message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) -> if is_atom(Method) -> - atom_to_list(Method)++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."; + http_util:html_encode(atom_to_list(Method))++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported."; is_list(Method) -> - Method++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported." + http_util:html_encode(Method)++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported." end; message(503, String, _ConfigDB) -> - "This service in unavailable due to: "++String. + "This service in unavailable due to: "++ http_util:html_encode(String). maybe_encode(URI) -> - case lists:member($%, URI) of - true -> - URI; - false -> - http_uri:encode(URI) - end. + Decoded = try http_uri:decode(URI) of + N -> N + catch + error:_ -> URI + end, + http_uri:encode(Decoded). %%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}} diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..e36c33b282 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -415,7 +415,7 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> end; timeout -> ?hdrv("deliver_webpage_chunk - timeout", []), - send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]), + send_headers(ModData, 504, [{"connection", "close"}]), httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket), process_flag(trap_exit,false), {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]} @@ -452,6 +452,10 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) -> ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]), httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend), receive + {esi_data, Data} when is_binary(Data) -> + ?hdrt("handle_body - received binary data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), + IsDisableChunkedSend); {esi_data, Data} -> ?hdrt("handle_body - received data (esi)", []), handle_body(Pid, ModData, Data, Timeout, Size + length(Data), diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..47f3fbba58 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,9 +18,14 @@ {"%VSN%", [ + {"5.5.2", + [ + {restart_application, inets} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {restart_application, inets} ] }, {"5.5", @@ -34,10 +39,15 @@ ] } ], - [ + [ + {"5.5.2", + [ + {restart_application, inets} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {restart_application, inets} ] }, {"5.5", diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 4bafdbfef8..17e5f6777e 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -57,35 +57,42 @@ %% Description: Returns documentation/test cases in this test suite %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, solaris8_test}, {group, solaris9_test}, - {group, solaris10_test}, {group, linux_x86_test}, - {group, linux_ppc_test}, {group, macosx_x86_test}, - {group, macosx_ppc_test}, {group, openbsd_test}, - {group, freebsd_test}, {group, netbsd_test}, + [ + {group, solaris8_test}, + {group, solaris9_test}, + {group, solaris10_test}, + {group, linux_x86_test}, + {group, linux_ppc_test}, + {group, macosx_x86_test}, + {group, macosx_ppc_test}, + {group, openbsd_test}, + {group, freebsd_test}, + {group, netbsd_test}, {group, windows_xp_test}, {group, windows_2003_server_test}, - {group, ticket_tests}]. + {group, ticket_tests} + ]. groups() -> - [{solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, - {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, - {solaris10_test, [], - [{ftp_solaris10_sparc_test, all}, - {ftp_solaris10_x86_test, all}]}, - {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, - {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, - {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, - {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, - {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, - {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, - {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, - {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, - {windows_2003_server_test, [], - [{ftp_windows_2003_server_test, all}]}, - {ticket_tests, [], [{ftp_ticket_test, all}]}]. + [ + {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, + {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, + {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, + {ftp_solaris10_x86_test, all}]}, + {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, + {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, + {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, + {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, + {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, + {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, + {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, + {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, + {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, + {ticket_tests, [], [{ftp_ticket_test, all}]} + ]. init_per_group(_GroupName, Config) -> Config. diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 2c8febf5ed..1998bd3950 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -28,6 +28,7 @@ -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). +-include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -62,36 +63,84 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [proxy_options, proxy_head, proxy_get, proxy_trace, - proxy_post, proxy_put, proxy_delete, proxy_auth, - proxy_headers, proxy_emulate_lower_versions, - http_options, http_head, http_get, http_post, - http_dummy_pipe, http_inets_pipe, http_trace, - http_async, http_save_to_file, http_save_to_file_async, - http_headers, http_headers_dummy, http_bad_response, - ssl_head, ossl_head, essl_head, ssl_get, ossl_get, - essl_get, ssl_trace, ossl_trace, essl_trace, - http_redirect, http_redirect_loop, - http_internal_server_error, http_userinfo, http_cookie, - http_server_does_not_exist, http_invalid_http, - http_emulate_lower_versions, http_relaxed, - page_does_not_exist, proxy_page_does_not_exist, - proxy_https_not_supported, http_stream, - http_stream_once, proxy_stream, parse_url, options, - ipv6, headers_as_is, {group, tickets}]. + [ + proxy_options, + proxy_head, + proxy_get, + proxy_trace, + proxy_post, + proxy_put, + proxy_delete, + proxy_auth, + proxy_headers, + proxy_emulate_lower_versions, + http_options, + http_head, + http_get, + http_post, + http_post_streaming, + http_dummy_pipe, + http_inets_pipe, + http_trace, + http_async, + http_save_to_file, + http_save_to_file_async, + http_headers, + http_headers_dummy, + http_bad_response, + ssl_head, + ossl_head, + essl_head, + ssl_get, + ossl_get, + essl_get, + ssl_trace, + ossl_trace, + essl_trace, + http_redirect, + http_redirect_loop, + http_internal_server_error, + http_userinfo, http_cookie, + http_server_does_not_exist, + http_invalid_http, + http_emulate_lower_versions, + http_relaxed, + page_does_not_exist, + proxy_page_does_not_exist, + proxy_https_not_supported, + http_stream, + http_stream_once, + proxy_stream, + parse_url, + options, + ipv6, + headers_as_is, + {group, tickets}, + initial_server_connect + ]. groups() -> - [{tickets, [], - [hexed_query_otp_6191, empty_body_otp_6243, - empty_response_header_otp_6830, - transfer_encoding_otp_6807, proxy_not_modified_otp_6821, - no_content_204_otp_6982, missing_CR_otp_7304, - {group, otp_7883}, {group, otp_8154}, {group, otp_8106}, - otp_8056, otp_8352, otp_8371, otp_8739]}, - {otp_7883, [], [otp_7883_1, otp_7883_2]}, + [{tickets, [], [hexed_query_otp_6191, + empty_body_otp_6243, + empty_response_header_otp_6830, + transfer_encoding_otp_6807, + proxy_not_modified_otp_6821, + no_content_204_otp_6982, + missing_CR_otp_7304, + {group, otp_7883}, + {group, otp_8154}, + {group, otp_8106}, + otp_8056, + otp_8352, + otp_8371, + otp_8739]}, + {otp_7883, [], [otp_7883_1, + otp_7883_2]}, {otp_8154, [], [otp_8154_1]}, - {otp_8106, [], - [otp_8106_pid, otp_8106_fun, otp_8106_mfa]}]. + {otp_8106, [], [otp_8106_pid, + otp_8106_fun, + otp_8106_mfa]}]. + init_per_group(_GroupName, Config) -> Config. @@ -138,6 +187,7 @@ init_per_suite(Config) -> {local_port, ?IP_PORT}, {local_ssl_port, ?SSL_PORT} | Config]. + %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -165,6 +215,20 @@ end_per_suite(Config) -> %%-------------------------------------------------------------------- init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); + +init_per_testcase(initial_server_connect, Config) -> + %% Try to check if crypto actually exist or not, + %% this test case does not work unless it does + case (catch crypto:start()) of + ok -> + application:start(public_key), + application:start(ssl), + inets:start(), + Config; + _ -> + {skip,"Could not start crypto"} + end; + init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). @@ -180,8 +244,8 @@ init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> [{local_ssl_server, Server} | Config2]. init_per_testcase(Case, Timeout, Config) -> - io:format(user, "~n~n*** INIT ~w:[~w][~w] ***~n~n", - [?MODULE, Timeout, Case]), + io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", + [?MODULE, Case, Timeout]), PrivDir = ?config(priv_dir, Config), tsp("init_per_testcase -> stop inets"), application:stop(inets), @@ -205,9 +269,10 @@ init_per_testcase(Case, Timeout, Config) -> [$e, $s, $s, $l | _] -> init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); - "proxy" ++ Rest -> + "proxy_" ++ Rest -> + io:format("init_per_testcase -> Rest: ~p~n", [Rest]), case Rest of - "_https_not_supported" -> + "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), tsp("init_per_testcase -> [proxy case] start ssl"), @@ -221,13 +286,39 @@ init_per_testcase(Case, Timeout, Config) -> | TmpConfig] end; _ -> + %% We use erlang.org for the proxy tests + %% and after the switch to erlang-web, many + %% of the test cases no longer work (erlang.org + %% previously run on Apache). + %% Until we have had time to update inets + %% (and updated erlang.org to use that inets) + %% and the test cases, we simply skip the + %% problematic test cases. + %% This is not ideal, but I am busy.... case is_proxy_available(?PROXY, ?PROXY_PORT) of true -> - inets:start(), - [{watchdog, Dog} | TmpConfig]; + BadCases = + [ + "delete", + "get", + "head", + "not_modified_otp_6821", + "options", + "page_does_not_exist", + "post", + "put", + "stream" + ], + case lists:member(Rest, BadCases) of + true -> + [{skip, "TC and server not compatible"}| + TmpConfig]; + false -> + inets:start(), + [{watchdog, Dog} | TmpConfig] + end; false -> - [{skip, "Failed to contact proxy"} | - TmpConfig] + [{skip, "proxy not responding"} | TmpConfig] end end; _ -> @@ -395,6 +486,53 @@ http_post(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- +http_post_streaming(doc) -> + ["Test streaming http post request against local server. " + "We only care about the client side of the the post. " + "The server script will not actually use the post data."]; +http_post_streaming(suite) -> + []; +http_post_streaming(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = case test_server:os_type() of + {win32, _} -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo.exe"; + _ -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo" + end, + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + io:format("~w:http_post_streaming_fun -> " + "zero~n", [?MODULE]), + eof; + (LenLeft) -> + io:format("~w:http_post_streaming_fun -> " + "LenLeft: ~p~n", [?MODULE, LenLeft]), + {ok, lists:duplicate(10, "1"), LenLeft - 10} + end, + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "100"}], + "text/plain", {BodyFun, 100}}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "10"}], + "text/plain", {BodyFun, 10}}, [], []); + + _ -> + {skip, "Failed to start local http-server"} + end. + + +%%------------------------------------------------------------------------- http_emulate_lower_versions(doc) -> ["Perform request as 0.9 and 1.0 clients."]; http_emulate_lower_versions(suite) -> @@ -478,34 +616,35 @@ http_inets_pipe(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. + test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1"), + {ok, RequestId1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + p("test_pipeline -> RequestId1: ~p", [RequestId1]), - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + p("test_pipeline -> issue (async) request 2"), + {ok, RequestId2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), + tsp("RequestId2: ~p", [RequestId2]), + p("test_pipeline -> RequestId2: ~p", [RequestId2]), - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive + receive {http, {RequestId1, {{_, 200, _}, _, _}}} -> p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), receive @@ -523,46 +662,46 @@ test_pipeline(URL) -> ok; {http, Msg2} -> test_server:fail(Msg2) - end; + end; {http, Msg3} -> test_server:fail(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - test_server:fail({error, {timeout, Any1}}) - end + after 60000 -> + receive Any1 -> + tsp("received crap after timeout: ~n ~p", [Any1]), + test_server:fail({error, {timeout, Any1}}) + end end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), + p("test_pipeline -> sleep some"), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + p("test_pipeline -> issue (async) request 4"), + {ok, RequestId3} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - test_server:fail(http_cancel_request_failed) - after 3000 -> - ok - end, + tsp("RequestId3: ~p", [RequestId3]), + p("test_pipeline -> RequestId3: ~p", [RequestId3]), - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> + p("test_pipeline -> issue (async) request 5"), + {ok, RequestId4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId4: ~p~n", [RequestId4]), + p("test_pipeline -> RequestId4: ~p", [RequestId4]), + + p("test_pipeline -> cancel (async) request 4"), + ok = httpc:cancel_request(RequestId3), + + p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request 4"), + Body = + receive + {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> p("test_pipeline -> received reply for (async) request 5"), tsp("Receive : ~p", [Res]), BinBody4; @@ -577,9 +716,9 @@ test_pipeline(URL) -> p("test_pipeline -> check reply for (async) request 5"), inets_test_lib:check_body(binary_to_list(Body)), - + p("test_pipeline -> ensure no unexpected incomming"), - receive + receive {http, Any} -> test_server:fail({unexpected_message, Any}) after 500 -> @@ -589,8 +728,6 @@ test_pipeline(URL) -> p("test_pipeline -> done"), ok. - - %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1253,6 +1390,9 @@ proxy_options(doc) -> proxy_options(suite) -> []; proxy_options(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets, which + %% do not implement "options". case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1277,6 +1417,8 @@ proxy_head(doc) -> proxy_head(suite) -> []; proxy_head(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1372,6 +1514,8 @@ proxy_post(doc) -> proxy_post(suite) -> []; proxy_post(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1394,6 +1538,8 @@ proxy_put(doc) -> proxy_put(suite) -> []; proxy_put(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1418,6 +1564,8 @@ proxy_delete(doc) -> proxy_delete(suite) -> []; proxy_delete(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", @@ -1541,25 +1689,11 @@ proxy_https_not_supported(suite) -> proxy_https_not_supported(Config) when is_list(Config) -> Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []), case Result of - {error, Reason} -> - %% ok so far - case Reason of - {failed_connecting, Why} -> - %% ok, now check why - case Why of - https_through_proxy_is_not_currently_supported -> - ok; - _ -> - tsf({unexpected_why, Why}) - end; - _ -> - tsf({unexpected_reason, Reason}) - end; + {error, https_through_proxy_is_not_currently_supported} -> + ok; _ -> - tsf({unexpected_result, Result}) - end, - ok. - + tsf({unexpected_reason, Result}) + end. %%------------------------------------------------------------------------- @@ -2312,7 +2446,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> {skip, "Failed to start local http-server"} - end. + end. otp_8106_mfa(doc) -> @@ -2538,7 +2672,7 @@ otp_8739(Config) when is_list(Config) -> Request = {URL, []}, HttpOptions = [{connect_timeout, 500}, {timeout, 1}], Options = [{sync, true}], - case http:request(Method, Request, HttpOptions, Options) of + case httpc:request(Method, Request, HttpOptions, Options) of {error, timeout} -> %% And now we check the size of the handler db Info = httpc:info(), @@ -2573,7 +2707,7 @@ otp_8739_dummy_server_init(Parent) -> Parent ! {port, Port}, otp_8739_dummy_server_main(Parent, ListenSocket). -otp_8739_dummy_server_main(Parent, ListenSocket) -> +otp_8739_dummy_server_main(_Parent, ListenSocket) -> case gen_tcp:accept(ListenSocket) of {ok, Sock} -> %% Ignore the request, and simply wait for the socket to close @@ -2595,7 +2729,31 @@ otp_8739_dummy_server_main(Parent, ListenSocket) -> exit(Error) end. - +%%------------------------------------------------------------------------- + +initial_server_connect(doc) -> + ["If this test cases times out the init of httpc_handler process is" + "blocking the manager/client process (implementation dependent which) but nither" + "should be blocked."]; +initial_server_connect(suite) -> + []; +initial_server_connect(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + ok = httpc:set_options([{ipfamily, inet}]), + + CertFile = filename:join(DataDir, "ssl_server_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + + {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions), + + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html", + + httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]), + + [{session_cookies,[]}] = httpc:which_cookies(), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]). %%-------------------------------------------------------------------- %% Internal functions @@ -3108,11 +3266,9 @@ pick_header(Headers, Name) -> Val end. - not_implemented_yet() -> exit(not_implemented_yet). - p(F) -> p(F, []). @@ -3126,3 +3282,37 @@ tsp(F, A) -> tsf(Reason) -> test_server:fail(Reason). + + +dummy_ssl_server_hang(Caller, IpV, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + ssl:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt); + ipv6 -> + ssl:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt) + end, + {ok, {_,Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + dummy_ssl_server_hang_loop(AcceptSocket). + +dummy_ssl_server_hang_loop(_) -> + %% Do not do ssl:ssl_accept as we + %% want to time out the underlying gen_tcp:connect + receive + stop -> + ok + end. diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 3e29b68283..f23d0b4765 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -29,7 +29,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [uri_too_long_414, header_too_long_413, escaped_url_in_error_body]. + [ + uri_too_long_414, + header_too_long_413, + escaped_url_in_error_body + ]. groups() -> []. @@ -40,6 +44,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -50,6 +55,8 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + tsp("init_per_suite -> entry with" + "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), HttpdConf = [{port, 0}, {ipfamily, inet}, @@ -64,6 +71,8 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> + tsp("end_per_suite -> entry with" + "~n Config: ~p", [_Config]), inets:stop(), ok. @@ -79,9 +88,12 @@ end_per_suite(_Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- -init_per_testcase(_Case, Config) -> +init_per_testcase(Case, Config) -> + tsp("init_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -90,9 +102,12 @@ init_per_testcase(_Case, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + tsp("end_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- @@ -142,22 +157,30 @@ escaped_url_in_error_body(doc) -> escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + tsp("escaped_url_in_error_body -> entry with" + "~n Config: ~p", [Config]), + HttpdConf = ?config(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), - Address = proplists:get_value(bind_address, Info), - Path = "/<b>this_is_bold<b>", + _Address = proplists:get_value(bind_address, Info), + Path = "/<b>this_is_bold</b>", URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), - {ok, {404, Body}} = httpc:request(get, {URL, []}, - [{url_encode, true}], - [{version, "HTTP/1.0"}, {full_result, false}]), - EscapedPath = find_URL_path(string:tokens(Body, " ")), - {ok, {404, Body1}} = httpc:request(get, {URL, []}, [], - [{version, "HTTP/1.0"}, {full_result, false}]), + {ok, {404, Body1}} = httpc:request(get, {URL, []}, + [{url_encode, true}, + {version, "HTTP/1.0"}], + [{full_result, false}]), EscapedPath = find_URL_path(string:tokens(Body1, " ")), - inets:stop(httpd, Pid). + {ok, {404, Body2}} = httpc:request(get, {URL, []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]), + HTMLEncodedPath = http_util:html_encode(Path), + HTMLEncodedPath = find_URL_path(string:tokens(Body2, " ")), + inets:stop(httpd, Pid), + tsp("escaped_url_in_error_body -> done"), + ok. find_URL_path([]) -> ""; @@ -165,3 +188,10 @@ find_URL_path(["URL", URL | _]) -> URL; find_URL_path([_ | Rest]) -> find_URL_path(Rest). + + +tsp(F) -> + tsp(F, []). +tsp(F, A) -> + test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index f2c1fd6a65..1754cec7bc 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,7 +19,6 @@ %% -module(httpd_mod). --author('[email protected]'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -815,6 +814,8 @@ esi(Type, Port, Host, Node) -> [{statuscode, 302}, {version, "HTTP/1.0"}]), ok. + + %%-------------------------------------------------------------------- get(Type, Port, Host, Node) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Node, diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl index 11b507fa26..49ea18501f 100644 --- a/lib/inets/test/inets_app_test.erl +++ b/lib/inets/test/inets_app_test.erl @@ -241,6 +241,20 @@ undef_funcs(suite) -> undef_funcs(doc) -> []; undef_funcs(Config) when is_list(Config) -> + %% We need to check if there is a point to run this test. + %% On some platforms, crypto will not build, which in turn + %% causes ssl to not to not build (at this time, this will + %% change in the future). + %% So, we first check if we can start crypto, and if not, + %% we skip this test case! + case (catch crypto:start()) of + ok -> + ok; + {error, {already_started, crypto}} -> + ok; + _ -> + ?SKIP(crypto_start_check_failed) + end, App = inets, AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index c56a714f5a..c837326bb5 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -32,7 +32,7 @@ -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). -export([oscmd/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1]). +-export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). @@ -395,6 +395,13 @@ sleep(MSecs) -> skip(Reason, File, Line) -> exit({skipped, {Reason, File, Line}}). +fail(Reason, File, Line) -> + String = lists:flatten(io_lib:format("Failure ~p(~p): ~p~n", + [File, Line, Reason])), + tsf(String). + + + flush() -> receive Msg -> @@ -407,7 +414,7 @@ flush() -> tsp(F) -> tsp(F, []). tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index 0cdb04139c..cc83a309b5 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -72,7 +72,7 @@ %% - Test case macros - --define(SKIP(Reason), inets_test_lib:skip(Reason)). +-define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)). -define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml index 962be63968..11fcc5f387 100644 --- a/lib/jinterface/doc/src/notes.xml +++ b/lib/jinterface/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/kernel/doc/specs/.gitignore b/lib/kernel/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/kernel/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f8c1cac8b3..de10e31d36 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -1,19 +1,20 @@ -# ``The contents of this file are subject to the Erlang Public License, +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in # compliance with the License. You should have received a copy of the # Erlang Public License along with this software. If not, it can be -# retrieved via the world wide web at http://www.erlang.org/. -# +# retrieved online at http://www.erlang.org/. +# # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. -# -# The Initial Developer of the Original Code is Ericsson Utvecklings AB. -# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -# AB. All Rights Reserved.'' -# -# $Id$ +# +# %CopyrightEnd% # include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk @@ -94,19 +95,24 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -125,8 +131,22 @@ clean clean_docs: rm -f $(MAN4DIR)/* rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_erl_prim_loader_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_prim_loader_stub +$(SPECDIR)/specs_erlang_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erlang_stub +$(SPECDIR)/specs_init_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module init_stub +$(SPECDIR)/specs_zlib_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module zlib_stub + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 47d578a339..51a3311ec2 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -50,20 +50,27 @@ <p>Refer to <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> for more information about applications and behaviours.</p> </description> + <datatypes> + <datatype> + <name name="start_type"/> + </datatype> + <datatype> + <name name="restart_type"/> + </datatype> + <datatype> + <!-- Parameterized opaque types are NYI: --> + <name><marker id="type-tuple_of">tuple_of(T)</marker></name> + <desc><p>A tuple where the elements are of type <c>T</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>get_all_env() -> Env</name> - <name>get_all_env(Application) -> Env</name> + <name name="get_all_env" arity="0"/> + <name name="get_all_env" arity="1"/> <fsummary>Get the configuration parameters for an application</fsummary> - <type> - <v>Application = atom()</v> - <v>Env = [{Par,Val}]</v> - <v> Par = atom()</v> - <v> Val = term()</v> - </type> <desc> <p>Returns the configuration parameters and their values for - <c>Application</c>. If the argument is omitted, it defaults to + <c><anno>Application</anno></c>. If the argument is omitted, it defaults to the application of the calling process.</p> <p>If the specified application is not loaded, or if the process executing the call does not belong to any application, @@ -71,18 +78,12 @@ </desc> </func> <func> - <name>get_all_key() -> {ok, Keys} | []</name> - <name>get_all_key(Application) -> {ok, Keys} | undefined </name> + <name name="get_all_key" arity="0"/> + <name name="get_all_key" arity="1"/> <fsummary>Get the application specification keys</fsummary> - <type> - <v>Application = atom()</v> - <v>Keys = [{Key,Val}]</v> - <v> Key = atom()</v> - <v> Val = term()</v> - </type> <desc> <p>Returns the application specification keys and their values - for <c>Application</c>. If the argument is omitted, it + for <c><anno>Application</anno></c>. If the argument is omitted, it defaults to the application of the calling process.</p> <p>If the specified application is not loaded, the function returns <c>undefined</c>. If the process executing the call @@ -91,17 +92,12 @@ </desc> </func> <func> - <name>get_application() -> {ok, Application} | undefined</name> - <name>get_application(Pid | Module) -> {ok, Application} | undefined</name> + <name name="get_application" arity="0"/> + <name name="get_application" arity="1"/> <fsummary>Get the name of an application containing a certain process or module</fsummary> - <type> - <v>Pid = pid()</v> - <v>Module = atom()</v> - <v>Application = atom()</v> - </type> <desc> <p>Returns the name of the application to which the process - <c>Pid</c> or the module <c>Module</c> belongs. Providing no + <c><anno>Pid</anno></c> or the module <c><anno>Module</anno></c> belongs. Providing no argument is the same as calling <c>get_application(self())</c>.</p> <p>If the specified process does not belong to any application, @@ -110,17 +106,12 @@ </desc> </func> <func> - <name>get_env(Par) -> {ok, Val} | undefined</name> - <name>get_env(Application, Par) -> {ok, Val} | undefined</name> + <name name="get_env" arity="1"/> + <name name="get_env" arity="2"/> <fsummary>Get the value of a configuration parameter</fsummary> - <type> - <v>Application = atom()</v> - <v>Par = atom()</v> - <v>Val = term()</v> - </type> <desc> - <p>Returns the value of the configuration parameter <c>Par</c> - for <c>Application</c>. If the application argument is + <p>Returns the value of the configuration parameter <c><anno>Par</anno></c> + for <c><anno>Application</anno></c>. If the application argument is omitted, it defaults to the application of the calling process.</p> <p>If the specified application is not loaded, or @@ -130,17 +121,12 @@ </desc> </func> <func> - <name>get_key(Key) -> {ok, Val} | undefined</name> - <name>get_key(Application, Key) -> {ok, Val} | undefined</name> + <name name="get_key" arity="1"/> + <name name="get_key" arity="2"/> <fsummary>Get the value of an application specification key</fsummary> - <type> - <v>Application = atom()</v> - <v>Key = atom()</v> - <v>Val = term()</v> - </type> <desc> <p>Returns the value of the application specification key - <c>Key</c> for <c>Application</c>. If the application + <c><anno>Key</anno></c> for <c><anno>Application</anno></c>. If the application argument is omitted, it defaults to the application of the calling process.</p> <p>If the specified application is not loaded, or @@ -150,45 +136,35 @@ </desc> </func> <func> - <name>load(AppDescr) -> ok | {error, Reason}</name> - <name>load(AppDescr, Distributed) -> ok | {error, Reason}</name> + <name name="load" arity="1"/> + <name name="load" arity="2"/> <fsummary>Load an application</fsummary> - <type> - <v>AppDescr = Application | AppSpec</v> - <v> Application = atom()</v> - <v> AppSpec = {application,Application,AppSpecKeys}</v> - <v> AppSpec = [{Key,Val}]</v> - <v> Key = atom()</v> - <v> Val = term()</v> - <v>Distributed = {Application,Nodes} | {Application,Time,Nodes} | default</v> - <v> Nodes = [node() | {node(),..,node()}]</v> - <v> Time = integer() > 0</v> - <v>Reason = term()</v> - </type> + <type name="application_spec"/> + <type name="application_opt"/> <desc> <p>Loads the application specification for an application into the application controller. It will also load the application specifications for any included applications. Note that the function does not load the actual Erlang object code.</p> - <p>The application can be given by its name <c>Application</c>. + <p>The application can be given by its name <c><anno>Application</anno></c>. In this case the application controller will search the code - path for the application resource file <c>Application.app</c> + path for the application resource file <c><anno>Application</anno>.app</c> and load the specification it contains.</p> <p>The application specification can also be given directly as a - tuple <c>AppSpec</c>. This tuple should have the format and + tuple <c><anno>AppSpec</anno></c>. This tuple should have the format and contents as described in <c>app(4)</c>.</p> - <p>If <c>Distributed == {Application,[Time,]Nodes}</c>, + <p>If <c><anno>Distributed</anno> == {<anno>Application</anno>,[<anno>Time</anno>,]<anno>Nodes</anno>}</c>, the application will be distributed. The argument overrides the value for the application in the Kernel configuration - parameter <c>distributed</c>. <c>Application</c> must be + parameter <c>distributed</c>. <c><anno>Application</anno></c> must be the name of the application (same as in the first argument). - If a node crashes and <c>Time</c> has been specified, then - the application controller will wait for <c>Time</c> + If a node crashes and <c><anno>Time</anno></c> has been specified, then + the application controller will wait for <c><anno>Time</anno></c> milliseconds before attempting to restart the application on - another node. If <c>Time</c> is not specified, it will + another node. If <c><anno>Time</anno></c> is not specified, it will default to 0 and the application will be restarted immediately.</p> - <p><c>Nodes</c> is a list of node names where the application + <p><c><anno>Nodes</anno></c> is a list of node names where the application may run, in priority from left to right. Node names can be grouped using tuples to indicate that they have the same priority. Example:</p> @@ -204,32 +180,22 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>loaded_applications() -> [{Application, Description, Vsn}]</name> + <name name="loaded_applications" arity="0"/> <fsummary>Get the currently loaded applications</fsummary> - <type> - <v>Application = atom()</v> - <v>Description = string()</v> - <v>Vsn = string()</v> - </type> <desc> <p>Returns a list with information about the applications which have been loaded using <c>load/1,2</c>, also included - applications. <c>Application</c> is the application name. - <c>Description</c> and <c>Vsn</c> are the values of its + applications. <c><anno>Application</anno></c> is the application name. + <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the values of its <c>description</c> and <c>vsn</c> application specification keys, respectively.</p> </desc> </func> <func> - <name>permit(Application, Bool) -> ok | {error, Reason}</name> + <name name="permit" arity="2"/> <fsummary>Change an application's permission to run on a node.</fsummary> - <type> - <v>Application = atom()</v> - <v>Bool = bool()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Changes the permission for <c>Application</c> to run at + <p>Changes the permission for <c><anno>Application</anno></c> to run at the current node. The application must have been loaded using <c>load/1,2</c> for the function to have effect.</p> <p>If the permission of a loaded, but not started, application @@ -258,20 +224,14 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>set_env(Application, Par, Val) -> ok</name> - <name>set_env(Application, Par, Val, Timeout) -> ok</name> + <name name="set_env" arity="3"/> + <name name="set_env" arity="4"/> <fsummary>Set the value of a configuration parameter</fsummary> - <type> - <v>Application = atom()</v> - <v>Par = atom()</v> - <v>Val = term()</v> - <v>Timeout = int() | infinity</v> - </type> <desc> - <p>Sets the value of the configuration parameter <c>Par</c> for - <c>Application</c>.</p> + <p>Sets the value of the configuration parameter <c><anno>Par</anno></c> for + <c><anno>Application</anno></c>.</p> <p><c>set_env/3</c> uses the standard <c>gen_server</c> timeout - value (5000 ms). A <c>Timeout</c> argument can be provided + value (5000 ms). A <c><anno>Timeout</anno></c> argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.</p> <warning> @@ -285,20 +245,15 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>start(Application) -> ok | {error, Reason}</name> - <name>start(Application, Type) -> ok | {error, Reason}</name> + <name name="start" arity="1"/> + <name name="start" arity="2"/> <fsummary>Load and start an application</fsummary> - <type> - <v>Application = atom()</v> - <v>Type = permanent | transient | temporary</v> - <v>Reason = term()</v> - </type> <desc> - <p>Starts <c>Application</c>. If it is not loaded, + <p>Starts <c><anno>Application</anno></c>. If it is not loaded, the application controller will first load it using <c>load/1</c>. It will make sure any included applications are loaded, but will not start them. That is assumed to be - taken care of in the code for <c>Application</c>.</p> + taken care of in the code for <c><anno>Application</anno></c>.</p> <p>The application controller checks the value of the application specification key <c>applications</c>, to ensure that all applications that should be started before @@ -310,7 +265,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> The application master starts the application by calling the application callback function <c>Module:start/2</c> as defined by the application specification key <c>mod</c>.</p> - <p>The <c>Type</c> argument specifies the type of + <p>The <c><anno>Type</anno></c> argument specifies the type of the application. If omitted, it defaults to <c>temporary</c>.</p> <list type="bulleted"> <item>If a permanent application terminates, all other @@ -331,19 +286,15 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>start_type() -> StartType | local | undefined</name> + <name name="start_type" arity="0"/> <fsummary>Get the start type of an ongoing application startup.</fsummary> - <type> - <v>StartType = normal | {takeover,Node} | {failover,Node}</v> - <v> Node = node()</v> - </type> <desc> <p>This function is intended to be called by a process belonging to an application, when the application is being started, to - determine the start type which is either <c>StartType</c> or + determine the start type which is either <c><anno>StartType</anno></c> or <c>local</c>.</p> - <p>See <c>Module:start/2</c> for a description of - <c>StartType</c>.</p> + <p>See <seealso marker="#start_type"><c>Module:start/2</c></seealso> for a description of + <c><anno>StartType</anno></c>.</p> <p><c>local</c> is returned if only parts of the application is being restarted (by a supervisor), or if the function is called outside a startup.</p> @@ -352,14 +303,10 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>stop(Application) -> ok | {error, Reason}</name> + <name name="stop" arity="1"/> <fsummary>Stop an application</fsummary> - <type> - <v>Application = atom()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Stops <c>Application</c>. The application master calls + <p>Stops <c><anno>Application</anno></c>. The application master calls <c>Module:prep_stop/1</c>, if such a function is defined, and then tells the top supervisor of the application to shutdown (see <c>supervisor(3)</c>). This means that the entire @@ -384,16 +331,11 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>takeover(Application, Type) -> ok | {error, Reason}</name> + <name name="takeover" arity="2"/> <fsummary>Take over a distributed application</fsummary> - <type> - <v>Application = atom()</v> - <v>Type = permanent | transient | temporary</v> - <v>Reason = term()</v> - </type> <desc> <p>Performs a takeover of the distributed application - <c>Application</c>, which executes at another node + <c><anno>Application</anno></c>, which executes at another node <c>Node</c>. At the current node, the application is restarted by calling <c>Module:start({takeover,Node},StartArgs)</c>. <c>Module</c> @@ -413,14 +355,10 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>unload(Application) -> ok | {error, Reason}</name> + <name name="unload" arity="1"/> <fsummary>Unload an application</fsummary> - <type> - <v>Application = atom()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Unloads the application specification for <c>Application</c> + <p>Unloads the application specification for <c><anno>Application</anno></c> from the application controller. It will also unload the application specifications for any included applications. Note that the function does not purge the actual Erlang @@ -428,19 +366,14 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>unset_env(Application, Par) -> ok</name> - <name>unset_env(Application, Par, Timeout) -> ok</name> + <name name="unset_env" arity="2"/> + <name name="unset_env" arity="3"/> <fsummary>Unset the value of a configuration parameter</fsummary> - <type> - <v>Application = atom()</v> - <v>Par = atom()</v> - <v>Timeout = int() | infinity</v> - </type> <desc> - <p>Removes the configuration parameter <c>Par</c> and its value - for <c>Application</c>.</p> + <p>Removes the configuration parameter <c><anno>Par</anno></c> and its value + for <c><anno>Application</anno></c>.</p> <p><c>unset_env/2</c> uses the standard <c>gen_server</c> - timeout value (5000 ms). A <c>Timeout</c> argument can be + timeout value (5000 ms). A <c><anno>Timeout</anno></c> argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.</p> <warning> @@ -454,23 +387,17 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </desc> </func> <func> - <name>which_applications() -> [{Application, Description, Vsn}]</name> - <name>which_applications(Timeout) -> [{Application, Description, Vsn}]</name> + <name name="which_applications" arity="0"/> + <name name="which_applications" arity="1"/> <fsummary>Get the currently running applications</fsummary> - <type> - <v>Application = atom()</v> - <v>Description = string()</v> - <v>Vsn = string()</v> - <v>Timeout = int() | infinity</v> - </type> <desc> <p>Returns a list with information about the applications which - are currently running. <c>Application</c> is the application - name. <c>Description</c> and <c>Vsn</c> are the values of its + are currently running. <c><anno>Application</anno></c> is the application + name. <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the values of its <c>description</c> and <c>vsn</c> application specification keys, respectively.</p> <p><c>which_applications/0</c> uses the standard - <c>gen_server</c> timeout value (5000 ms). A <c>Timeout</c> + <c>gen_server</c> timeout value (5000 ms). A <c><anno>Timeout</anno></c> argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.</p> @@ -501,7 +428,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> structured according to the OTP design principles as a supervision tree, this means starting the top supervisor of the tree.</p> - <p><c>StartType</c> defines the type of start:</p> + <p><marker id="start_type"/><c>StartType</c> defines the type of start:</p> <list type="bulleted"> <item><c>normal</c> if it's a normal startup.</item> <item><c>normal</c> also if the application is distributed and @@ -532,8 +459,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> <fsummary>Extended start of an application</fsummary> <type> <v>Phase = atom()</v> - <v>StartType = normal | {takeover,Node} | {failover,Node}</v> - <v> Node = node()</v> + <v>StartType = <seealso marker="#type-start_type">start_type()</seealso></v> <v>PhaseArgs = term()</v> <v>Pid = pid()</v> <v>State = state()</v> diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml index f53fc8b29a..15d9ef0fe4 100644 --- a/lib/kernel/doc/src/auth.xml +++ b/lib/kernel/doc/src/auth.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,29 +34,28 @@ Cookie system, refer to <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso> in the Erlang Reference Manual.</p> </description> + <datatypes> + <datatype> + <name name="cookie"/> + </datatype> + </datatypes> <funcs> <func> - <name>is_auth(Node) -> yes | no</name> + <name name="is_auth" arity="1"/> <fsummary>Status of communication authorization (deprecated)</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> - <p>Returns <c>yes</c> if communication with <c>Node</c> is - authorized. Note that a connection to <c>Node</c> will - be established in this case. Returns <c>no</c> if <c>Node</c> + <p>Returns <c>yes</c> if communication with <c><anno>Node</anno></c> is + authorized. Note that a connection to <c><anno>Node</anno></c> will + be established in this case. Returns <c>no</c> if <c><anno>Node</anno></c> does not exist or communication is not authorized (it has another cookie than <c>auth</c> thinks it has).</p> - <p>Use <seealso marker="net_adm#ping/1">net_adm:ping(Node)</seealso> + <p>Use <seealso marker="net_adm#ping/1">net_adm:ping(<anno>Node</anno>)</seealso> instead.</p> </desc> </func> <func> - <name>cookie() -> Cookie</name> + <name name="cookie" arity="0"/> <fsummary>Magic cookie for local node (deprecated)</fsummary> - <type> - <v>Cookie = atom()</v> - </type> <desc> <p>Use <seealso marker="erts:erlang#erlang:get_cookie/0">erlang:get_cookie()</seealso> @@ -64,16 +63,14 @@ </desc> </func> <func> - <name>cookie(TheCookie) -> true</name> + <name name="cookie" arity="1"/> <fsummary>Set the magic for the local node (deprecated)</fsummary> - <type> - <v>TheCookie = Cookie | [Cookie]</v> - <d>The cookie may also be given as a list with a single atom element</d> - <v> Cookie = atom()</v> - </type> + <type_desc variable="TheCookie"> + The cookie may also be given as a list with a single atom element. + </type_desc> <desc> <p>Use - <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(node(), Cookie)</seealso> + <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(node(), <anno>Cookie</anno>)</seealso> instead.</p> </desc> </func> @@ -82,7 +79,7 @@ <fsummary>Set the magic cookie for a node and verify authorization (deprecated)</fsummary> <type> <v>Node = node()</v> - <v>Cookie = atom()</v> + <v>Cookie = <seealso marker="#type-cookie">cookie()</seealso></v> </type> <desc> <p>Equivalent to @@ -90,18 +87,14 @@ </desc> </func> <func> - <name>node_cookie(Node, Cookie) -> yes | no</name> + <name name="node_cookie" arity="2"/> <fsummary>Set the magic cookie for a node and verify authorization (deprecated)</fsummary> - <type> - <v>Node = node()</v> - <v>Cookie = atom()</v> - </type> <desc> - <p>Sets the magic cookie of <c>Node</c> to <c>Cookie</c>, and + <p>Sets the magic cookie of <c><anno>Node</anno></c> to <c><anno>Cookie</anno></c>, and verifies the status of the authorization. Equivalent to calling - <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(Node, Cookie)</seealso>, followed by - <seealso marker="#is_auth/1">auth:is_auth(Node)</seealso>.</p> + <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(<anno>Node</anno>, <anno>Cookie</anno>)</seealso>, followed by + <seealso marker="#is_auth/1">auth:is_auth(<anno>Node</anno>)</seealso>.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 4b8f934df1..6f85388c22 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -286,6 +286,12 @@ given to <c>set_path/1</c>.</p> </section> + <datatypes> + <datatype> + <name name="load_error_rsn"/> + </datatype> + </datatypes> + <funcs> <func> <name>set_path(Path) -> true | {error, What}</name> diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 324d4264cf..9721907162 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -179,13 +179,48 @@ reopen the log simultaneously.</p> </note> </description> + <datatypes> + <datatype> + <name name="log"/> + </datatype> + <datatype> + <name name="dlog_size"/> + </datatype> + <datatype> + <name name="dlog_format"/> + </datatype> + <datatype> + <name name="dlog_head_opt"/> + </datatype> + <datatype> + <name name="dlog_byte"/> + </datatype> + <datatype> + <name name="dlog_mode"/> + </datatype> + <datatype> + <name name="dlog_type"/> + </datatype> + <datatype> + <name name="continuation"/> + <desc><p>Chunk continuation returned by + <c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>.</p> + </desc> + </datatype> + <datatype> + <name name="bytes"/> + </datatype> + <datatype> + <name name="invalid_header"/> + </datatype> + <datatype> + <name name="file_error"/> + </datatype> + </datatypes> <funcs> <func> - <name>accessible_logs() -> {[LocalLog], [DistributedLog]}</name> + <name name="accessible_logs" arity="0"/> <fsummary>Return the accessible disk logs on the current node.</fsummary> - <type> - <v>LocalLog = DistributedLog = term()</v> - </type> <desc> <p>The <c>accessible_logs/0</c> function returns the names of the disk logs accessible on the current node. @@ -195,16 +230,13 @@ </desc> </func> <func> - <name>alog(Log, Term)</name> - <name>balog(Log, Bytes) -> ok | {error, Reason}</name> + <name name="alog" arity="2"/> + <name name="balog" arity="2"/> + <type variable="Log"/> + <type variable="Term" name_i="1"/> + <type variable="Bytes"/> + <type name="notify_ret"/> <fsummary>Asynchronously log an item onto a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Term = term()</v> - <v>Bytes = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log</v> - </type> <desc> <p>The <c>alog/2</c> and <c>balog/2</c> functions asynchronously append an item to a disk log. The function <c>alog/2</c> is @@ -225,17 +257,13 @@ </desc> </func> <func> - <name>alog_terms(Log, TermList)</name> - <name>balog_terms(Log, BytesList) -> ok | {error, Reason}</name> + <name name="alog_terms" arity="2"/> + <name name="balog_terms" arity="2"/> <fsummary>Asynchronously log several items onto a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>TermList = [term()]</v> - <v>BytesList = [Bytes]</v> - <v>Bytes = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log</v> - </type> + <type variable="Log"/> + <type variable="TermList" name_i="1"/> + <type variable="ByteList"/> + <type name="notify_ret"/> <desc> <p>The <c>alog_terms/2</c> and <c>balog_terms/2</c> functions asynchronously append a list of items to a disk log. @@ -257,14 +285,10 @@ </desc> </func> <func> - <name>block(Log)</name> - <name>block(Log, QueueLogRecords) -> ok | {error, Reason}</name> + <name name="block" arity="1"/> + <name name="block" arity="2"/> + <type name="block_error_rsn"/> <fsummary>Block a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>QueueLogRecords = bool()</v> - <v>Reason = no_such_log | nonode | {blocked_log, Log}</v> - </type> <desc> <p>With a call to <c>block/1,2</c> a process can block a log. If the blocking process is not an owner of the log, a temporary @@ -280,52 +304,32 @@ affected by the block. Any other attempt than those hitherto mentioned to update or read a blocked log suspends the calling process until the log is unblocked or returns an - error message <c>{blocked_log, Log}</c>, depending on - whether the value of <c>QueueLogRecords</c> is <c>true</c> - or <c>false</c>. The default value of <c>QueueLogRecords</c> + error message <c>{blocked_log, <anno>Log</anno>}</c>, depending on + whether the value of <c><anno>QueueLogRecords</anno></c> is <c>true</c> + or <c>false</c>. The default value of <c><anno>QueueLogRecords</anno></c> is <c>true</c>, which is used by <c>block/1</c>. </p> </desc> </func> <func> - <name>change_header(Log, Header) -> ok | {error, Reason}</name> + <name name="change_header" arity="2"/> <fsummary>Change the head or head_func option for an owner of a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Header = {head, Head} | {head_func, {M,F,A}}</v> - <v>Head = none | term() | binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {badarg, head}</v> - </type> <desc> <p>The <c>change_header/2</c> function changes the value of the <c>head</c> or <c>head_func</c> option of a disk log.</p> </desc> </func> <func> - <name>change_notify(Log, Owner, Notify) -> ok | {error, Reason}</name> + <name name="change_notify" arity="3"/> <fsummary>Change the notify option for an owner of a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Owner = pid()</v> - <v>Notify = bool()</v> - <v>Reason = no_such_log | nonode | {blocked_log, Log} | {badarg, notify} | {not_owner, Owner}</v> - </type> <desc> <p>The <c>change_notify/3</c> function changes the value of the <c>notify</c> option for an owner of a disk log. </p> </desc> </func> <func> - <name>change_size(Log, Size) -> ok | {error, Reason}</name> + <name name="change_size" arity="2"/> <fsummary>Change the size of an open disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Size = integer() > 0 | infinity | {MaxNoBytes, MaxNoFiles}</v> - <v>MaxNoBytes = integer() > 0</v> - <v>MaxNoFiles = integer() > 0</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {new_size_too_small, CurrentSize} | {badarg, size} | {file_error, FileName, FileError}</v> - </type> <desc> <p>The <c>change_size/2</c> function changes the size of an open log. For a halt log it is always possible to increase the size, @@ -363,21 +367,17 @@ </desc> </func> <func> - <name>chunk(Log, Continuation)</name> - <name>chunk(Log, Continuation, N) -> {Continuation2, Terms} | {Continuation2, Terms, Badbytes} | eof | {error, Reason}</name> - <name>bchunk(Log, Continuation)</name> - <name>bchunk(Log, Continuation, N) -> {Continuation2, Binaries} | {Continuation2, Binaries, Badbytes} | eof | {error, Reason}</name> + <name name="chunk" arity="2"/> + <name name="chunk" arity="3"/> + <name name="bchunk" arity="2"/> + <name name="bchunk" arity="3"/> <fsummary>Read a chunk of items written to a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Continuation = start | cont()</v> - <v>N = integer() > 0 | infinity</v> - <v>Continuation2 = cont()</v> - <v>Terms = [term()]</v> - <v>Badbytes = integer()</v> - <v>Reason = no_such_log | {format_external, Log} | {blocked_log, Log} | {badarg, continuation} | {not_internal_wrap, Log} | {corrupt_log_file, FileName} | {file_error, FileName, FileError}</v> - <v>Binaries = [binary()]</v> - </type> + <type variable="Log"/> + <type variable="Continuation"/> + <type variable="N"/> + <type name="chunk_ret"/> + <type name="bchunk_ret"/> + <type name="chunk_error_rsn"/> <desc> <p>The <c>chunk/2,3</c> and <c>bchunk/2,3</c> functions make it possible to efficiently read the terms which have been @@ -394,31 +394,31 @@ individual distributed log on some other node is chosen, if such a log exists. </p> - <p>When <c>chunk/3</c> is called, <c>N</c> controls the + <p>When <c>chunk/3</c> is called, <c><anno>N</anno></c> controls the maximum number of terms that are read from the log in each chunk. Default is <c>infinity</c>, which means that all the terms contained in the 64 kilobyte chunk are read. If less than - <c>N</c> terms are returned, this does not necessarily mean + <c><anno>N</anno></c> terms are returned, this does not necessarily mean that the end of the file has been reached. </p> <p>The <c>chunk</c> function returns a tuple - <c>{Continuation2, Terms}</c>, where <c>Terms</c> is a list - of terms found in the log. <c>Continuation2</c> is yet + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list + of terms found in the log. <c><anno>Continuation2</anno></c> is yet another continuation which must be passed on to any subsequent calls to <c>chunk</c>. With a series of calls to <c>chunk</c> it is possible to extract all terms from a log. </p> <p>The <c>chunk</c> function returns a tuple - <c>{Continuation2, Terms, Badbytes}</c> if the log is opened - in read-only mode and the read chunk is corrupt. <c>Badbytes</c> + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened + in read-only mode and the read chunk is corrupt. <c><anno>Badbytes</anno></c> is the number of bytes in the file which were found not to be Erlang terms in the chunk. Note also that the log is not repaired. When trying to read chunks from a log opened in read-write mode, - the tuple <c>{corrupt_log_file, FileName}</c> is returned if the + the tuple <c>{corrupt_log_file, <anno>FileName</anno>}</c> is returned if the read chunk is corrupt. </p> <p><c>chunk</c> returns <c>eof</c> when the end of the log is - reached, or <c>{error, Reason}</c> if an error occurs. Should + reached, or <c>{error, <anno>Reason</anno>}</c> if an error occurs. Should a wrap log file be missing, a message is output on the error log. </p> <p>When <c>chunk/2,3</c> is used with wrap logs, the returned @@ -431,12 +431,8 @@ </desc> </func> <func> - <name>chunk_info(Continuation) -> InfoList | {error, Reason}</name> + <name name="chunk_info" arity="1"/> <fsummary>Return information about a chunk continuation of a disk log.</fsummary> - <type> - <v>Continuation = cont()</v> - <v>Reason = {no_continuation, Continuation}</v> - </type> <desc> <p>The <c>chunk_info/1</c> function returns the following pair describing the chunk continuation returned by @@ -444,29 +440,22 @@ </p> <list type="bulleted"> <item> - <p><c>{node, Node}</c>. Terms are read from - the disk log running on <c>Node</c>.</p> + <p><c>{node, <anno>Node</anno>}</c>. Terms are read from + the disk log running on <c><anno>Node</anno></c>.</p> </item> </list> </desc> </func> <func> - <name>chunk_step(Log, Continuation, Step) -> {ok, Continuation2} | {error, Reason}</name> + <name name="chunk_step" arity="3"/> <fsummary>Step forward or backward among the wrap log files of a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Continuation = start | cont()</v> - <v>Step = integer()</v> - <v>Continuation2 = cont()</v> - <v>Reason = no_such_log | end_of_log | {format_external, Log} | {blocked_log, Log} | {badarg, continuation} | {file_error, FileName, FileError}</v> - </type> <desc> <p>The function <c>chunk_step</c> can be used in conjunction with <c>chunk/2,3</c> and <c>bchunk/2,3</c> to search through an internally formatted wrap log. It takes as argument a continuation as returned by <c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>, and steps forward - (or backward) <c>Step</c> files in the wrap log. The + (or backward) <c><anno>Step</anno></c> files in the wrap log. The continuation returned points to the first log item in the new current file. </p> @@ -482,11 +471,9 @@ </desc> </func> <func> - <name>close(Log) -> ok | {error, Reason}</name> + <name name="close" arity="1"/> <fsummary>Close a disk log.</fsummary> - <type> - <v>Reason = no_such_log | nonode | {file_error, FileName, FileError}</v> - </type> + <type name="close_error_rsn"/> <desc> <p> <marker id="close_1"></marker> The function <c>close/1</c> closes a @@ -511,11 +498,8 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>format_error(Error) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return an English description of a disk log error reply.</fsummary> - <type> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Given the error returned by any function in this module, the function <c>format_error</c> returns a descriptive string @@ -524,11 +508,10 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>inc_wrap_file(Log) -> ok | {error, Reason}</name> + <name name="inc_wrap_file" arity="1"/> <fsummary>Change to the next wrap log file of a disk log.</fsummary> - <type> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {halt_log, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError}</v> - </type> + <type name="inc_wrap_error_rsn"/> + <type name="invalid_header"/> <desc> <p>The <c>inc_wrap_file/1</c> function forces the internally formatted disk log to start logging to the @@ -543,8 +526,9 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>info(Log) -> InfoList | {error, no_such_log}</name> + <name name="info" arity="1"/> <fsummary>Return information about a disk log.</fsummary> + <type name="dlog_info"/> <desc> <p>The <c>info/1</c> function returns a list of <c>{Tag, Value}</c> pairs describing the log. If there is a disk log process running @@ -556,55 +540,55 @@ The function <c>close/1</c> closes a </p> <list type="bulleted"> <item> - <p><c>{name, Log}</c>, where <c>Log</c> is the name of + <p><c>{name, <anno>Log</anno>}</c>, where <c><anno>Log</anno></c> is the name of the log as given by the <c>open/1</c> option <c>name</c>.</p> </item> <item> - <p><c>{file, File}</c>. For halt logs <c>File</c> is the - filename, and for wrap logs <c>File</c> is the base name.</p> + <p><c>{file, <anno>File</anno>}</c>. For halt logs <c><anno>File</anno></c> is the + filename, and for wrap logs <c><anno>File</anno></c> is the base name.</p> </item> <item> - <p><c>{type, Type}</c>, where <c>Type</c> is the type of + <p><c>{type, <anno>Type</anno>}</c>, where <c><anno>Type</anno></c> is the type of the log as given by the <c>open/1</c> option <c>type</c>.</p> </item> <item> - <p><c>{format, Format}</c>, where <c>Format</c> is the format + <p><c>{format, <anno>Format</anno>}</c>, where <c><anno>Format</anno></c> is the format of the log as given by the <c>open/1</c> option <c>format</c>.</p> </item> <item> - <p><c>{size, Size}</c>, where <c>Size</c> is the size + <p><c>{size, <anno>Size</anno>}</c>, where <c><anno>Size</anno></c> is the size of the log as given by the <c>open/1</c> option <c>size</c>, or the size set by <c>change_size/2</c>. The value set by <c>change_size/2</c> is reflected immediately.</p> </item> <item> - <p><c>{mode, Mode}</c>, where <c>Mode</c> is the mode + <p><c>{mode, <anno>Mode</anno>}</c>, where <c><anno>Mode</anno></c> is the mode of the log as given by the <c>open/1</c> option <c>mode</c>.</p> </item> <item> - <p><c>{owners, [{pid(), Notify}]}</c> where <c>Notify</c> + <p><c>{owners, [{pid(), <anno>Notify</anno>}]}</c> where <c><anno>Notify</anno></c> is the value set by the <c>open/1</c> option <c>notify</c> or the function <c>change_notify/3</c> for the owners of the log.</p> </item> <item> - <p><c>{users, Users}</c> where <c>Users</c> is the number + <p><c>{users, <anno>Users</anno>}</c> where <c><anno>Users</anno></c> is the number of anonymous users of the log, see the <c>open/1</c> option <seealso marker="#linkto">linkto</seealso>.</p> </item> <item> - <p><c>{status, Status}</c>, where <c>Status</c> is <c>ok</c> - or <c>{blocked, QueueLogRecords}</c> as set by the functions + <p><c>{status, <anno>Status</anno>}</c>, where <c><anno>Status</anno></c> is <c>ok</c> + or <c>{blocked, <anno>QueueLogRecords</anno>}</c> as set by the functions <c>block/1,2</c> and <c>unblock/1</c>.</p> </item> <item> - <p><c>{node, Node}</c>. The information returned by the + <p><c>{node, <anno>Node</anno>}</c>. The information returned by the current invocation of the <c>info/1</c> function has been - gathered from the disk log process running on <c>Node</c>.</p> + gathered from the disk log process running on <c><anno>Node</anno></c>.</p> </item> <item> - <p><c>{distributed, Dist}</c>. If the log is local on - the current node, then <c>Dist</c> has the value <c>local</c>, + <p><c>{distributed, <anno>Dist</anno>}</c>. If the log is local on + the current node, then <c><anno>Dist</anno></c> has the value <c>local</c>, otherwise all nodes where the log is distributed are returned as a list.</p> </item> @@ -614,16 +598,16 @@ The function <c>close/1</c> closes a </p> <list type="bulleted"> <item> - <p><c>{head, Head}</c>. Depending of the value of + <p><c>{head, <anno>Head</anno>}</c>. Depending of the value of the <c>open/1</c> options <c>head</c> and <c>head_func</c> or set by the function <c>change_header/2</c>, the value - of <c>Head</c> is <c>none</c> (default), + of <c><anno>Head</anno></c> is <c>none</c> (default), <c>{head, H}</c> (<c>head</c> option) or <c>{M,F,A}</c> (<c>head_func</c> option).</p> </item> <item> - <p><c>{no_written_items, NoWrittenItems}</c>, where - <c>NoWrittenItems</c> is the number of items + <p><c>{no_written_items, <anno>NoWrittenItems</anno>}</c>, where + <c><anno>NoWrittenItems</anno></c> is the number of items written to the log since the disk log process was created.</p> </item> </list> @@ -632,7 +616,7 @@ The function <c>close/1</c> closes a </p> <list type="bulleted"> <item> - <p><c>{full, Full}</c>, where <c>Full</c> is <c>true</c> or + <p><c>{full, <anno>Full</anno>}</c>, where <c><anno>Full</anno></c> is <c>true</c> or <c>false</c> depending on whether the halt log is full or not.</p> </item> </list> @@ -660,8 +644,8 @@ The function <c>close/1</c> closes a <c>size</c> or set by <c>change_size/2</c>.</p> </item> <item> - <p><c>{no_overflows, {SinceLogWasOpened, SinceLastInfo}}</c>, - where <c>SinceLogWasOpened</c> (<c>SinceLastInfo</c>) is + <p><c>{no_overflows, {<anno>SinceLogWasOpened</anno>, <anno>SinceLastInfo</anno>}}</c>, + where <c><anno>SinceLogWasOpened</anno></c> (<c><anno>SinceLastInfo</anno></c>) is the number of times a wrap log file has been filled up and a new one opened or <c>inc_wrap_file/1</c> has been called since the disk log was last opened (<c>info/1</c> @@ -677,21 +661,18 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>lclose(Log)</name> - <name>lclose(Log, Node) -> ok | {error, Reason}</name> + <name name="lclose" arity="1"/> + <name name="lclose" arity="2"/> + <type name="lclose_error_rsn"/> <fsummary>Close a disk log on one node.</fsummary> - <type> - <v>Node = node()</v> - <v>Reason = no_such_log | {file_error, FileName, FileError}</v> - </type> <desc> <p>The function <c>lclose/1</c> closes a local log or an individual distributed log on the current node. The function <c>lclose/2</c> closes an individual distributed log on the specified node if the node is not the current one. - <c>lclose(Log)</c> is equivalent to - <c>lclose(Log, node())</c>. + <c>lclose(<anno>Log</anno>)</c> is equivalent to + <c>lclose(<anno>Log</anno>, node())</c>. See also <seealso marker="#close_1">close/1</seealso>. </p> <p>If there is no log with the given name @@ -700,20 +681,17 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>log(Log, Term)</name> - <name>blog(Log, Bytes) -> ok | {error, Reason}</name> + <name name="log" arity="2"/> + <name name="blog" arity="2"/> <fsummary>Log an item onto a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Term = term()</v> - <v>Bytes = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {format_external, Log} | {blocked_log, Log} | {full, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError}</v> - </type> + <type variable="Log"/> + <type variable="Term" name_i="1"/> + <type variable="Bytes"/> + <type name="log_error_rsn"/> <desc> <p>The <c>log/2</c> and <c>blog/2</c> functions synchronously append a term to a disk log. They return <c>ok</c> or - <c>{error, Reason}</c> when the term has been written to + <c>{error, <anno>Reason</anno>}</c> when the term has been written to disk. If the log is distributed, <c>ok</c> is always returned, unless all nodes are down. Terms are written by means of the ordinary <c>write()</c> function of the @@ -736,17 +714,13 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>log_terms(Log, TermList)</name> - <name>blog_terms(Log, BytesList) -> ok | {error, Reason}</name> + <name name="log_terms" arity="2"/> + <name name="blog_terms" arity="2"/> <fsummary>Log several items onto a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>TermList = [term()]</v> - <v>BytesList = [Bytes]</v> - <v>Bytes = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {format_external, Log} | {blocked_log, Log} | {full, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError}</v> - </type> + <type variable="Log"/> + <type variable="TermList" name_i="1"/> + <type variable="BytesList"/> + <type name="log_error_rsn"/> <desc> <p>The <c>log_terms/2</c> and <c>blog_terms/2</c> functions synchronously append a list of items to the log. The benefit @@ -769,47 +743,33 @@ The function <c>close/1</c> closes a </desc> </func> <func> - <name>open(ArgL) -> OpenRet | DistOpenRet</name> + <name name="open" arity="1"/> + <type name="dlog_options"/> + <type name="dlog_option"/> + <type name="open_ret"/> + <type name="ret"/> + <type name="dist_open_ret"/> + <type name="dist_error_rsn"/> + <type name="open_error_rsn"/> + <type name="dlog_optattr"/> + <type name="dlog_size"/> <fsummary>Open a disk log file.</fsummary> - <type> - <v>ArgL = [Opt]</v> - <v>Opt = {name, term()} | {file, FileName}, {linkto, LinkTo} | {repair, Repair} | {type, Type} | {format, Format} | {size, Size} | {distributed, [Node]} | {notify, bool()} | {head, Head} | {head_func, {M,F,A}} | {mode, Mode}</v> - <v>FileName = string() | atom()</v> - <v>LinkTo = pid() | none</v> - <v>Repair = true | false | truncate</v> - <v>Type = halt | wrap</v> - <v>Format = internal | external</v> - <v>Size = integer() > 0 | infinity | {MaxNoBytes, MaxNoFiles}</v> - <v>MaxNoBytes = integer() > 0</v> - <v>MaxNoFiles = 0 < integer() < 65000</v> - <v>Rec = integer()</v> - <v>Bad = integer()</v> - <v>Head = none | term() | binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Mode = read_write | read_only</v> - <v>OpenRet = Ret | {error, Reason}</v> - <v>DistOpenRet = {[{Node, Ret}], [{BadNode, {error, DistReason}}]}</v> - <v>Node = BadNode = atom()</v> - <v>Ret = {ok, Log} | {repaired, Log, {recovered, Rec}, {badbytes, Bad}}</v> - <v>DistReason = nodedown | Reason</v> - <v>Reason = no_such_log | {badarg, Arg} | {size_mismatch, CurrentSize, NewSize} | {arg_mismatch, OptionName, CurrentValue, Value} | {name_already_open, Log} | {open_read_write, Log} | {open_read_only, Log} | {need_repair, Log} | {not_a_log_file, FileName} | {invalid_index_file, FileName} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} | {node_already_open, Log}</v> - </type> <desc> - <p>The <c>ArgL</c> parameter is a list of options which have + <p>The <c><anno>ArgL</anno></c> parameter is a list of options which have the following meanings:</p> <list type="bulleted"> <item> - <p><c>{name, Log}</c> specifies the name of the log. + <p><c>{name, <anno>Log</anno>}</c> specifies the name of the log. This is the name which must be passed on as a parameter in all subsequent logging operations. A name must always be supplied. </p> </item> <item> - <p><c>{file, FileName}</c> specifies the name of the + <p><c>{file, <anno>FileName</anno>}</c> specifies the name of the file which will be used for logged terms. If this value is omitted and the name of the log is either an atom or a string, - the file name will default to <c>lists:concat([Log, ".LOG"])</c> for halt logs. For wrap logs, this will be + the file name will default to <c>lists:concat([<anno>Log</anno>, ".LOG"])</c> for halt logs. For wrap logs, this will be the base name of the files. Each file in a wrap log will be called <c><![CDATA[<base_name>.N]]></c>, where <c>N</c> is an integer. Each wrap log will also have two files called @@ -817,22 +777,22 @@ The function <c>close/1</c> closes a </p> </item> <item> - <p><c>{linkto, LinkTo}</c>. <marker id="linkto"></marker> + <p><c>{linkto, <anno>LinkTo</anno>}</c>. <marker id="linkto"></marker> If - <c>LinkTo</c> is a pid, that pid becomes an owner of the - log. If <c>LinkTo</c> is <c>none</c> the log records + <c><anno>LinkTo</anno></c> is a pid, that pid becomes an owner of the + log. If <c><anno>LinkTo</anno></c> is <c>none</c> the log records that it is used anonymously by some process by incrementing the <c>users</c> counter. By default, the process which calls <c>open/1</c> owns the log. </p> </item> <item> - <p><c>{repair, Repair}</c>. If <c>Repair</c> is <c>true</c>, + <p><c>{repair, <anno>Repair</anno>}</c>. If <c><anno>Repair</anno></c> is <c>true</c>, the current log file will be repaired, if needed. As the restoration is initiated, a message is output on the error log. If <c>false</c> is given, no automatic repair will be attempted. Instead, the - tuple <c>{error, {need_repair, Log}}</c> is returned if an + tuple <c>{error, {need_repair, <anno>Log</anno>}}</c> is returned if an attempt is made to open a corrupt log file. If <c>truncate</c> is given, the log file will be truncated, creating an empty log. Default is @@ -841,41 +801,41 @@ If </p> </item> <item> - <p><c>{type, Type}</c> is the type of the log. Default + <p><c>{type, <anno>Type</anno>}</c> is the type of the log. Default is <c>halt</c>. </p> </item> <item> - <p><c>{format, Format}</c> specifies the format of the + <p><c>{format, <anno>Format</anno>}</c> specifies the format of the disk log. Default is <c>internal</c>. </p> </item> <item> - <p><c>{size, Size}</c> specifies the size of the log. + <p><c>{size, <anno>Size</anno>}</c> specifies the size of the log. When a halt log has reached its maximum size, all attempts to log more items are rejected. The default size is <c>infinity</c>, which for halt implies that there is no - maximum size. For wrap logs, the <c>Size</c> parameter + maximum size. For wrap logs, the <c><anno>Size</anno></c> parameter may be either a pair - <c>{MaxNoBytes, MaxNoFiles}</c> or <c>infinity</c>. In the + <c>{<anno>MaxNoBytes</anno>, <anno>MaxNoFiles</anno>}</c> or <c>infinity</c>. In the latter case, if the files of an already existing wrap log with the same name can be found, the size is read from the existing wrap log, otherwise an error is returned. - Wrap logs write at most <c>MaxNoBytes</c> bytes on each file - and use <c>MaxNoFiles</c> files before starting all over with - the first wrap log file. Regardless of <c>MaxNoBytes</c>, + Wrap logs write at most <c><anno>MaxNoBytes</anno></c> bytes on each file + and use <c><anno>MaxNoFiles</anno></c> files before starting all over with + the first wrap log file. Regardless of <c><anno>MaxNoBytes</anno></c>, at least the header (if there is one) and one item is written on each wrap log file before wrapping to the next file. When opening an existing wrap log, it is not necessary to supply a value for the option <c>Size</c>, but any supplied value must equal the current size of the log, otherwise - the tuple <c>{error, {size_mismatch, CurrentSize, NewSize}}</c> + the tuple <c>{error, {size_mismatch, <anno>CurrentSize</anno>, <anno>NewSize</anno>}}</c> is returned. </p> </item> <item> - <p><c>{distributed, Nodes}</c>. This option can be used for + <p><c>{distributed, <anno>Nodes</anno>}</c>. This option can be used for adding members to a distributed disk log. The default value is <c>[]</c>, which means that the log is local on the current node. @@ -946,10 +906,10 @@ If </list> </item> <item> - <p><c>{head, Head}</c> specifies a header to be + <p><c>{head, <anno>Head</anno>}</c> specifies a header to be written first on the log file. If the log is a wrap - log, the item <c>Head</c> is written first in each new file. - <c>Head</c> should be a term if the format is + log, the item <c><anno>Head</anno></c> is written first in each new file. + <c><anno>Head</anno></c> should be a term if the format is <c>internal</c>, and a deep list of bytes (or a binary) otherwise. Default is <c>none</c>, which means that no header is written first on the file. @@ -966,17 +926,17 @@ If </p> </item> <item> - <p><c>{mode, Mode}</c> specifies if the log is to be + <p><c>{mode, <anno>Mode</anno>}</c> specifies if the log is to be opened in read-only or read-write mode. It defaults to <c>read_write</c>. </p> </item> </list> - <p>The <c>open/1</c> function returns <c>{ok, Log}</c> if the + <p>The <c>open/1</c> function returns <c>{ok, <anno>Log</anno>}</c> if the log file was successfully opened. If the file was - successfully repaired, the tuple <c>{repaired, Log, {recovered, Rec}, {badbytes, Bad}}</c> is returned, where - <c>Rec</c> is the number of whole Erlang terms found in the - file and <c>Bad</c> is the number of bytes in the file which + successfully repaired, the tuple <c>{repaired, <anno>Log</anno>, {recovered, <anno>Rec</anno>}, {badbytes, <anno>Bad</anno>}}</c> is returned, where + <c><anno>Rec</anno></c> is the number of whole Erlang terms found in the + file and <c><anno>Bad</anno></c> is the number of bytes in the file which were non-Erlang terms. If the <c>distributed</c> parameter was given, <c>open/1</c> returns a list of successful replies and a list of erroneous replies. Each @@ -988,7 +948,7 @@ If position after the last logged item, and the logging of items will commence from there. If the format is <c>internal</c> and the existing file is not recognized as an internally - formatted log, a tuple <c>{error, {not_a_log_file, FileName}}</c> + formatted log, a tuple <c>{error, {not_a_log_file, <anno>FileName</anno>}}</c> is returned. </p> <p>The <c>open/1</c> function cannot be used for changing the @@ -1000,15 +960,15 @@ If or <c>change_size/2</c>. As a consequence, none of the options except <c>name</c> is mandatory. If some given value differs from the current value, a tuple - <c>{error, {arg_mismatch, OptionName, CurrentValue, Value}}</c> + <c>{error, {arg_mismatch, <anno>OptionName</anno>, <anno>CurrentValue</anno>, <anno>Value</anno>}}</c> is returned. Caution: an owner's attempt to open a log as owner once again is acknowledged with the return value - <c>{ok, Log}</c>, but the state of the disk log is not + <c>{ok, <anno>Log</anno>}</c>, but the state of the disk log is not affected in any way. </p> <p>If a log with a given name is local on some node, and one tries to open the log distributed on the same node, - then the tuple <c>{error, {node_already_open, Name}}</c> is + then the tuple <c>{error, {node_already_open, <anno>Log</anno>}}</c> is returned. The same tuple is returned if the log is distributed on some node, and one tries to open the log locally on the same node. Opening individual distributed disk logs for the first time @@ -1036,12 +996,8 @@ If </desc> </func> <func> - <name>pid2name(Pid) -> {ok, Log} | undefined</name> + <name name="pid2name" arity="1"/> <fsummary>Return the name of the disk log handled by a pid.</fsummary> - <type> - <v>Log = term()</v> - <v>Pid = pid()</v> - </type> <desc> <p>The <c>pid2name/1</c> function returns the name of the log given the pid of a disk log process on the current node, or @@ -1052,26 +1008,23 @@ If </desc> </func> <func> - <name>reopen(Log, File)</name> - <name>reopen(Log, File, Head)</name> - <name>breopen(Log, File, BHead) -> ok | {error, Reason}</name> + <name name="reopen" arity="2"/> + <name name="reopen" arity="3"/> + <name name="breopen" arity="3"/> <fsummary>Reopen a disk log and save the old log.</fsummary> - <type> - <v>Log = term()</v> - <v>File = string()</v> - <v>Head = term()</v> - <v>BHead = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {same_file_name, Log} | {invalid_index_file, FileName} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError}</v> - </type> + <type variable="Log"/> + <type variable="File" name_i="1"/> + <type variable="Head" name_i="2"/> + <type variable="BHead"/> + <type name="reopen_error_rsn"/> <desc> <p>The <c>reopen</c> functions first rename the log file - to <c>File</c> and then re-create a new log file. - In case of a wrap log, <c>File</c> is used as the base name + to <c><anno>File</anno></c> and then re-create a new log file. + In case of a wrap log, <c><anno>File</anno></c> is used as the base name of the renamed files. By default the header given to <c>open/1</c> is written first in - the newly opened log file, but if the <c>Head</c> or the - <c>BHead</c> argument is given, this item is used instead. + the newly opened log file, but if the <c><anno>Head</anno></c> or the + <c><anno>BHead</anno></c> argument is given, this item is used instead. The header argument is used once only; next time a wrap log file is opened, the header given to <c>open/1</c> is used. </p> @@ -1089,12 +1042,9 @@ If </desc> </func> <func> - <name>sync(Log) -> ok | {error, Reason}</name> + <name name="sync" arity="1"/> + <type name="sync_error_rsn"/> <fsummary>Flush the contents of a disk log to the disk.</fsummary> - <type> - <v>Log = term()</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {file_error, FileName, FileError}</v> - </type> <desc> <p>The <c>sync/1</c> function ensures that the contents of the log are actually written to the disk. @@ -1103,20 +1053,17 @@ If </desc> </func> <func> - <name>truncate(Log)</name> - <name>truncate(Log, Head)</name> - <name>btruncate(Log, BHead) -> ok | {error, Reason}</name> + <name name="truncate" arity="1"/> + <name name="truncate" arity="2"/> + <name name="btruncate" arity="2"/> <fsummary>Truncate a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Head = term()</v> - <v>BHead = binary() | [Byte]</v> - <v>Byte = [Byte] | 0 =< integer() =< 255</v> - <v>Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError}</v> - </type> + <type variable="Log"/> + <type variable="Head" name_i="2"/> + <type variable="BHead"/> + <type name="trunc_error_rsn"/> <desc> <p>The <c>truncate</c> functions remove all items from a disk log. - If the <c>Head</c> or the <c>BHead</c> argument is + If the <c><anno>Head</anno></c> or the <c><anno>BHead</anno></c> argument is given, this item is written first in the newly truncated log, otherwise the header given to <c>open/1</c> is used. The header argument is only used once; next time a wrap log file @@ -1138,12 +1085,9 @@ If </desc> </func> <func> - <name>unblock(Log) -> ok | {error, Reason}</name> + <name name="unblock" arity="1"/> + <type name="unblock_error_rsn"/> <fsummary>Unblock a disk log.</fsummary> - <type> - <v>Log = term()</v> - <v>Reason = no_such_log | nonode | {not_blocked, Log} | {not_blocked_by_pid, Log}</v> - </type> <desc> <p>The <c>unblock/1</c> function unblocks a log. A log can only be unblocked by the blocking process. @@ -1159,4 +1103,3 @@ If <seealso marker="wrap_log_reader">wrap_log_reader(3)</seealso></p> </section> </erlref> - diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml index 4e7533810e..472671a80e 100644 --- a/lib/kernel/doc/src/erl_boot_server.xml +++ b/lib/kernel/doc/src/erl_boot_server.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -49,29 +49,17 @@ </description> <funcs> <func> - <name>start(Slaves) -> {ok, Pid} | {error, What}</name> + <name name="start" arity="1"/> <fsummary>Start the boot server</fsummary> - <type> - <v>Slaves = [Host]</v> - <v>Host = atom()</v> - <v>Pid = pid()</v> - <v>What = term()</v> - </type> <desc> - <p>Starts the boot server. <c>Slaves</c> is a list of IP + <p>Starts the boot server. <c><anno>Slaves</anno></c> is a list of IP addresses for hosts which are allowed to use this server as a boot server.</p> </desc> </func> <func> - <name>start_link(Slaves) -> {ok, Pid} | {error, What}</name> + <name name="start_link" arity="1"/> <fsummary>Start the boot server and links the caller</fsummary> - <type> - <v>Slaves = [Host]</v> - <v>Host = atom()</v> - <v>Pid = pid()</v> - <v>What = term()()</v> - </type> <desc> <p>Starts the boot server and links to the caller. This function is used to start the server if it is included in a supervision @@ -79,37 +67,23 @@ </desc> </func> <func> - <name>add_slave(Slave) -> ok | {error, What}</name> + <name name="add_slave" arity="1"/> <fsummary>Add a slave to the list of allowed slaves</fsummary> - <type> - <v>Slave = Host</v> - <v>Host = atom()</v> - <v>What = term()</v> - </type> <desc> - <p>Adds a <c>Slave</c> node to the list of allowed slave hosts.</p> + <p>Adds a <c><anno>Slave</anno></c> node to the list of allowed slave hosts.</p> </desc> </func> <func> - <name>delete_slave(Slave) -> ok | {error, What}</name> + <name name="delete_slave" arity="1"/> <fsummary>Delete a slave from the list of allowed slaves</fsummary> - <type> - <v>Slave = Host</v> - <v>Host = atom()</v> - <v>What = void()</v> - </type> <desc> - <p>Deletes a <c>Slave</c> node from the list of allowed slave + <p>Deletes a <c><anno>Slave</anno></c> node from the list of allowed slave hosts.</p> </desc> </func> <func> - <name>which_slaves() -> Slaves</name> + <name name="which_slaves" arity="0"/> <fsummary>Return the current list of allowed slave hosts</fsummary> - <type> - <v>Slaves = [Host]</v> - <v>Host = atom()</v> - </type> <desc> <p>Returns the current list of allowed slave hosts.</p> </desc> diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml index 9a62b45d63..f9514dda2f 100644 --- a/lib/kernel/doc/src/erl_ddll.xml +++ b/lib/kernel/doc/src/erl_ddll.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -172,6 +172,14 @@ </item> </taglist> </description> + <datatypes> + <datatype> + <name name="driver"/> + </datatype> + <datatype> + <name name="path"/> + </datatype> + </datatypes> <funcs> <func> <name>demonitor(MonitorRef) -> ok</name> @@ -189,37 +197,21 @@ </desc> </func> <func> - <name>info() -> AllInfoList</name> + <name name="info" arity="0"/> <fsummary>Retrieve information about all drivers</fsummary> - <type> - <v>AllInfoList = [ DriverInfo ]</v> - <v>DriverInfo = {DriverName, InfoList}</v> - <v>DriverName = string()</v> - <v>InfoList = [ InfoItem ]</v> - <v>InfoItem = {Tag, Value}</v> - <v>Tag = atom()</v> - <v>Value = term()</v> - </type> <desc> - <p>Returns a list of tuples <c>{DriverName, InfoList}</c>, where - <c>InfoList</c> is the result of calling <seealso marker="#info/1">info/1</seealso> for that - <c>DriverName</c>. Only dynamically linked in drivers are + <p>Returns a list of tuples <c>{<anno>DriverName</anno>, <anno>InfoList</anno>}</c>, where + <c><anno>InfoList</anno></c> is the result of calling <seealso marker="#info/1">info/1</seealso> for that + <c><anno>DriverName</anno></c>. Only dynamically linked in drivers are included in the list.</p> </desc> </func> <func> - <name>info(Name) -> InfoList</name> + <name name="info" arity="1"/> <fsummary>Retrieve information about one driver</fsummary> - <type> - <v>Name = string() | atom()</v> - <v>InfoList = [ InfoItem ]</v> - <v>InfoItem = {Tag, Value}</v> - <v>Tag = atom()</v> - <v>Value = term()</v> - </type> <desc> - <p>Returns a list of tuples <c>{Tag, Value}</c>, where - <c>Tag</c> is the information item and <c>Value</c> is the result + <p>Returns a list of tuples <c>{<anno>Tag</anno>, <anno>Value</anno>}</c>, where + <c><anno>Tag</anno></c> is the information item and <c><anno>Value</anno></c> is the result of calling <seealso marker="#info/2">info/2</seealso> with this driver name and this tag. The result being a tuple list containing all information available about a driver. </p> @@ -305,22 +297,18 @@ </desc> </func> <func> - <name>load(Path, Name) -> ok | {error, ErrorDesc}</name> + <name name="load" arity="2"/> <fsummary>Load a driver</fsummary> - <type> - <v>Path = Name = string() | atom()</v> - <v>ErrorDesc = term()</v> - </type> <desc> - <p>Loads and links the dynamic driver <c>Name</c>. <c>Path</c> + <p>Loads and links the dynamic driver <c><anno>Name</anno></c>. <c><anno>Path</anno></c> is a file path to the directory containing the driver. - <c>Name</c> must be a sharable object/dynamic library. Two - drivers with different <c>Path</c> parameters cannot be - loaded under the same name. The <c>Name</c> is a string or + <c><anno>Name</anno></c> must be a sharable object/dynamic library. Two + drivers with different <c><anno>Path</anno></c> parameters cannot be + loaded under the same name. The <c><anno>Name</anno></c> is a string or atom containing at least one character.</p> - <p>The <c>Name</c> given should correspond to the filename + <p>The <c><anno>Name</anno></c> given should correspond to the filename of the actual dynamically loadable object file residing in - the directory given as <c>Path</c>, but <em>without</em> the + the directory given as <c><anno>Path</anno></c>, but <em>without</em> the extension (i.e. <c>.so</c>). The driver name provided in the driver initialization routine must correspond with the filename, in much the same way as erlang module names @@ -328,14 +316,14 @@ <p>If the driver has been previously unloaded, but is still present due to open ports against it, a call to <c>load/2</c> will stop the unloading and keep the driver - (as long as the <c>Path</c> is the same) and <c>ok</c> is + (as long as the <c><anno>Path</anno></c> is the same) and <c>ok</c> is returned. If one actually wants the object code to be reloaded, one uses <seealso marker="#reload/2">reload/2</seealso> or the low-level interface <seealso marker="#try_load/3">try_load/3</seealso> instead. Please refer to the description of <seealso marker="#scenarios">different scenarios</seealso> for loading/unloading in the introduction.</p> <p>If more than one process tries to load an already loaded - driver withe the same <c>Path</c>, or if the same process + driver withe the same <c><anno>Path</anno></c>, or if the same process tries to load it several times, the function will return <c>ok</c>. The emulator will keep track of the <c>load/2</c> calls, so that a corresponding number of @@ -349,16 +337,16 @@ several drivers with the same name but with different <c>Path</c> parameters.</p> <note> - <p>Note especially that the <c>Path</c> is interpreted + <p>Note especially that the <c><anno>Path</anno></c> is interpreted literally, so that all loaders of the same driver needs to - give the same <em>literal</em><c>Path</c> string, even + give the same <em>literal</em><c><anno>Path</anno></c> string, even though different paths might point out the same directory in the filesystem (due to use of relative paths and links).</p> </note> <p>On success, the function returns <c>ok</c>. On - failure, the return value is <c>{error,ErrorDesc}</c>, - where <c>ErrorDesc</c> is an opaque term to be + failure, the return value is <c>{error,<anno>ErrorDesc</anno>}</c>, + where <c><anno>ErrorDesc</anno></c> is an opaque term to be translated into human readable form by the <seealso marker="#format_error/1">format_error/1</seealso> function.</p> <p>For more control over the error handling, again use the @@ -369,20 +357,16 @@ </desc> </func> <func> - <name>load_driver(Path, Name) -> ok | {error, ErrorDesc}</name> + <name name="load_driver" arity="2"/> <fsummary>Load a driver</fsummary> - <type> - <v>Path = Name = string() | atom()</v> - <v>ErrorDesc = term()</v> - </type> <desc> <p>Works essentially as <c>load/2</c>, but will load the driver - with options other options. All ports that are using the + with other options. All ports that are using the driver will get killed with the reason <c>driver_unloaded</c> when the driver is to be unloaded.</p> <p>The number of loads and unloads by different <seealso marker="#users">users</seealso> influence the actual loading and unloading of a driver file. The port killing will - therefore only happen when the <em>last</em><seealso marker="#users">user</seealso> unloads the driver, or the + therefore only happen when the <em>last</em> <seealso marker="#users">user</seealso> unloads the driver, or the last process having loaded the driver exits.</p> <p>This interface (or at least the name of the functions) is kept for backward compatibility. Using <seealso marker="#try_load/3">try_load/3</seealso> with @@ -551,16 +535,11 @@ </desc> </func> <func> - <name>reload(Path, Name) -> ok | {error, ErrorDesc}</name> + <name name="reload" arity="2"/> <fsummary>Replace a driver</fsummary> - <type> - <v>Path = Name = string() | atom()</v> - <v>ErrorDesc = pending_process | OpaqueError</v> - <v>OpaqueError = term()</v> - </type> <desc> - <p>Reloads the driver named <c>Name</c> from a possibly - different <c>Path</c> than was previously used. This + <p>Reloads the driver named <c><anno>Name</anno></c> from a possibly + different <c><anno>Path</anno></c> than was previously used. This function is used in the code change <seealso marker="#scenarios">scenario</seealso> described in the introduction.</p> <p>If there are other <seealso marker="#users">users</seealso> @@ -574,7 +553,7 @@ <p>If one wants to avoid hanging on open ports, one should use the <seealso marker="#try_load/3">try_load/3</seealso> function instead.</p> - <p>The <c>Name</c> and <c>Path</c> parameters have exactly the + <p>The <c><anno>Name</anno></c> and <c><anno>Path</anno></c> parameters have exactly the same meaning as when calling the plain <seealso marker="#load/2">load/2</seealso> function.</p> <note> <p>Avoid mixing @@ -594,13 +573,8 @@ </desc> </func> <func> - <name>reload_driver(Path, Name) -> ok | {error, ErrorDesc}</name> + <name name="reload_driver" arity="2"/> <fsummary>Replace a driver</fsummary> - <type> - <v>Path = Name = string() | atom()</v> - <v>ErrorDesc = pending_process | OpaqueError</v> - <v>OpaqueError = term()</v> - </type> <desc> <p>Works exactly as <seealso marker="#reload/2">reload/2</seealso>, but for drivers loaded with the <seealso marker="#load_driver/2">load_driver/2</seealso> interface. </p> @@ -1066,15 +1040,11 @@ </desc> </func> <func> - <name>unload(Name) -> ok | {error, ErrorDesc}</name> + <name name="unload" arity="1"/> <fsummary>Unload a driver</fsummary> - <type> - <v>Name = string() | atom()</v> - <v>ErrorDesc = term()</v> - </type> <desc> <p>Unloads, or at least dereferences the driver named - <c>Name</c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, and there + <c><anno>Name</anno></c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, and there are no more open ports using the driver, the driver will actually get unloaded. In all other cases, actual unloading will be delayed until all ports are closed and there are no @@ -1084,7 +1054,7 @@ is no longer considered a user of the driver. For usage scenarios, see the <seealso marker="#scenarios">description</seealso> in the beginning of this document. </p> - <p>The <c>ErrorDesc</c> returned is an opaque value to be + <p>The <c><anno>ErrorDesc</anno></c> returned is an opaque value to be passed further on to the <seealso marker="#format_error/1">format_error/1</seealso> function. For more control over the operation, use the <seealso marker="#try_unload/2">try_unload/2</seealso> @@ -1094,15 +1064,11 @@ </desc> </func> <func> - <name>unload_driver(Name) -> ok | {error, ErrorDesc}</name> + <name name="unload_driver" arity="1"/> <fsummary>Unload a driver</fsummary> - <type> - <v>Name = string() | atom()</v> - <v>ErrorDesc = term()</v> - </type> <desc> <p>Unloads, or at least dereferences the driver named - <c>Name</c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, all + <c><anno>Name</anno></c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, all remaining open ports using the driver will get killed with the reason <c>driver_unloaded</c> and the driver will eventually get unloaded.</p> @@ -1112,7 +1078,7 @@ <seealso marker="#users">user</seealso>. For usage scenarios, see the <seealso marker="#scenarios">description</seealso> in the beginning of this document.</p> - <p>The <c>ErrorDesc</c> returned is an opaque value to be + <p>The <c><anno>ErrorDesc</anno></c> returned is an opaque value to be passed further on to the <seealso marker="#format_error/1">format_error/1</seealso> function. For more control over the operation, use the <seealso marker="#try_unload/2">try_unload/2</seealso> @@ -1125,7 +1091,7 @@ <name>loaded_drivers() -> {ok, Drivers}</name> <fsummary>List loaded drivers</fsummary> <type> - <v>Drivers = [Driver()]</v> + <v>Drivers = [Driver]</v> <v>Driver = string()</v> </type> <desc> @@ -1138,13 +1104,10 @@ </desc> </func> <func> - <name>format_error(ErrorDesc) -> string()</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDesc -- see below</v> - </type> <desc> - <p>Takes an <c>ErrorDesc</c> returned by load, unload or + <p>Takes an <c><anno>ErrorDesc</anno></c> returned by load, unload or reload functions and returns a string which describes the error or warning.</p> <note> diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml index 7f78322472..acbf9a2c6e 100644 --- a/lib/kernel/doc/src/error_handler.xml +++ b/lib/kernel/doc/src/error_handler.xml @@ -37,48 +37,44 @@ </description> <funcs> <func> - <name>undefined_function(Module, Function, Args) -> term()</name> + <name name="undefined_function" arity="3"/> <fsummary>Called when an undefined function is encountered</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <d>A (possibly empty) list of arguments <c>Arg1,..,ArgN</c></d> - </type> + <type_desc variable="Args"> + A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> + </type_desc> <desc> <p>This function is evaluated if a call is made to - <c>Module:Function(Arg1,.., ArgN)</c> and - <c>Module:Function/N</c> is undefined. Note that + <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> and + <c><anno>Module</anno>:<anno>Function</anno>/N</c> is undefined. Note that <c>undefined_function/3</c> is evaluated inside the process making the original call.</p> - <p>If <c>Module</c> is interpreted, the interpreter is invoked + <p>If <c><anno>Module</anno></c> is interpreted, the interpreter is invoked and the return value of the interpreted - <c>Function(Arg1,.., ArgN)</c> call is returned.</p> + <c><anno>Function</anno>(Arg1,.., ArgN)</c> call is returned.</p> <p>Otherwise, it returns, if possible, the value of - <c>apply(Module, Function, Args)</c> after an attempt has been - made to autoload <c>Module</c>. If this is not possible, the - call to <c>Module:Function(Arg1,.., ArgN)</c> fails with + <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> after an attempt has been + made to autoload <c><anno>Module</anno></c>. If this is not possible, the + call to <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> fails with exit reason <c>undef</c>.</p> </desc> </func> <func> - <name>undefined_lambda(Module, Fun, Args) -> term()</name> + <name name="undefined_lambda" arity="3"/> <fsummary>Called when an undefined lambda (fun) is encountered</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <d>A (possibly empty) list of arguments <c>Arg1,..,ArgN</c></d> - </type> + <type_desc variable="Args"> + A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> + </type_desc> <desc> <p>This function is evaluated if a call is made to - <c>Fun(Arg1,.., ArgN)</c> when the module defining the fun is + <c><anno>Fun</anno>(Arg1,.., ArgN)</c> when the module defining the fun is not loaded. The function is evaluated inside the process making the original call.</p> - <p>If <c>Module</c> is interpreted, the interpreter is invoked + <p>If <c><anno>Module</anno></c> is interpreted, the interpreter is invoked and the return value of the interpreted - <c>Fun(Arg1,.., ArgN)</c> call is returned.</p> + <c><anno>Fun</anno>(Arg1,.., ArgN)</c> call is returned.</p> <p>Otherwise, it returns, if possible, the value of - <c>apply(Fun, Args)</c> after an attempt has been made to - autoload <c>Module</c>. If this is not possible, the call + <c>apply(<anno>Fun</anno>, <anno>Args</anno>)</c> after an attempt has been made to + autoload <c><anno>Module</anno></c>. If this is not possible, the call fails with exit reason <c>undef</c>.</p> </desc> </func> diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index e107d9b746..2d95f96ac7 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -65,19 +65,20 @@ be tagged as warnings or info. Tagging them as warnings may require rewriting existing user defined event handlers.</p> </description> + <datatypes> + <datatype> + <name name="report"/> + </datatype> + </datatypes> <funcs> <func> - <name>error_msg(Format) -> ok</name> - <name>error_msg(Format, Data) -> ok</name> - <name>format(Format, Data) -> ok</name> + <name name="error_msg" arity="1"/> + <name name="error_msg" arity="2"/> + <name name="format" arity="2"/> <fsummary>Send an standard error event to the error logger</fsummary> - <type> - <v>Format = string()</v> - <v>Data = [term()]</v> - </type> <desc> <p>Sends a standard error event to the error logger. - The <c>Format</c> and <c>Data</c> arguments are the same as + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <c>io:format/2</c>. The event is handled by the standard event handler.</p> <pre> @@ -94,12 +95,8 @@ ok</pre> </desc> </func> <func> - <name>error_report(Report) -> ok</name> + <name name="error_report" arity="1"/> <fsummary>Send a standard error report event to the error logger</fsummary> - <type> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a standard error report event to the error logger. The event is handled by the standard event handler.</p> @@ -119,18 +116,13 @@ ok</pre> </desc> </func> <func> - <name>error_report(Type, Report) -> ok</name> + <name name="error_report" arity="2"/> <fsummary>Send a user defined error report event to the error logger</fsummary> - <type> - <v>Type = term()</v> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a user defined error report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.</p> - <p>It is recommended that <c>Report</c> follows the same + <p>It is recommended that <c><anno>Report</anno></c> follows the same structure as for <c>error_report/1</c>.</p> </desc> </func> @@ -174,16 +166,12 @@ ok</pre> </desc> </func> <func> - <name>warning_msg(Format) -> ok</name> - <name>warning_msg(Format, Data) -> ok</name> + <name name="warning_msg" arity="1"/> + <name name="warning_msg" arity="2"/> <fsummary>Send a standard warning event to the error logger</fsummary> - <type> - <v>Format = string()</v> - <v>Data = [term()]</v> - </type> <desc> <p>Sends a standard warning event to the error logger. - The <c>Format</c> and <c>Data</c> arguments are the same as + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <c>io:format/2</c>. The event is handled by the standard event handler. It is tagged either as an error, warning or info, see @@ -196,12 +184,8 @@ ok</pre> </desc> </func> <func> - <name>warning_report(Report) -> ok</name> + <name name="warning_report" arity="1"/> <fsummary>Send a standard warning report event to the error logger</fsummary> - <type> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a standard warning report event to the error logger. The event is handled by the standard event handler. It is @@ -210,13 +194,8 @@ ok</pre> </desc> </func> <func> - <name>warning_report(Type, Report) -> ok</name> + <name name="warning_report" arity="2"/> <fsummary>Send a user defined warning report event to the error logger</fsummary> - <type> - <v>Type = term()</v> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a user defined warning report event to the error logger. An event handler to handle the event is supposed to @@ -227,16 +206,12 @@ ok</pre> </desc> </func> <func> - <name>info_msg(Format) -> ok</name> - <name>info_msg(Format, Data) -> ok</name> + <name name="info_msg" arity="1"/> + <name name="info_msg" arity="2"/> <fsummary>Send a standard information event to the error logger</fsummary> - <type> - <v>Format = string()</v> - <v>Data = [term()]</v> - </type> <desc> <p>Sends a standard information event to the error logger. - The <c>Format</c> and <c>Data</c> arguments are the same as + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as the arguments of <c>io:format/2</c>. The event is handled by the standard event handler.</p> <pre> @@ -253,12 +228,8 @@ ok</pre> </desc> </func> <func> - <name>info_report(Report) -> ok</name> + <name name="info_report" arity="1"/> <fsummary>Send a standard information report event to the error logger</fsummary> - <type> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a standard information report event to the error logger. The event is handled by the standard event handler.</p> @@ -278,63 +249,49 @@ ok</pre> </desc> </func> <func> - <name>info_report(Type, Report) -> ok</name> + <name name="info_report" arity="2"/> <fsummary>Send a user defined information report event to the error logger</fsummary> - <type> - <v>Type = term()</v> - <v>Report = [{Tag, Data} | term()] | string() | term()</v> - <v> Tag = Data = term()</v> - </type> <desc> <p>Sends a user defined information report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.</p> - <p>It is recommended that <c>Report</c> follows the same + <p>It is recommended that <c><anno>Report</anno></c> follows the same structure as for <c>info_report/1</c>.</p> </desc> </func> <func> - <name>add_report_handler(Handler) -> Result</name> - <name>add_report_handler(Handler, Args) -> Result</name> + <name name="add_report_handler" arity="1"/> + <name name="add_report_handler" arity="2"/> <fsummary>Add an event handler to the error logger</fsummary> - <type> - <v>Handler, Args, Result -- see gen_event:add_handler/3</v> - </type> <desc> <p>Adds a new event handler to the error logger. The event handler must be implemented as a <c>gen_event</c> callback module, see <seealso marker="stdlib:gen_event">gen_event(3)</seealso>.</p> - <p><c>Handler</c> is typically the name of the callback module - and <c>Args</c> is an optional term (defaults to []) passed - to the initialization callback function <c>Module:init/1</c>. + <p><c><anno>Handler</anno></c> is typically the name of the callback module + and <c><anno>Args</anno></c> is an optional term (defaults to []) passed + to the initialization callback function <c><anno>Handler</anno>:init/1</c>. The function returns <c>ok</c> if successful.</p> <p>The event handler must be able to handle the <seealso marker="#events">events</seealso> described below.</p> </desc> </func> <func> - <name>delete_report_handler(Handler) -> Result</name> + <name name="delete_report_handler" arity="1"/> <fsummary>Delete an event handler from the error logger</fsummary> - <type> - <v>Handler, Result -- see gen_event:delete_handler/3</v> - </type> <desc> <p>Deletes an event handler from the error logger by calling - <c>gen_event:delete_handler(error_logger, Handler, [])</c>, + <c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>, see <seealso marker="stdlib:gen_event">gen_event(3)</seealso>.</p> </desc> </func> <func> - <name>tty(Flag) -> ok</name> + <name name="tty" arity="1"/> <fsummary>Enable or disable printouts to the tty</fsummary> - <type> - <v>Flag = bool()</v> - </type> <desc> - <p>Enables (<c>Flag == true</c>) or disables - (<c>Flag == false</c>) printout of standard events to the tty.</p> + <p>Enables (<c><anno>Flag</anno> == true</c>) or disables + (<c><anno>Flag</anno> == false</c>) printout of standard events to the tty.</p> <p>This is done by adding or deleting the standard event handler for output to tty, thus calling this function overrides the value of the Kernel <c>error_logger</c> configuration @@ -342,13 +299,15 @@ ok</pre> </desc> </func> <func> - <name>logfile(Request) -> ok | Filename | {error, What}</name> + <name name="logfile" arity="1" clause_i="1"/> + <name name="logfile" arity="1" clause_i="2"/> + <name name="logfile" arity="1" clause_i="3"/> + <type variable="Filename"/> + <type variable="OpenReason" name_i="1"/> + <type variable="CloseReason" name_i="2"/> + <type variable="FilenameReason" name_i="3"/> + <type name="open_error"/> <fsummary>Enable or disable error printouts to a file</fsummary> - <type> - <v>Request = {open, Filename} | close | filename</v> - <v> Filename = atom() | string()</v> - <v>What = allready_have_logfile | no_log_file | term()</v> - </type> <desc> <p>Enables or disables printout of standard events to a file.</p> <p>This is done by adding or deleting the standard event handler @@ -361,22 +320,22 @@ ok</pre> There can only be one active log file at a time.</p> <p><c>Request</c> is one of:</p> <taglist> - <tag><c>{open, Filename}</c></tag> + <tag><c>{open, <anno>Filename</anno>}</c></tag> <item> - <p>Opens the log file <c>Filename</c>. Returns <c>ok</c> if + <p>Opens the log file <c><anno>Filename</anno></c>. Returns <c>ok</c> if successful, or <c>{error, allready_have_logfile}</c> if logging to file is already enabled, or an error tuple if - another error occurred. For example, if <c>Filename</c> + another error occurred. For example, if <c><anno>Filename</anno></c> could not be opened.</p> </item> <tag><c>close</c></tag> <item> <p>Closes the current log file. Returns <c>ok</c>, or - <c>{error, What}</c>.</p> + <c>{error, module_not_found}</c>.</p> </item> <tag><c>filename</c></tag> <item> - <p>Returns the name of the log file <c>Filename</c>, or + <p>Returns the name of the log file <c><anno>Filename</anno></c>, or <c>{error, no_log_file}</c> if logging to file is not enabled.</p> </item> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 36fce464c5..e0feaf6ee7 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -93,47 +93,76 @@ is UTF-8...</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -iodata() = iolist() | binary() - iolist() = [char() | binary() | iolist()] - -io_device() - as returned by file:open/2, a process handling IO protocols - -name() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). - -posix() - an atom which is named from the POSIX error codes used in - Unix, and in the runtime libraries of most C compilers - -ext_posix() = posix() | badarg + <datatypes> + <datatype> + <name name="bindings"/> + </datatype> + <datatype> + <name name="deep_list"/> + </datatype> + <datatype> + <name name="fd"/> + </datatype> + <datatype> + <name name="filename"/> + </datatype> + <datatype> + <name name="io_device"/> + <desc> + <p>As returned by + <seealso marker="#open/2">file:open/2</seealso>, + a process handling IO protocols.</p> + </desc> + </datatype> + <datatype> + <name name="name"/> + <desc> + <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c> + are allowed to be > 255. + <c><anno>RawFilename</anno></c> is a filename not subject to + Unicode translation, + meaning that it can contain characters not conforming to + the Unicode encoding expected from the filesystem + (i.e. non-UTF-8 characters although the VM is started + in Unicode filename mode). + </p> + </desc> + </datatype> + <datatype> + <name name="posix"/> + <desc> + <p>An atom which is named from the POSIX error codes used in + Unix, and in the runtime libraries of most C compilers.</p> + </desc> + </datatype> + <datatype> + <name name="date"/> + </datatype> + <datatype> + <name name="time"/> + </datatype> + <datatype> + <name name="date_time"/> + <desc> + <p>Must denote a valid date and time.</p> + </desc> + </datatype> + <datatype> + <name name="file_info"/> + </datatype> + <datatype> + <name name="location"/> + </datatype> + <datatype> + <name name="mode"/> + </datatype> + </datatypes> -time() = {{Year, Month, Day}, {Hour, Minute, Second}} - Year = Month = Day = Hour = Minute = Second = int() - Must denote a valid date and time</code> - </section> <funcs> <func> - <name>advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason}</name> + <name name="advise" arity="4"/> <fsummary>Predeclare an access pattern for file data</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Offset = int()</v> - <v>Length = int()</v> - <v>Advise = posix_file_advise()</v> - <v>posix_file_advise() = normal | sequential | random | no_reuse - | will_need | dont_need</v> - <v>Reason = ext_posix()</v> - </type> + <type name="posix_file_advise"/> <desc> <p><c>advise/4</c> can be used to announce an intention to access file data in a specific pattern in the future, thus allowing the @@ -142,93 +171,58 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} </desc> </func> <func> - <name>change_group(Filename, Gid) -> ok | {error, Reason}</name> + <name name="change_group" arity="2"/> <fsummary>Change group of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Gid = int()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes group of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>change_mode(Filename, Mode) -> ok | {error, Reason}</name> + <name name="change_mode" arity="2"/> <fsummary>Change permissions of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Mode = int()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes permissions of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>change_owner(Filename, Uid) -> ok | {error, Reason}</name> + <name name="change_owner" arity="2"/> <fsummary>Change owner of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Uid = int()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes owner of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>change_owner(Filename, Uid, Gid) -> ok | {error, Reason}</name> + <name name="change_owner" arity="3"/> <fsummary>Change owner and group of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Uid = int()</v> - <v>Gid = int()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes owner and group of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>change_time(Filename, Mtime) -> ok | {error, Reason}</name> + <name name="change_time" arity="2"/> <fsummary>Change the modification time of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Mtime = time()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes the modification and access times of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>change_time(Filename, Mtime, Atime) -> ok | {error, Reason}</name> + <name name="change_time" arity="3"/> <fsummary>Change the modification and last access time of a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Mtime = Atime = time()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Changes the modification and last access times of a file. See <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> </desc> </func> <func> - <name>close(IoDevice) -> ok | {error, Reason}</name> + <name name="close" arity="1"/> <fsummary>Close a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> - <p>Closes the file referenced by <c>IoDevice</c>. It mostly + <p>Closes the file referenced by <c><anno>IoDevice</anno></c>. It mostly returns <c>ok</c>, expect for some severe errors such as out of memory.</p> <p>Note that if the option <c>delayed_write</c> was @@ -238,20 +232,13 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} </desc> </func> <func> - <name>consult(Filename) -> {ok, Terms} | {error, Reason}</name> + <name name="consult" arity="1"/> <fsummary>Read Erlang terms from a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Terms = [term()]</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> - <p>Reads Erlang terms, separated by '.', from <c>Filename</c>. - Returns one of the following:</p> + <p>Reads Erlang terms, separated by '.', from + <c><anno>Filename</anno></c>. Returns one of the following:</p> <taglist> - <tag><c>{ok, Terms}</c></tag> + <tag><c>{ok, <anno>Terms</anno>}</c></tag> <item> <p>The file was successfully read.</p> </item> @@ -261,7 +248,8 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} See <seealso marker="#open/2">open/2</seealso> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang terms in the file. Use <c>format_error/1</c> to convert @@ -279,53 +267,46 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>copy(Source, Destination) -></name> - <name>copy(Source, Destination, ByteCount) -> {ok, BytesCopied} | {error, Reason}</name> + <name name="copy" arity="2"/> + <name name="copy" arity="3"/> <fsummary>Copy file contents</fsummary> - <type> - <v>Source = Destination = io_device() | Filename | {Filename, Modes}</v> - <v> Filename = name()</v> - <v> Modes = [Mode] -- see open/2</v> - <v>ByteCount = int() >= 0 | infinity</v> - <v>BytesCopied = int()</v> - </type> <desc> - <p>Copies <c>ByteCount</c> bytes from <c>Source</c> to - <c>Destination</c>. <c>Source</c> and <c>Destination</c> refer + <p>Copies <c><anno>ByteCount</anno></c> bytes from + <c><anno>Source</anno></c> to <c><anno>Destination</anno></c>. + <c><anno>Source</anno></c> and <c><anno>Destination</anno></c> refer to either filenames or IO devices from e.g. <c>open/2</c>. - <c>ByteCount</c> defaults <c>infinity</c>, denoting an + <c><anno>ByteCount</anno></c> defaults to <c>infinity</c>, denoting an infinite number of bytes.</p> - <p>The argument <c>Modes</c> is a list of possible modes, see - <seealso marker="#open/2">open/2</seealso>, and defaults to + <p>The argument <c><anno>Modes</anno></c> is a list of possible modes, + see <seealso marker="#open/2">open/2</seealso>, and defaults to [].</p> - <p>If both <c>Source</c> and <c>Destination</c> refer to + <p>If both <c><anno>Source</anno></c> and + <c><anno>Destination</anno></c> refer to filenames, the files are opened with <c>[read, binary]</c> and <c>[write, binary]</c> prepended to their mode lists, respectively, to optimize the copy.</p> - <p>If <c>Source</c> refers to a filename, it is opened with + <p>If <c><anno>Source</anno></c> refers to a filename, it is opened with <c>read</c> mode prepended to the mode list before the copy, and closed when done.</p> - <p>If <c>Destination</c> refers to a filename, it is opened + <p>If <c><anno>Destination</anno></c> refers to a filename, it is opened with <c>write</c> mode prepended to the mode list before the copy, and closed when done.</p> - <p>Returns <c>{ok, BytesCopied}</c> where <c>BytesCopied</c> is + <p>Returns <c>{ok, <anno>BytesCopied</anno>}</c> where + <c><anno>BytesCopied</anno></c> is the number of bytes that actually was copied, which may be - less than <c>ByteCount</c> if end of file was encountered on - the source. If the operation fails, <c>{error, Reason}</c> is - returned.</p> + less than <c><anno>ByteCount</anno></c> if end of file was + encountered on the source. If the operation fails, + <c>{error, <anno>Reason</anno>}</c> is returned.</p> <p>Typical error reasons: As for <c>open/2</c> if a file had to be opened, and as for <c>read/2</c> and <c>write/2</c>.</p> </desc> </func> <func> - <name>del_dir(Dir) -> ok | {error, Reason}</name> + <name name="del_dir" arity="1"/> <fsummary>Delete a directory</fsummary> - <type> - <v>Dir = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>Tries to delete the directory <c>Dir</c>. The directory must + <p>Tries to delete the directory <c><anno>Dir</anno></c>. + The directory must be empty before it can be deleted. Returns <c>ok</c> if successful.</p> <p>Typical error reasons are:</p> @@ -333,7 +314,7 @@ f.txt: {person, "kalle", 25}. <tag><c>eacces</c></tag> <item> <p>Missing search or write permissions for the parent - directories of <c>Dir</c>.</p> + directories of <c><anno>Dir</anno></c>.</p> </item> <tag><c>eexist</c></tag> <item> @@ -345,8 +326,8 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>enotdir</c></tag> <item> - <p>A component of <c>Dir</c> is not a directory. On some - platforms, <c>enoent</c> is returned instead.</p> + <p>A component of <c><anno>Dir</anno></c> is not a directory. + On some platforms, <c>enoent</c> is returned instead.</p> </item> <tag><c>einval</c></tag> <item> @@ -357,15 +338,11 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>delete(Filename) -> ok | {error, Reason}</name> + <name name="delete" arity="1"/> <fsummary>Delete a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>Tries to delete the file <c>Filename</c>. Returns <c>ok</c> - if successful.</p> + <p>Tries to delete the file <c><anno>Filename</anno></c>. + Returns <c>ok</c> if successful.</p> <p>Typical error reasons are:</p> <taglist> <tag><c>enoent</c></tag> @@ -387,30 +364,25 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>einval</c></tag> <item> - <p><c>Filename</c> had an improper type, such as tuple.</p> + <p><c><anno>Filename</anno></c> had an improper type, such as tuple.</p> </item> </taglist> <warning> - <p>In a future release, a bad type for the <c>Filename</c> - argument will probably generate an exception.</p> + <p>In a future release, a bad type for the + <c><anno>Filename</anno></c> argument will probably generate + an exception.</p> <p></p> </warning> </desc> </func> <func> - <name>eval(Filename) -> ok | {error, Reason}</name> + <name name="eval" arity="1"/> <fsummary>Evaluate Erlang expressions in a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from - <c>Filename</c>. The actual result of the evaluation is not - returned; any expression sequence in the file must be there + <c><anno>Filename</anno></c>. The actual result of the evaluation + is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:</p> <taglist> <tag><c>ok</c></tag> @@ -422,7 +394,8 @@ f.txt: {person, "kalle", 25}. <p>An error occurred when opening the file or reading it. See <c>open/2</c> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang expressions in the file. Use <c>format_error/1</c> to @@ -433,18 +406,11 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>eval(Filename, Bindings) -> ok | {error, Reason}</name> + <name name="eval" arity="2"/> <fsummary>Evaluate Erlang expressions in a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Bindings -- see erl_eval(3)</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see eval/1</v> - </type> <desc> <p>The same as <c>eval/1</c> but the variable bindings - <c>Bindings</c> are used in the evaluation. See + <c><anno>Bindings</anno></c> are used in the evaluation. See <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about variable bindings.</p> </desc> @@ -458,27 +424,19 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>format_error(Reason) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return a descriptive string for an error reason</fsummary> - <type> - <v>Reason = atom() | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see eval/1</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Given the error reason returned by any function in this module, returns a descriptive string of the error in English.</p> </desc> </func> <func> - <name>get_cwd() -> {ok, Dir} | {error, Reason}</name> + <name name="get_cwd" arity="0"/> <fsummary>Get the current working directory</fsummary> - <type> - <v>Dir = string()</v> - <v>Reason = posix()</v> - </type> <desc> - <p>Returns <c>{ok, Dir}</c>, where <c>Dir</c> is the current + <p>Returns <c>{ok, <anno>Dir</anno>}</c>, where <c><anno>Dir</anno></c> + is the current working directory of the file server.</p> <note> <p>In rare circumstances, this function can fail on Unix. @@ -496,17 +454,14 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>get_cwd(Drive) -> {ok, Dir} | {error, Reason}</name> + <name name="get_cwd" arity="1"/> <fsummary>Get the current working directory for the drive specified</fsummary> - <type> - <v>Drive = string() -- see below</v> - <v>Dir = string()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p><c>Drive</c> should be of the form "<c>Letter</c><c>:</c>", - for example "c:". Returns <c>{ok, Dir}</c> or - <c>{error, Reason}</c>, where <c>Dir</c> is the current + <p><c><anno>Drive</anno></c> should be of the form + "<c>Letter</c><c>:</c>", + for example "c:". Returns <c>{ok, <anno>Dir</anno>}</c> or + <c>{error, <anno>Reason</anno>}</c>, where <c><anno>Dir</anno></c> + is the current working directory of the drive specified.</p> <p>This function returns <c>{error, enotsup}</c> on platforms which have no concept of current drive (Unix, for example).</p> @@ -514,7 +469,7 @@ f.txt: {person, "kalle", 25}. <taglist> <tag><c>enotsup</c></tag> <item> - <p>The operating system have no concept of drives.</p> + <p>The operating system has no concept of drives.</p> </item> <tag><c>eacces</c></tag> <item> @@ -522,32 +477,27 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>einval</c></tag> <item> - <p>The format of <c>Drive</c> is invalid.</p> + <p>The format of <c><anno>Drive</anno></c> is invalid.</p> </item> </taglist> </desc> </func> <func> - <name>list_dir(Dir) -> {ok, Filenames} | {error, Reason}</name> + <name name="list_dir" arity="1"/> <fsummary>List files in a directory</fsummary> - <type> - <v>Dir = name()</v> - <v>Filenames = [Filename]</v> - <v> Filename = string()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Lists all the files in a directory. Returns - <c>{ok, Filenames}</c> if successful. Otherwise, it returns - <c>{error, Reason}</c>. <c>Filenames</c> is a list of + <c>{ok, <anno>Filenames</anno>}</c> if successful. + Otherwise, it returns <c>{error, <anno>Reason</anno>}</c>. + <c><anno>Filenames</anno></c> is a list of the names of all the files in the directory. The names are not sorted.</p> <p>Typical error reasons are:</p> <taglist> <tag><c>eacces</c></tag> <item> - <p>Missing search or write permissions for <c>Dir</c> or - one of its parent directories.</p> + <p>Missing search or write permissions for <c><anno>Dir</anno></c> + or one of its parent directories.</p> </item> <tag><c>enoent</c></tag> <item> @@ -557,14 +507,10 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>make_dir(Dir) -> ok | {error, Reason}</name> + <name name="make_dir" arity="1"/> <fsummary>Make a directory</fsummary> - <type> - <v>Dir = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>Tries to create the directory <c>Dir</c>. Missing parent + <p>Tries to create the directory <c><anno>Dir</anno></c>. Missing parent directories are <em>not</em> created. Returns <c>ok</c> if successful.</p> <p>Typical error reasons are:</p> @@ -572,15 +518,15 @@ f.txt: {person, "kalle", 25}. <tag><c>eacces</c></tag> <item> <p>Missing search or write permissions for the parent - directories of <c>Dir</c>.</p> + directories of <c><anno>Dir</anno></c>.</p> </item> <tag><c>eexist</c></tag> <item> - <p>There is already a file or directory named <c>Dir</c>.</p> + <p>There is already a file or directory named <c><anno>Dir</anno></c>.</p> </item> <tag><c>enoent</c></tag> <item> - <p>A component of <c>Dir</c> does not exist.</p> + <p>A component of <c><anno>Dir</anno></c> does not exist.</p> </item> <tag><c>enospc</c></tag> <item> @@ -588,35 +534,33 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>enotdir</c></tag> <item> - <p>A component of <c>Dir</c> is not a directory. On some - platforms, <c>enoent</c> is returned instead.</p> + <p>A component of <c><anno>Dir</anno></c> is not a directory. + On some platforms, <c>enoent</c> is returned instead.</p> </item> </taglist> </desc> </func> <func> - <name>make_link(Existing, New) -> ok | {error, Reason}</name> + <name name="make_link" arity="2"/> <fsummary>Make a hard link to a file</fsummary> - <type> - <v>Existing = New = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>Makes a hard link from <c>Existing</c> to <c>New</c>, on + <p>Makes a hard link from <c><anno>Existing</anno></c> to + <c><anno>New</anno></c>, on platforms that support links (Unix). This function returns <c>ok</c> if the link was successfully created, or - <c>{error, Reason}</c>. On platforms that do not support + <c>{error, <anno>Reason</anno>}</c>. On platforms that do not support links, <c>{error,enotsup}</c> is returned.</p> <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> <p>Missing read or write permissions for the parent - directories of <c>Existing</c> or <c>New</c>.</p> + directories of <c><anno>Existing</anno></c> or + <c><anno>New</anno></c>.</p> </item> <tag><c>eexist</c></tag> <item> - <p><c>New</c> already exists.</p> + <p><c><anno>New</anno></c> already exists.</p> </item> <tag><c>enotsup</c></tag> <item> @@ -626,30 +570,28 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>make_symlink(Name1, Name2) -> ok | {error, Reason}</name> + <name name="make_symlink" arity="2"/> <fsummary>Make a symbolic link to a file or directory</fsummary> - <type> - <v>Name1 = Name2 = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>This function creates a symbolic link <c>Name2</c> to - the file or directory <c>Name1</c>, on platforms that support - symbolic links (most Unix systems). <c>Name1</c> need not + <p>This function creates a symbolic link <c><anno>Name2</anno></c> to + the file or directory <c><anno>Name1</anno></c>, on platforms that + support + symbolic links (most Unix systems). <c><anno>Name1</anno></c> need not exist. This function returns <c>ok</c> if the link was - successfully created, or <c>{error, Reason}</c>. On platforms + successfully created, or <c>{error, <anno>Reason</anno>}</c>. + On platforms that do not support symbolic links, <c>{error, enotsup}</c> is returned.</p> <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> - <p>Missing read or write permissions for the parent - directories of <c>Name1</c> or <c>Name2</c>.</p> + <p>Missing read or write permissions for the parent directories + of <c><anno>Name1</anno></c> or <c><anno>Name2</anno></c>.</p> </item> <tag><c>eexist</c></tag> <item> - <p><c>Name2</c> already exists.</p> + <p><c><anno>Name2</anno></c> already exists.</p> </item> <tag><c>enotsup</c></tag> <item> @@ -668,22 +610,12 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}</name> + <name name="open" arity="2"/> <fsummary>Open a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Modes = [Mode]</v> - <v> Mode = read | write | append | exclusive | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed | {encoding, Encoding}</v> - <v> Size = Delay = int()</v> - <v> Encoding = latin1 | unicode | utf8 | utf16 | {utf16, Endian} | utf32 | {utf32, Endian}</v> - <v> Endian = big | little</v> - <v>IoDevice = io_device()</v> - <v>Reason = ext_posix() | system_limit</v> - </type> <desc> - <p>Opens the file <c>Filename</c> in the mode determined by - <c>Modes</c>, which may contain one or more of the following - items:</p> + <p>Opens the file <c><anno>Filename</anno></c> in the mode determined + by <c><anno>Modes</anno></c>, which may contain one or more of the + following items:</p> <taglist> <tag><c>read</c></tag> <item> @@ -841,23 +773,23 @@ f.txt: {person, "kalle", 25}. </taglist> <p>Returns:</p> <taglist> - <tag><c>{ok, IoDevice}</c></tag> + <tag><c>{ok, <anno>IoDevice</anno>}</c></tag> <item> <p>The file has been opened in the requested mode. - <c>IoDevice</c> is a reference to the file.</p> + <c><anno>IoDevice</anno></c> is a reference to the file.</p> </item> - <tag><c>{error, Reason}</c></tag> + <tag><c>{error, <anno>Reason</anno>}</c></tag> <item> <p>The file could not be opened.</p> </item> </taglist> - <p><c>IoDevice</c> is really the pid of the process which + <p><c><anno>IoDevice</anno></c> is really the pid of the process which handles the file. This process is linked to the process which originally opened the file. If any process to which - the <c>IoDevice</c> is linked terminates, the file will be - closed and the process itself will be terminated. - An <c>IoDevice</c> returned from this call can be used as an - argument to the IO functions (see + the <c><anno>IoDevice</anno></c> is linked terminates, the file will + be closed and the process itself will be terminated. + An <c><anno>IoDevice</anno></c> returned from this call can be used + as an argument to the IO functions (see <seealso marker="stdlib:io">io(3)</seealso>).</p> <note> <p>In previous versions of <c>file</c>, modes were given @@ -897,34 +829,25 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason}</name> + <name name="path_consult" arity="2"/> <fsummary>Read Erlang terms from a file</fsummary> - <type> - <v>Path = [Dir]</v> - <v> Dir = name()</v> - <v>Filename = name()</v> - <v>Terms = [term()]</v> - <v>FullName = string()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> - <p>Searches the path <c>Path</c> (a list of directory names) - until the file <c>Filename</c> is found. If <c>Filename</c> - is an absolute filename, <c>Path</c> is ignored. + <p>Searches the path <c><anno>Path</anno></c> (a list of directory + names) until the file <c><anno>Filename</anno></c> is found. + If <c><anno>Filename</anno></c> + is an absolute filename, <c><anno>Path</anno></c> is ignored. Then reads Erlang terms, separated by '.', from the file. Returns one of the following:</p> <taglist> - <tag><c>{ok, Terms, FullName}</c></tag> + <tag><c>{ok, <anno>Terms</anno>, <anno>FullName</anno>}</c></tag> <item> - <p>The file was successfully read. <c>FullName</c> is + <p>The file was successfully read. <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> <p>The file could not be found in any of the directories in - <c>Path</c>.</p> + <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> @@ -932,7 +855,8 @@ f.txt: {person, "kalle", 25}. See <seealso marker="#open/2">open/2</seealso> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang terms in the file. Use <c>format_error/1</c> to convert @@ -943,36 +867,28 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>path_eval(Path, Filename) -> {ok, FullName} | {error, Reason}</name> + <name name="path_eval" arity="2"/> <fsummary>Evaluate Erlang expressions in a file</fsummary> - <type> - <v>Path = [Dir]</v> - <v> Dir = name()</v> - <v>Filename = name()</v> - <v>FullName = string()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> - <p>Searches the path <c>Path</c> (a list of directory names) - until the file <c>Filename</c> is found. If <c>Filename</c> - is an absolute file name, <c>Path</c> is ignored. Then reads + <p>Searches the path <c><anno>Path</anno></c> (a list of directory + names) until the file <c><anno>Filename</anno></c> is found. + If <c><anno>Filename</anno></c> is an absolute file name, + <c><anno>Path</anno></c> is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. The actual result of evaluation is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:</p> <taglist> - <tag><c>{ok, FullName}</c></tag> + <tag><c>{ok, <anno>FullName</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c>FullName</c> is + <p>The file was read and evaluated. <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> <p>The file could not be found in any of the directories in - <c>Path</c>.</p> + <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> @@ -980,7 +896,8 @@ f.txt: {person, "kalle", 25}. See <seealso marker="#open/2">open/2</seealso> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang expressions in the file. Use <c>format_error/1</c> to @@ -991,34 +908,26 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>path_open(Path, Filename, Modes) -> {ok, IoDevice, FullName} | {error, Reason}</name> + <name name="path_open" arity="3"/> <fsummary>Open a file</fsummary> - <type> - <v>Path = [Dir]</v> - <v> Dir = name()</v> - <v>Filename = name()</v> - <v>Modes = [Mode] -- see open/2</v> - <v>IoDevice = io_device()</v> - <v>FullName = string()</v> - <v>Reason = ext_posix() | system_limit</v> - </type> <desc> - <p>Searches the path <c>Path</c> (a list of directory names) - until the file <c>Filename</c> is found. If <c>Filename</c> - is an absolute file name, <c>Path</c> is ignored. - Then opens the file in the mode determined by <c>Modes</c>. + <p>Searches the path <c><anno>Path</anno></c> (a list of directory + names) until the file <c><anno>Filename</anno></c> is found. + If <c><anno>Filename</anno></c> + is an absolute file name, <c><anno>Path</anno></c> is ignored. + Then opens the file in the mode determined by <c><anno>Modes</anno></c>. Returns one of the following:</p> <taglist> - <tag><c>{ok, IoDevice, FullName}</c></tag> + <tag><c>{ok, <anno>IoDevice</anno>, <anno>FullName</anno>}</c></tag> <item> <p>The file has been opened in the requested mode. - <c>IoDevice</c> is a reference to the file and - <c>FullName</c> is the full name of the file.</p> + <c><anno>IoDevice</anno></c> is a reference to the file and + <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> <p>The file could not be found in any of the directories in - <c>Path</c>.</p> + <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> @@ -1028,36 +937,27 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>path_script(Path, Filename) -> {ok, Value, FullName} | {error, Reason}</name> + <name name="path_script" arity="2"/> <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> - <type> - <v>Path = [Dir]</v> - <v> Dir = name()</v> - <v>Filename = name()</v> - <v>Value = term()</v> - <v>FullName = string()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> - <p>Searches the path <c>Path</c> (a list of directory names) - until the file <c>Filename</c> is found. If <c>Filename</c> - is an absolute file name, <c>Path</c> is ignored. Then reads + <p>Searches the path <c><anno>Path</anno></c> (a list of directory + names) until the file <c><anno>Filename</anno></c> is found. + If <c><anno>Filename</anno></c> is an absolute file name, + <c><anno>Path</anno></c> is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. Returns one of the following:</p> <taglist> - <tag><c>{ok, Value, FullName}</c></tag> + <tag><c>{ok, <anno>Value</anno>, <anno>FullName</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c>FullName</c> is - the full name of the file and <c>Value</c> the value of + <p>The file was read and evaluated. <c><anno>FullName</anno></c> is + the full name of the file and <c><anno>Value</anno></c> the value of the last expression.</p> </item> <tag><c>{error, enoent}</c></tag> <item> <p>The file could not be found in any of the directories in - <c>Path</c>.</p> + <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> @@ -1065,7 +965,8 @@ f.txt: {person, "kalle", 25}. See <seealso marker="#open/2">open/2</seealso> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang expressions in the file. Use <c>format_error/1</c> to @@ -1076,42 +977,28 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>path_script(Path, Filename, Bindings) -> {ok, Value, FullName} | {error, Reason}</name> + <name name="path_script" arity="3"/> <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> - <type> - <v>Path = [Dir]</v> - <v> Dir = name()</v> - <v>Filename = name()</v> - <v>Bindings -- see erl_eval(3)</v> - <v>Value = term()</v> - <v>FullName = string()</v> - <v>Reason = posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see path_script/2</v> - </type> <desc> <p>The same as <c>path_script/2</c> but the variable bindings - <c>Bindings</c> are used in the evaluation. See + <c><anno>Bindings</anno></c> are used in the evaluation. See <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about variable bindings.</p> </desc> </func> <func> - <name>pid2name(Pid) -> string() | undefined</name> + <name name="pid2name" arity="1"/> <fsummary>Return the name of the file handled by a pid</fsummary> - <type> - <v>Pid = pid()</v> - </type> <desc> - <p>If <c>Pid</c> is an IO device, that is, a pid returned from + <p>If <c><anno>Pid</anno></c> is an IO device, that is, a pid returned from <c>open/2</c>, this function returns the filename, or rather:</p> <taglist> - <tag><c>{ok, Filename}</c></tag> + <tag><c>{ok, <anno>Filename</anno>}</c></tag> <item> <p>If this node's file server is not a slave, the file was opened by this node's file server, (this implies that - <c>Pid</c> must be a local pid) and the file is not - closed. <c>Filename</c> is the filename in flat string + <c><anno>Pid</anno></c> must be a local pid) and the file is not + closed. <c><anno>Filename</anno></c> is the filename in flat string format.</p> </item> <tag><c>undefined</c></tag> @@ -1125,21 +1012,15 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>position(IoDevice, Location) -> {ok, NewPosition} | {error, Reason}</name> + <name name="position" arity="2"/> <fsummary>Set position in a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v> - <v> Offset = int()</v> - <v>NewPosition = int()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> - <p>Sets the position of the file referenced by <c>IoDevice</c> - to <c>Location</c>. Returns <c>{ok, NewPosition}</c> (as + <p>Sets the position of the file referenced by <c><anno>IoDevice</anno></c> + to <c><anno>Location</anno></c>. Returns + <c>{ok, <anno>NewPosition</anno>}</c> (as absolute offset) if successful, otherwise - <c>{error, Reason}</c>. <c>Location</c> is one of - the following:</p> + <c>{error, <anno>Reason</anno>}</c>. <c><anno>Location</anno></c> is + one of the following:</p> <taglist> <tag><c>Offset</c></tag> <item> @@ -1167,7 +1048,8 @@ f.txt: {person, "kalle", 25}. <taglist> <tag><c>einval</c></tag> <item> - <p>Either <c>Location</c> was illegal, or it evaluated to a + <p>Either <c><anno>Location</anno></c> was illegal, or it + evaluated to a negative offset in the file. Note that if the resulting position is a negative value, the result is an error, and after the call the file position is undefined.</p> @@ -1176,22 +1058,14 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>pread(IoDevice, LocNums) -> {ok, DataL} | eof | {error, Reason}</name> + <name name="pread" arity="2"/> <fsummary>Read from a file at certain positions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>LocNums = [{Location, Number}]</v> - <v> Location -- see position/2</v> - <v> Number = int()</v> - <v>DataL = [Data]</v> - <v> Data = [char()] | binary()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Performs a sequence of <c>pread/3</c> in one operation, which is more efficient than calling them one at a time. - Returns <c>{ok, [Data, ...]}</c> or <c>{error, Reason}</c>, - where each <c>Data</c>, the result of the corresponding + Returns <c>{ok, [<anno>Data</anno>, ...]}</c> or + <c>{error, <anno>Reason</anno>}</c>, + where each <c><anno>Data</anno></c>, the result of the corresponding <c>pread</c>, is either a list or a binary depending on the mode of the file, or <c>eof</c> if the requested position was beyond end of file.</p> @@ -1199,76 +1073,53 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>pread(IoDevice, Location, Number) -> {ok, Data} | eof | {error, Reason}</name> + <name name="pread" arity="3"/> <fsummary>Read from a file at a certain position</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Location -- see position/2</v> - <v>Number = int()</v> - <v>Data = [char()] | binary()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Combines <c>position/2</c> and <c>read/2</c> in one operation, which is more efficient than calling them one at a - time. If <c>IoDevice</c> has been opened in raw mode, some - restrictions apply: <c>Location</c> is only allowed to be an + time. If <c><anno>IoDevice</anno></c> has been opened in raw mode, + some restrictions apply: <c><anno>Location</anno></c> is only allowed + to be an integer; and the current position of the file is undefined after the operation.</p> <p>As the position is given as a byte-offset, special caution has to be taken when working with files where <c>encoding</c> is set to something else than <c>latin1</c>, as not every byte position will be a valid character boundary on such a file.</p> </desc> </func> <func> - <name>pwrite(IoDevice, LocBytes) -> ok | {error, {N, Reason}}</name> + <name name="pwrite" arity="2"/> <fsummary>Write to a file at certain positions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>LocBytes = [{Location, Bytes}]</v> - <v> Location -- see position/2</v> - <v> Bytes = iodata()</v> - <v>N = int()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Performs a sequence of <c>pwrite/3</c> in one operation, which is more efficient than calling them one at a time. - Returns <c>ok</c> or <c>{error, {N, Reason}}</c>, where - <c>N</c> is the number of successful writes that was done + Returns <c>ok</c> or <c>{error, {<anno>N</anno>, + <anno>Reason</anno>}}</c>, where + <c><anno>N</anno></c> is the number of successful writes that was done before the failure.</p> <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, caution must be taken to set the position on a correct character boundary, see <seealso marker="#position/2">position/2</seealso> for details.</p> </desc> </func> <func> - <name>pwrite(IoDevice, Location, Bytes) -> ok | {error, Reason}</name> + <name name="pwrite" arity="3"/> <fsummary>Write to a file at a certain position</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Location -- see position/2</v> - <v>Bytes = iodata()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Combines <c>position/2</c> and <c>write/2</c> in one operation, which is more efficient than calling them one at a - time. If <c>IoDevice</c> has been opened in raw mode, some - restrictions apply: <c>Location</c> is only allowed to be an + time. If <c><anno>IoDevice</anno></c> has been opened in raw mode, + some restrictions apply: <c><anno>Location</anno></c> is only allowed + to be an integer; and the current position of the file is undefined after the operation.</p> <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, caution must be taken to set the position on a correct character boundary, see <seealso marker="#position/2">position/2</seealso> for details.</p> </desc> </func> <func> - <name>read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason}</name> + <name name="read" arity="2"/> <fsummary>Read from a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Number = int()</v> - <v>Data = [char()] | binary()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> - <p>Reads <c>Number</c> bytes/characters from the file referenced by - <c>IoDevice</c>. The functions <c>read/2</c>, <c>pread/3</c> + <p>Reads <c><anno>Number</anno></c> bytes/characters from the file + referenced by <c><anno>IoDevice</anno></c>. The functions + <c>read/2</c>, <c>pread/3</c> and <c>read_line/1</c> are the only ways to read from a file opened in raw mode (although they work for normally opened files, too).</p> @@ -1276,7 +1127,7 @@ f.txt: {person, "kalle", 25}. <p>Also if <c>encoding</c> is set to something else than <c>latin1</c>, the <c>read/3</c> call will fail if the data contains characters larger than 255, why the <seealso marker="stdlib:io">io(3)</seealso> module is to be preferred when reading such a file.</p> <p>The function returns:</p> <taglist> - <tag><c>{ok, Data}</c></tag> + <tag><c>{ok, <anno>Data</anno>}</c></tag> <item> <p>If the file was opened in binary mode, the read bytes are returned in a binary, otherwise in a list. The list or @@ -1285,10 +1136,10 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>eof</c></tag> <item> - <p>Returned if <c>Number>0</c> and end of file was reached - before anything at all could be read.</p> + <p>Returned if <c><anno>Number</anno>>0</c> and end of file was + reached before anything at all could be read.</p> </item> - <tag><c>{error, Reason}</c></tag> + <tag><c>{error, <anno>Reason</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -1307,17 +1158,14 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>read_file(Filename) -> {ok, Binary} | {error, Reason}</name> + <name name="read_file" arity="1"/> <fsummary>Read a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Binary = binary()</v> - <v>Reason = ext_posix() | terminated | system_limit</v> - </type> <desc> - <p>Returns <c>{ok, Binary}</c>, where <c>Binary</c> is a binary - data object that contains the contents of <c>Filename</c>, or - <c>{error, Reason}</c> if an error occurs.</p> + <p>Returns <c>{ok, <anno>Binary</anno>}</c>, where + <c><anno>Binary</anno></c> is a binary + data object that contains the contents of + <c><anno>Filename</anno></c>, or + <c>{error, <anno>Reason</anno>}</c> if an error occurs.</p> <p>Typical error reasons:</p> <taglist> <tag><c>enoent</c></tag> @@ -1346,17 +1194,13 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>read_file_info(Filename) -> {ok, FileInfo} | {error, Reason}</name> + <name name="read_file_info" arity="1"/> <fsummary>Get information about a file</fsummary> - <type> - <v>Filename = name()</v> - <v>FileInfo = #file_info{}</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Retrieves information about a file. Returns - <c>{ok, FileInfo}</c> if successful, otherwise - <c>{error, Reason}</c>. <c>FileInfo</c> is a record + <c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise + <c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c> + is a record <c>file_info</c>, defined in the Kernel include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> @@ -1364,7 +1208,7 @@ f.txt: {person, "kalle", 25}. -include_lib("kernel/include/file.hrl").</code> <p>The record <c>file_info</c> contains the following fields.</p> <taglist> - <tag><c>size = int()</c></tag> + <tag><c>size = integer()</c></tag> <item> <p>Size of file in bytes.</p> </item> @@ -1391,7 +1235,7 @@ f.txt: {person, "kalle", 25}. the file or the inode was changed. In Windows, it is the create time.</p> </item> - <tag><c>mode = int()</c></tag> + <tag><c>mode = integer()</c></tag> <item> <p>The file permissions as the sum of the following bit values:</p> @@ -1422,33 +1266,33 @@ f.txt: {person, "kalle", 25}. <p>On Unix platforms, other bits than those listed above may be set.</p> </item> - <tag><c>links = int()</c></tag> + <tag><c>links = integer()</c></tag> <item> <p>Number of links to the file (this will always be 1 for file systems which have no concept of links).</p> </item> - <tag><c>major_device = int()</c></tag> + <tag><c>major_device = integer()</c></tag> <item> <p>Identifies the file system where the file is located. In Windows, the number indicates a drive as follows: 0 means A:, 1 means B:, and so on.</p> </item> - <tag><c>minor_device = int()</c></tag> + <tag><c>minor_device = integer()</c></tag> <item> <p>Only valid for character devices on Unix. In all other cases, this field is zero.</p> </item> - <tag><c>inode = int()</c></tag> + <tag><c>inode = integer()</c></tag> <item> <p>Gives the <c>inode</c> number. On non-Unix file systems, this field will be zero.</p> </item> - <tag><c>uid = int()</c></tag> + <tag><c>uid = integer()</c></tag> <item> <p>Indicates the owner of the file. Will be zero for non-Unix file systems.</p> </item> - <tag><c>gid = int()</c></tag> + <tag><c>gid = integer()</c></tag> <item> <p>Gives the group that the owner of the file belongs to. Will be zero for non-Unix file systems.</p> @@ -1474,21 +1318,16 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>read_line(IoDevice) -> {ok, Data} | eof | {error, Reason}</name> + <name name="read_line" arity="1"/> <fsummary>Read a line from a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Data = [char()] | binary()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Reads a line of bytes/characters from the file referenced by - <c>IoDevice</c>. Lines are defined to be delimited by the linefeed (LF, <c>\n</c>) character, but any carriage return (CR, <c>\r</c>) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned <em>including</em> the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of <seealso marker="stdlib:io#get_line/2">io:get_line/2</seealso>. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.</p> + <c><anno>IoDevice</anno></c>. Lines are defined to be delimited by the linefeed (LF, <c>\n</c>) character, but any carriage return (CR, <c>\r</c>) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned <em>including</em> the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of <seealso marker="stdlib:io#get_line/2">io:get_line/2</seealso>. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.</p> <p>The function can be used on files opened in <c>raw</c> mode. It is however inefficient to use it on <c>raw</c> files if the file is not opened with the option <c>{read_ahead, Size}</c> specified, why combining <c>raw</c> and <c>{read_ahead, Size}</c> is highly recommended when opening a text file for raw line oriented reading.</p> <p>If <c>encoding</c> is set to something else than <c>latin1</c>, the <c>read_line/1</c> call will fail if the data contains characters larger than 255, why the <seealso marker="stdlib:io">io(3)</seealso> module is to be preferred when reading such a file.</p> <p>The function returns:</p> <taglist> - <tag><c>{ok, Data}</c></tag> + <tag><c>{ok, <anno>Data</anno>}</c></tag> <item> <p>One line from the file is returned, including the trailing LF, but with CRLF sequences replaced by a single LF (see above).</p> <p>If the file was opened in binary mode, the read bytes are @@ -1499,7 +1338,7 @@ f.txt: {person, "kalle", 25}. <p>Returned if end of file was reached before anything at all could be read.</p> </item> - <tag><c>{error, Reason}</c></tag> + <tag><c>{error, <anno>Reason</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -1518,23 +1357,19 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>read_link(Name) -> {ok, Filename} | {error, Reason}</name> + <name name="read_link" arity="1"/> <fsummary>See what a link is pointing to</fsummary> - <type> - <v>Name = name()</v> - <v>Filename = string()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>This function returns <c>{ok, Filename}</c> if <c>Name</c> - refers to a symbolic link or <c>{error, Reason}</c> otherwise. + <p>This function returns <c>{ok, <anno>Filename</anno>}</c> if + <c><anno>Name</anno></c> refers to a symbolic link or + <c>{error, <anno>Reason</anno>}</c> otherwise. On platforms that do not support symbolic links, the return value will be <c>{error,enotsup}</c>.</p> <p>Typical error reasons:</p> <taglist> <tag><c>einval</c></tag> <item> - <p><c>Linkname</c> does not refer to a symbolic link.</p> + <p><c><anno>Name</anno></c> does not refer to a symbolic link.</p> </item> <tag><c>enoent</c></tag> <item> @@ -1548,34 +1383,26 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>read_link_info(Name) -> {ok, FileInfo} | {error, Reason}</name> + <name name="read_link_info" arity="1"/> <fsummary>Get information about a link or file</fsummary> - <type> - <v>Name = name()</v> - <v>FileInfo = #file_info{}, see read_file_info/1</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>This function works like <c>read_file_info/1</c>, except that - if <c>Name</c> is a symbolic link, information about the link - will be returned in the <c>file_info</c> record and + if <c><anno>Name</anno></c> is a symbolic link, information about + the link will be returned in the <c>file_info</c> record and the <c>type</c> field of the record will be set to <c>symlink</c>.</p> - <p>If <c>Name</c> is not a symbolic link, this function returns + <p>If <c><anno>Name</anno></c> is not a symbolic link, this function returns exactly the same result as <c>read_file_info/1</c>. On platforms that do not support symbolic links, this function is always equivalent to <c>read_file_info/1</c>.</p> </desc> </func> <func> - <name>rename(Source, Destination) -> ok | {error, Reason}</name> + <name name="rename" arity="2"/> <fsummary>Rename a file</fsummary> - <type> - <v>Source = Destination = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> - <p>Tries to rename the file <c>Source</c> to <c>Destination</c>. + <p>Tries to rename the file <c><anno>Source</anno></c> to + <c><anno>Destination</anno></c>. It can be used to move files (and directories) between directories, but it is not sufficient to specify the destination only. The destination file name must also be @@ -1593,25 +1420,28 @@ f.txt: {person, "kalle", 25}. <tag><c>eacces</c></tag> <item> <p>Missing read or write permissions for the parent - directories of <c>Source</c> or <c>Destination</c>. On + directories of <c><anno>Source</anno></c> or + <c><anno>Destination</anno></c>. On some platforms, this error is given if either - <c>Source</c> or <c>Destination</c> is open.</p> + <c><anno>Source</anno></c> or <c><anno>Destination</anno></c> + is open.</p> </item> <tag><c>eexist</c></tag> <item> - <p><c>Destination</c> is not an empty directory. On some - platforms, also given when <c>Source</c> and - <c>Destination</c> are not of the same type.</p> + <p><c><anno>Destination</anno></c> is not an empty directory. + On some platforms, also given when <c><anno>Source</anno></c> and + <c><anno>Destination</anno></c> are not of the same type.</p> </item> <tag><c>einval</c></tag> <item> - <p><c>Source</c> is a root directory, or <c>Destination</c> - is a sub-directory of <c>Source</c>.</p> + <p><c><anno>Source</anno></c> is a root directory, or + <c><anno>Destination</anno></c> + is a sub-directory of <c><anno>Source</anno></c>.</p> </item> <tag><c>eisdir</c></tag> <item> - <p><c>Destination</c> is a directory, but <c>Source</c> is - not.</p> + <p><c><anno>Destination</anno></c> is a directory, but + <c><anno>Source</anno></c> is not.</p> </item> <tag><c>enoent</c></tag> <item> @@ -1619,35 +1449,28 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>enotdir</c></tag> <item> - <p><c>Source</c> is a directory, but <c>Destination</c> is - not.</p> + <p><c><anno>Source</anno></c> is a directory, but + <c><anno>Destination</anno></c> is not.</p> </item> <tag><c>exdev</c></tag> <item> - <p><c>Source</c> and <c>Destination</c> are on different - file systems.</p> + <p><c><anno>Source</anno></c> and <c><anno>Destination</anno></c> + are on different file systems.</p> </item> </taglist> </desc> </func> <func> - <name>script(Filename) -> {ok, Value} | {error, Reason}</name> + <name name="script" arity="1"/> <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Value = term()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. Returns one of the following:</p> <taglist> - <tag><c>{ok, Value}</c></tag> + <tag><c>{ok, <anno>Value</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c>Value</c> is + <p>The file was read and evaluated. <c><anno>Value</anno></c> is the value of the last expression.</p> </item> <tag><c>{error, atom()}</c></tag> @@ -1656,7 +1479,8 @@ f.txt: {person, "kalle", 25}. See <seealso marker="#open/2">open/2</seealso> for a list of typical error codes.</p> </item> - <tag><c>{error, {Line, Mod, Term}}</c></tag> + <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, + <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang expressions in the file. Use <c>format_error/1</c> to @@ -1667,33 +1491,21 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>script(Filename, Bindings) -> {ok, Value} | {error, Reason}</name> + <name name="script" arity="2"/> <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Bindings -- see erl_eval(3)</v> - <v>Value = term()</v> - <v>Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term}</v> - <v> Line, Mod, Term -- see below</v> - </type> <desc> <p>The same as <c>script/1</c> but the variable bindings - <c>Bindings</c> are used in the evaluation. See + <c><anno>Bindings</anno></c> are used in the evaluation. See <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about variable bindings.</p> </desc> </func> <func> - <name>set_cwd(Dir) -> ok | {error,Reason}</name> + <name name="set_cwd" arity="1"/> <fsummary>Set the current working directory</fsummary> - <type> - <v>Dir = name()</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Sets the current working directory of the file server to - <c>Dir</c>. Returns <c>ok</c> if successful.</p> + <c><anno>Dir</anno></c>. Returns <c>ok</c> if successful.</p> <p>Typical error reasons are:</p> <taglist> <tag><c>enoent</c></tag> @@ -1702,8 +1514,8 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>enotdir</c></tag> <item> - <p>A component of <c>Dir</c> is not a directory. On some - platforms, <c>enoent</c> is returned.</p> + <p>A component of <c><anno>Dir</anno></c> is not a directory. + On some platforms, <c>enoent</c> is returned.</p> </item> <tag><c>eacces</c></tag> <item> @@ -1712,23 +1524,21 @@ f.txt: {person, "kalle", 25}. </item> <tag><c>badarg</c></tag> <item> - <p><c>Filename</c> had an improper type, such as tuple.</p> + <p><c><anno>Dir</anno></c> had an improper type, + such as tuple.</p> </item> </taglist> <warning> - <p>In a future release, a bad type for the <c>Filename</c> + <p>In a future release, a bad type for the + <c><anno>Dir</anno></c> argument will probably generate an exception.</p> <p></p> </warning> </desc> </func> <func> - <name>sync(IoDevice) -> ok | {error, Reason}</name> + <name name="sync" arity="1"/> <fsummary>Synchronizes the in-memory state of a file with that on the physical medium</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Makes sure that any buffers kept by the operating system (not by the Erlang runtime system) are written to disk. On @@ -1743,12 +1553,8 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>datasync(IoDevice) -> ok | {error, Reason}</name> + <name name="datasync" arity="1"/> <fsummary>Synchronizes the in-memory data of a file, ignoring most of its metadata, with that on the physical medium</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> <p>Makes sure that any buffers kept by the operating system (not by the Erlang runtime system) are written to disk. In @@ -1770,32 +1576,23 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>truncate(IoDevice) -> ok | {error, Reason}</name> + <name name="truncate" arity="1"/> <fsummary>Truncate a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> - <p>Truncates the file referenced by <c>IoDevice</c> at + <p>Truncates the file referenced by <c><anno>IoDevice</anno></c> at the current position. Returns <c>ok</c> if successful, - otherwise <c>{error, Reason}</c>.</p> + otherwise <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>write(IoDevice, Bytes) -> ok | {error, Reason}</name> + <name name="write" arity="2"/> <fsummary>Write to a file</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Bytes = iodata()</v> - <v>Reason = ext_posix() | terminated</v> - </type> <desc> - <p>Writes <c>Bytes</c> to the file referenced by - <c>IoDevice</c>. This function is the only way to write to a + <p>Writes <c><anno>Bytes</anno></c> to the file referenced by + <c><anno>IoDevice</anno></c>. This function is the only way to write to a file opened in raw mode (although it works for normally opened files, too). Returns <c>ok</c> if successful, and - <c>{error, Reason}</c> otherwise.</p> + <c>{error, <anno>Reason</anno>}</c> otherwise.</p> <p>If the file is opened with <c>encoding</c> set to something else than <c>latin1</c>, each byte written might result in several bytes actually being written to the file, as the byte range 0..255 might represent anything between one and four bytes depending on value and UTF encoding type.</p> <p>Typical error reasons are:</p> <taglist> @@ -1811,18 +1608,14 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>write_file(Filename, Bytes) -> ok | {error, Reason}</name> + <name name="write_file" arity="2"/> <fsummary>Write a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Bytes = iodata()</v> - <v>Reason = ext_posix() | terminated | system_limit</v> - </type> <desc> - <p>Writes the contents of the iodata term <c>Bytes</c> to the - file <c>Filename</c>. The file is created if it does not + <p>Writes the contents of the iodata term <c><anno>Bytes</anno></c> + to the file <c><anno>Filename</anno></c>. + The file is created if it does not exist. If it exists, the previous contents are - overwritten. Returns <c>ok</c>, or <c>{error, Reason}</c>.</p> + overwritten. Returns <c>ok</c>, or <c>{error, <anno>Reason</anno>}</c>.</p> <p>Typical error reasons are:</p> <taglist> <tag><c>enoent</c></tag> @@ -1851,33 +1644,23 @@ f.txt: {person, "kalle", 25}. </desc> </func> <func> - <name>write_file(Filename, Bytes, Modes) -> ok | {error, Reason}</name> + <name name="write_file" arity="3"/> <fsummary>Write a file</fsummary> - <type> - <v>Filename = name()</v> - <v>Bytes = iodata()</v> - <v>Modes = [Mode] -- see open/2</v> - <v>Reason = ext_posix() | terminated | system_limit</v> - </type> <desc> <p>Same as <c>write_file/2</c>, but takes a third argument - <c>Modes</c>, a list of possible modes, see + <c><anno>Modes</anno></c>, a list of possible modes, see <seealso marker="#open/2">open/2</seealso>. The mode flags <c>binary</c> and <c>write</c> are implicit, so they should not be used.</p> </desc> </func> <func> - <name>write_file_info(Filename, FileInfo) -> ok | {error, Reason}</name> + <name name="write_file_info" arity="2"/> <fsummary>Change information about a file</fsummary> - <type> - <v>Filename = name()</v> - <v>FileInfo = #file_info{} -- see also read_file_info/1</v> - <v>Reason = ext_posix()</v> - </type> <desc> <p>Change file information. Returns <c>ok</c> if successful, - otherwise <c>{error, Reason}</c>. <c>FileInfo</c> is a record + otherwise <c>{error, <anno>Reason</anno>}</c>. + <c><anno>FileInfo</anno></c> is a record <c>file_info</c>, defined in the Kernel include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> @@ -1901,7 +1684,7 @@ f.txt: {person, "kalle", 25}. time). On Windows, this field is the new creation time to set for the file.</p> </item> - <tag><c>mode = int()</c></tag> + <tag><c>mode = integer()</c></tag> <item> <p>The file permissions as the sum of the following bit values:</p> @@ -1932,12 +1715,12 @@ f.txt: {person, "kalle", 25}. <p>On Unix platforms, other bits than those listed above may be set.</p> </item> - <tag><c>uid = int()</c></tag> + <tag><c>uid = integer()</c></tag> <item> <p>Indicates the owner of the file. Ignored for non-Unix file systems.</p> </item> - <tag><c>gid = int()</c></tag> + <tag><c>gid = integer()</c></tag> <item> <p>Gives the group that the owner of the file belongs to. Ignored non-Unix file systems.</p> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index fb09092f1c..5ceb82ae41 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2010</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -65,77 +65,71 @@ <item><seealso marker="#seealso">SEE ALSO</seealso></item> <item><seealso marker="#authors">AUTHORS</seealso></item> </list> + <marker id="types"></marker> </section> - <section> - <marker id="types"></marker> - <title>DATA TYPES</title> - <marker id="type-assoc_id"></marker> - <taglist> - <tag><c>assoc_id()</c></tag> - <item> + <datatypes> + <datatype> + <name name="assoc_id"/> + <desc> <p>An opaque term returned in for example #sctp_paddr_change{} that identifies an association for an SCTP socket. The term is opaque except for the special value <c>0</c> that has a - meaning such as "the whole endpoint" or "all future associations".</p> - <marker id="type-charlist"></marker> - </item> - <tag><c>charlist() = [char()]</c></tag> - <item> <marker id="type-iolist"></marker> -</item> - <tag><c>iolist() = [char() | binary()]</c></tag> - <item> <marker id="type-ip_address"></marker> -</item> - <tag><c>ip_address()</c></tag> - <item> + meaning such as "the whole endpoint" or "all future associations". + </p> + </desc> + </datatype> + <datatype> + <name name="hostname"/> + </datatype> + <datatype> + <name name="ip_address"/> + <desc> <p>Represents an address of an SCTP socket. It is a tuple as explained in <seealso marker="inet">inet(3)</seealso>.</p> - <marker id="type-port_number"></marker> - </item> - <tag><c>port_number() = 0 .. 65535</c></tag> - <item> <marker id="type-posix"></marker> -</item> - <tag><c>posix()</c></tag> - <item> - <p>See - <seealso marker="inet#error_codes">inet(3); POSIX Error Codes.</seealso></p> - <marker id="type-sctp_option"></marker> - </item> - <tag><c>sctp_option()</c></tag> - <item> + </desc> + </datatype> + <datatype> + <name name="port_number"/> + </datatype> + <datatype> + <name name="posix"/> + <desc> + <p>See <seealso marker="inet#error_codes"> + inet(3); POSIX Error Codes</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="sctp_option"/> + <desc> <p>One of the <seealso marker="#options">SCTP Socket Options.</seealso></p> <marker id="type-sctp_socket"></marker> - </item> - <tag><c>sctp_socket()</c></tag> - <item> + </desc> + </datatype> + <datatype> + <name name="sctp_socket"/> + <desc> <p>Socket identifier returned from <c>open/*</c>.</p> - <marker id="type-timeout"></marker> - </item> - <tag><c>timeout() = int() | infinity</c></tag> - <item> - <p>Timeout used in SCTP connect and receive calls.</p> - </item> - </taglist> - <marker id="exports"></marker> - </section> + <marker id="exports"></marker> + </desc> + </datatype> + </datatypes> + <funcs> <func> - <name>abort(sctp_socket(), Assoc) -> ok | {error, posix()}</name> + <name name="abort" arity="2"/> <fsummary>Abnormally terminate the association given by Assoc, without flushing of unsent data</fsummary> - <type> - <v>Assoc = #sctp_assoc_change{}</v> - </type> <desc> - <p>Abnormally terminates the association given by <c>Assoc</c>, without + <p>Abnormally terminates the association given by <c><anno>Assoc</anno></c>, without flushing of unsent data. The socket itself remains open. Other associations opened on this socket are still valid, and it can be used in new associations.</p> </desc> </func> <func> - <name>close(sctp_socket()) -> ok | {error, posix()}</name> + <name name="close" arity="1"/> <fsummary>Completely close the socket and all associations on it</fsummary> <desc> <p>Completely closes the socket and all associations on it. The unsent @@ -148,35 +142,26 @@ </desc> </func> <func> - <name>connect(Socket, Addr, Port, Opts) -> {ok,Assoc} | {error, posix()}</name> + <name name="connect" arity="4"/> <fsummary>Same as <c>connect(Socket, Addr, Port, Opts, infinity)</c>.</fsummary> <desc> - <p>Same as <c>connect(Socket, Addr, Port, Opts, infinity)</c>.</p> + <p>Same as <c>connect(<anno>Socket</anno>, <anno>Addr</anno>, <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> </desc> </func> <func> - <name>connect(Socket, Addr, Port, [Opt], Timeout) -> {ok, Assoc} | {error, posix()}</name> + <name name="connect" arity="5"/> <fsummary>Establish a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>Addr = ip_address() | Host</v> - <v>Port = port_number()</v> - <v>Opt = sctp_option()</v> - <v>Timeout = timeout()</v> - <v>Host = atom() | string()</v> - <v>Assoc = #sctp_assoc_change{}</v> - </type> <desc> - <p>Establishes a new association for the socket <c>Socket</c>, + <p>Establishes a new association for the socket <c><anno>Socket</anno></c>, with the peer (SCTP server socket) given by - <c>Addr</c> and <c>Port</c>. The <c>Timeout</c>, + <c><anno>Addr</anno></c> and <c><anno>Port</anno></c>. The <c><anno>Timeout</anno></c>, is expressed in milliseconds. A socket can be associated with multiple peers.</p> - <p><b>WARNING:</b>Using a value of <c>Timeout</c> less than + <p><b>WARNING:</b>Using a value of <c><anno>Timeout</anno></c> less than the maximum time taken by the OS to establish an association (around 4.5 minutes if the default values from RFC 4960 are used) can result in inconsistent or incorrect return values. This is especially - relevant for associations sharing the same <c>Socket</c> + relevant for associations sharing the same <c><anno>Socket</anno></c> (i.e. source address and port) since the controlling process blocks until <c>connect/*</c> returns. <seealso marker="#connect_init/4">connect_init/*</seealso> @@ -185,7 +170,7 @@ <p><marker id="record-sctp_assoc_change"></marker> The result of <c>connect/*</c> is an <c>#sctp_assoc_change{}</c> event which contains, in particular, the new - <seealso marker="#type-assoc_id">Association ID:</seealso></p> + <seealso marker="#type-assoc_id">Association ID</seealso>.</p> <pre> #sctp_assoc_change{ state = atom(), @@ -198,13 +183,13 @@ giving an <c>sctp_initmsg</c> option to <c>connect</c> as in:</p> <pre> - connect(Socket, Ip, Port, + connect(<anno>Socket</anno>, Ip, <anno>Port</anno>, [{sctp_initmsg,#sctp_initmsg{num_ostreams=OutStreams, max_instreams=MaxInStreams}}]) </pre> - <p>All options <c>Opt</c> are set on the socket before the + <p>All options <c><anno>Opt</anno></c> are set on the socket before the association is attempted. If an option record has got undefined field values, the options record is first read from the socket - for those values. In effect, <c>Opt</c> option records only + for those values. In effect, <c><anno>Opt</anno></c> option records only define field values to change before connecting.</p> <p>The returned <c>outbound_streams</c> and <c>inbound_streams</c> are the actual stream numbers on the socket, which may be different @@ -242,27 +227,19 @@ </desc> </func> <func> - <name>connect_init(Socket, Addr, Port, Opts) -> ok | {error, posix()}</name> + <name name="connect_init" arity="4"/> <fsummary>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>.</fsummary> <desc> - <p>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>.</p> + <p>Same as <c>connect_init(<anno>Socket</anno>, <anno>Addr</anno>, <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> </desc> </func> <func> - <name>connect_init(Socket, Addr, Port, [Opt], Timeout) -> ok | {error, posix()}</name> + <name name="connect_init" arity="5"/> <fsummary>Initiate a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>Addr = ip_address() | Host</v> - <v>Port = port_number()</v> - <v>Opt = sctp_option()</v> - <v>Timeout = timeout()</v> - <v>Host = atom() | string()</v> - </type> <desc> - <p>Initiates a new association for the socket <c>Socket</c>, + <p>Initiates a new association for the socket <c><anno>Socket</anno></c>, with the peer (SCTP server socket) given by - <c>Addr</c> and <c>Port</c>.</p> + <c><anno>Addr</anno></c> and <c><anno>Port</anno></c>.</p> <p>The fundamental difference between this API and <c>connect/*</c> is that the return value is that of the underlying OS connect(2) system call. If <c>ok</c> is returned @@ -275,64 +252,52 @@ active option.</p> <p>The parameters are as described in <seealso marker="#connect/5">connect/*</seealso>, with the - exception of the <c>Timeout</c> value.</p> - <p>The timer associated with <c>Timeout</c> only supervises - IP resolution of <c>Addr</c></p> + exception of the <c><anno>Timeout</anno></c> value.</p> + <p>The timer associated with <c><anno>Timeout</anno></c> only supervises + IP resolution of <c><anno>Addr</anno></c></p> </desc> </func> <func> - <name>controlling_process(sctp_socket(), pid()) -> ok</name> + <name name="controlling_process" arity="2"/> <fsummary>Assign a new controlling process pid to the socket</fsummary> <desc> - <p>Assigns a new controlling process Pid to Socket. Same implementation + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to <c><anno>Socket</anno></c>. Same implementation as <c>gen_udp:controlling_process/2</c>.</p> </desc> </func> <func> - <name>eof(Socket, Assoc) -> ok | {error, Reason}</name> + <name name="eof" arity="2"/> <fsummary>Gracefully terminate the association given by Assoc, with flushing of all unsent data</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>Assoc = #sctp_assoc_change{}</v> - </type> <desc> - <p>Gracefully terminates the association given by <c>Assoc</c>, with + <p>Gracefully terminates the association given by <c><anno>Assoc</anno></c>, with flushing of all unsent data. The socket itself remains open. Other associations opened on this socket are still valid, and it can be used in new associations.</p> </desc> </func> <func> - <name>listen(Socket, IsServer) -> ok | {error, Reason}</name> + <name name="listen" arity="2"/> <fsummary>Set up a socket to listen.</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>IsServer = bool()</v> - </type> <desc> <p>Sets up a socket to listen on the IP address and port number - it is bound to. IsServer must be 'true' or 'false'. + it is bound to. <c><anno>IsServer</anno></c> must be <c>true</c> + or <c>false</c>. In the contrast to TCP, in SCTP there is no listening queue length. - If IsServer is 'true' the socket accepts new associations, i.e. + If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e. it will become an SCTP server socket.</p> </desc> </func> <func> - <name>open() -> {ok, Socket} | {error, posix()}</name> - <name>open(Port) -> {ok, Socket} | {error, posix()}</name> - <name>open([Opt]) -> {ok, Socket} | {error, posix()}</name> - <name>open(Port, [Opt]) -> {ok, Socket} | {error, posix()}</name> + <name name="open" arity="0"/> + <name name="open" arity="1" clause_i="1"/> + <name name="open" arity="1" clause_i="2"/> + <name name="open" arity="2"/> <fsummary>Create an SCTP socket and bind it to local addresses</fsummary> - <type> - <v>Opt = {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option()</v> - <v>IP = ip_address() | any | loopback</v> - <v>Port = port_number()</v> - </type> <desc> <p>Creates an SCTP socket and binds it to the local addresses - specified by all <c>{ip,IP}</c> (or synonymously <c>{ifaddr,IP}</c>) + specified by all <c>{ip,<anno>IP</anno>}</c> (or synonymously <c>{ifaddr,<anno>IP</anno>}</c>) options (this feature is called SCTP multi-homing). - The default <c>IP</c> and <c>Port</c> are <c>any</c> + The default <c><anno>IP</anno></c> and <c><anno>Port</anno></c> are <c>any</c> and <c>0</c>, meaning bind to all local addresses on any one free port.</p> <p>A default set of socket <seealso marker="#options">options</seealso> @@ -345,27 +310,16 @@ </desc> </func> <func> - <name>recv(sctp_socket()) -> {ok, {FromIP, FromPort, AncData, BinMsg}} | {error, Reason}</name> - <name>recv(sctp_socket(), timeout()) -> {ok, {FromIP, FromPort, AncData, Data}} | {error, Reason}</name> + <name name="recv" arity="1"/> + <name name="recv" arity="2"/> <fsummary>Receive a message from a socket</fsummary> - <type> - <v>FromIP = ip_address()</v> - <v>FromPort = port_number()</v> - <v>AncData = [#sctp_sndrcvinfo{}]</v> - <v>Data = binary() | charlist() | #sctp_sndrcvinfo{} | - #sctp_assoc_change{} | #sctp_paddr_change{} | - #sctp_adaptation_event{} </v> - <v>Reason = posix() | #sctp_send_failed{} | #scpt_paddr_change{} | - #sctp_pdapi_event{} | #sctp_remote_error{} | - #sctp_shutdown_event{}</v> - </type> <desc> - <p>Receives the <c>Data</c> message from any association of the socket. + <p>Receives the <c><anno>Data</anno></c> message from any association of the socket. If the receive times out <c>{error,timeout</c> is returned. The default timeout is <c>infinity</c>. - <c>FromIP</c> and <c>FromPort</c> indicate the sender's address.</p> - <p><c>AncData</c> is a list of Ancillary Data items which - may be received along with the main <c>Data</c>. + <c><anno>FromIP</anno></c> and <c><anno>FromPort</anno></c> indicate the sender's address.</p> + <p><c><anno>AncData</anno></c> is a list of Ancillary Data items which + may be received along with the main <c><anno>Data</anno></c>. This list can be empty, or contain a single <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> record, if receiving of such ancillary data is enabled @@ -375,10 +329,10 @@ provide an easy way of determining the association and stream over which the message has been received. (An alternative way would be to get the Association ID from the - <c>FromIP</c> and <c>FromPort</c> using the + <c><anno>FromIP</anno></c> and <c><anno>FromPort</anno></c> using the <seealso marker="#option-sctp_get_peer_addr_info">sctp_get_peer_addr_info</seealso> socket option, but this would still not produce the Stream number).</p> - <p>The actual <c>Data</c> received may be a <c>binary()</c>, + <p>The actual <c><anno>Data</anno></c> received may be a <c>binary()</c>, or <c>list()</c> of bytes (integers in the range 0 through 255) depending on the socket mode, or an SCTP Event. <marker id="sctp_events"></marker> @@ -476,15 +430,10 @@ </desc> </func> <func> - <name>send(Socket, SndRcvInfo, Data) -> ok | {error, Reason}</name> + <name name="send" arity="3"/> <fsummary>Send a message using an <c>#sctp_sndrcvinfo{}</c>record</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>SndRcvInfo = #sctp_sndrcvinfo{}</v> - <v>Data = binary() | iolist()</v> - </type> <desc> - <p>Sends the <c>Data</c> message with all sending parameters from a + <p>Sends the <c><anno>Data</anno></c> message with all sending parameters from a <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> record. This way, the user can specify the PPID (passed to the remote end) and Context (passed to the local SCTP layer) which can be used @@ -494,21 +443,15 @@ </desc> </func> <func> - <name>send(Socket, Assoc, Stream, Data) -> ok | {error, Reason}</name> + <name name="send" arity="4"/> <fsummary>Send a message over an existing association and given stream</fsummary> - <type> - <v>Socket = sctp_socket()</v> - <v>Assoc = #sctp_assoc_change{} | assoc_id()</v> - <v>Stream = integer()</v> - <v>Data = binary() | iolist()</v> - </type> <desc> - <p>Sends <c>Data</c> message over an existing association and given + <p>Sends <c><anno>Data</anno></c> message over an existing association and given stream.</p> </desc> </func> <func> - <name>error_string(integer()) -> ok | string() | undefined</name> + <name name="error_string" arity="1"/> <fsummary>Translate an SCTP error number into a string</fsummary> <desc> <p>Translates an SCTP error number from for example diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index aa171c77c2..f1d42d9faa 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -63,36 +63,45 @@ do_recv(Sock, Bs) -> <p>For more examples, see the <seealso marker="#examples">examples</seealso> section.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ip_address() - see inet(3) - -posix() - see inet(3) + <datatypes> + <datatype> + <name name="hostname"/> + </datatype> + <datatype> + <name name="ip_address"/> + <desc> + <p>Represents an address of a TCP socket. + It is a tuple as explained in + <seealso marker="inet">inet(3)</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="port_number"/> + </datatype> + <datatype> + <name name="posix"/> + <desc> + <p>See <seealso marker="inet#error_codes"> + inet(3); POSIX Error Codes</seealso>.</p> + </desc> + </datatype> + <datatype> + <name><marker id="type-socket">socket()</marker></name> + <desc> + <p>As returned by accept/1,2 and connect/3,4.</p> + <marker id="connect"></marker> + </desc> + </datatype> + </datatypes> -socket() - as returned by accept/1,2 and connect/3,4</code> - <marker id="connect"></marker> - </section> <funcs> <func> - <name>connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}</name> - <name>connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}</name> + <name name="connect" arity="3"/> + <name name="connect" arity="4"/> <fsummary>Connect to a TCP port</fsummary> - <type> - <v>Address = string() | atom() | ip_address()</v> - <v>Port = 0..65535</v> - <v>Options = [Opt]</v> - <v> Opt -- see below</v> - <v>Timeout = int() | infinity</v> - <v>Socket = socket()</v> - <v>Reason = posix()</v> - </type> <desc> - <p>Connects to a server on TCP port <c>Port</c> on the host - with IP address <c>Address</c>. The <c>Address</c> argument + <p>Connects to a server on TCP port <c><anno>Port</anno></c> on the host + with IP address <c><anno>Address</anno></c>. The <c><anno>Address</anno></c> argument can be either a hostname, or an IP address.</p> <p>The available options are:</p> <taglist> @@ -127,13 +136,13 @@ socket() <item> <p>Set up the socket for IPv4.</p> </item> - <tag>Opt</tag> + <tag><c>Opt</c></tag> <item> <p>See <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> </item> </taglist> - <p>Packets can be sent to the returned socket <c>Socket</c> + <p>Packets can be sent to the returned socket <c><anno>Socket</anno></c> using <c>send/2</c>. Packets sent from the peer are delivered as messages:</p> <code type="none"> @@ -148,7 +157,7 @@ socket() <p>unless <c>{active, false}</c> is specified in the option list for the socket, in which case packets are retrieved by calling <c>recv/2</c>.</p> - <p>The optional <c>Timeout</c> parameter specifies a timeout in + <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in milliseconds. The default value is <c>infinity</c>.</p> <note> <p>The default values for options given to <c>connect</c> can @@ -159,19 +168,12 @@ socket() </desc> </func> <func> - <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> + <name name="listen" arity="2"/> <fsummary>Set up a socket to listen on a port</fsummary> - <type> - <v>Port = 0..65535</v> - <v>Options = [Opt]</v> - <v> Opt -- see below</v> - <v>ListenSocket -- see below</v> - <v>Reason = posix()</v> - </type> <desc> - <p>Sets up a socket to listen on the port <c>Port</c> on + <p>Sets up a socket to listen on the port <c><anno>Port</anno></c> on the local host.</p> - <p>If <c>Port == 0</c>, the underlying OS assigns an available + <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns an available port number, use <c>inet:port/1</c> to retrieve it.</p> <p>The available options are:</p> <taglist> @@ -214,7 +216,7 @@ socket() <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> </item> </taglist> - <p>The returned socket <c>ListenSocket</c> can only be used in + <p>The returned socket <c><anno>ListenSocket</anno></c> can only be used in calls to <c>accept/1,2</c>.</p> <note> <p>The default values for options given to <c>listen</c> can @@ -225,27 +227,23 @@ socket() </desc> </func> <func> - <name>accept(ListenSocket) -> {ok, Socket} | {error, Reason}</name> - <name>accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason}</name> + <name name="accept" arity="1"/> + <name name="accept" arity="2"/> <fsummary>Accept an incoming connection request on a listen socket</fsummary> - <type> - <v>ListenSocket -- see listen/2</v> - <v>Timeout = int() | infinity</v> - <v>Socket = socket()</v> - <v>Reason = closed | timeout | posix()</v> - </type> + <type_desc variable="ListenSocket">Returned by <c>listen/2</c>. + </type_desc> <desc> <p>Accepts an incoming connection request on a listen socket. - <c>Socket</c> must be a socket returned from <c>listen/2</c>. - <c>Timeout</c> specifies a timeout value in ms, defaults to + <c><anno>Socket</anno></c> must be a socket returned from <c>listen/2</c>. + <c><anno>Timeout</anno></c> specifies a timeout value in ms, defaults to <c>infinity</c>.</p> - <p>Returns <c>{ok, Socket}</c> if a connection is established, - or <c>{error, closed}</c> if <c>ListenSocket</c> is closed, + <p>Returns <c>{ok, <anno>Socket</anno>}</c> if a connection is established, + or <c>{error, closed}</c> if <c><anno>ListenSocket</anno></c> is closed, or <c>{error, timeout}</c> if no connection is established within the specified time. May also return a POSIX error value if something else goes wrong, see inet(3) for possible error values.</p> - <p>Packets can be sent to the returned socket <c>Socket</c> + <p>Packets can be sent to the returned socket <c><anno>Socket</anno></c> using <c>send/2</c>. Packets sent from the peer are delivered as messages:</p> <code type="none"> @@ -264,13 +262,8 @@ socket() </desc> </func> <func> - <name>send(Socket, Packet) -> ok | {error, Reason}</name> + <name name="send" arity="2"/> <fsummary>Send a packet</fsummary> - <type> - <v>Socket = socket()</v> - <v>Packet = [char()] | binary()</v> - <v>Reason = posix()</v> - </type> <desc> <p>Sends a packet on a socket. </p> <p>There is no <c>send</c> call with timeout option, you use the @@ -279,70 +272,52 @@ socket() </desc> </func> <func> - <name>recv(Socket, Length) -> {ok, Packet} | {error, Reason}</name> - <name>recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason}</name> + <name name="recv" arity="2"/> + <name name="recv" arity="3"/> <fsummary>Receive a packet from a passive socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Length = int()</v> - <v>Packet = [char()] | binary() | HttpPacket</v> - <v>Timeout = int() | infinity</v> - <v>Reason = closed | posix()</v> - <v>HttpPacket = see the description of <c>HttpPacket</c> in <seealso marker="erts:erlang#decode_packet/3">erlang:decode_packet/3</seealso></v> - </type> + <type_desc variable="HttpPacket">See the description of + <c>HttpPacket</c> in <seealso marker="erts:erlang#decode_packet/3"> + erlang:decode_packet/3</seealso>. + </type_desc> <desc> <p>This function receives a packet from a socket in passive mode. A closed socket is indicated by a return value <c>{error, closed}</c>.</p> - <p>The <c>Length</c> argument is only meaningful when + <p>The <c><anno>Length</anno></c> argument is only meaningful when the socket is in <c>raw</c> mode and denotes the number of - bytes to read. If <c>Length</c> = 0, all available bytes are - returned. If <c>Length</c> > 0, exactly <c>Length</c> + bytes to read. If <c><anno>Length</anno></c> = 0, all available bytes are + returned. If <c><anno>Length</anno></c> > 0, exactly <c><anno>Length</anno></c> bytes are returned, or an error; possibly discarding less - than <c>Length</c> bytes of data when the socket gets closed + than <c><anno>Length</anno></c> bytes of data when the socket gets closed from the other side.</p> - <p>The optional <c>Timeout</c> parameter specifies a timeout in + <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in milliseconds. The default value is <c>infinity</c>.</p> </desc> </func> <func> - <name>controlling_process(Socket, Pid) -> ok | {error, Reason}</name> + <name name="controlling_process" arity="2"/> <fsummary>Change controlling process of a socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Pid = pid()</v> - <v>Reason = closed | not_owner | posix()</v> - </type> <desc> - <p>Assigns a new controlling process <c>Pid</c> to - <c>Socket</c>. The controlling process is the process which + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to + <c><anno>Socket</anno></c>. The controlling process is the process which receives messages from the socket. If called by any other process than the current controlling process, <c>{error, eperm}</c> is returned.</p> </desc> </func> <func> - <name>close(Socket) -> ok | {error, Reason}</name> + <name name="close" arity="1"/> <fsummary>Close a TCP socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Reason = posix()</v> - </type> <desc> <p>Closes a TCP socket.</p> </desc> </func> <func> - <name>shutdown(Socket, How) -> ok | {error, Reason}</name> + <name name="shutdown" arity="2"/> <fsummary>Immediately close a socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>How = read | write | read_write</v> - <v>Reason = posix()</v> - </type> <desc> <p>Immediately close a socket in one or two directions.</p> - <p><c>How == write</c> means closing the socket for writing, + <p><c><anno>How</anno> == write</c> means closing the socket for writing, reading from it is still possible.</p> <p>To be able to handle that the peer has done a shutdown on the write side, the <c>{exit_on_close, false}</c> option diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 71f2e9bd83..c0e783f508 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,32 +34,43 @@ with sockets using the UDP protocol.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ip_address() - see inet(3) - -posix() - see inet(3) + <datatypes> + <datatype> + <name name="hostname"/> + </datatype> + <datatype> + <name name="ip_address"/> + <desc> + <p>Represents an address of a TCP socket. + It is a tuple as explained in + <seealso marker="inet">inet(3)</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="port_number"/> + </datatype> + <datatype> + <name name="posix"/> + <desc> + <p>See <seealso marker="inet#error_codes"> + inet(3); POSIX Error Codes</seealso>.</p> + </desc> + </datatype> + <datatype> + <name><marker id="type-socket">socket()</marker></name> + <desc> + <p>As returned by open/1,2.</p> + </desc> + </datatype> + </datatypes> -socket() - as returned by open/1,2</code> - </section> <funcs> <func> - <name>open(Port) -> {ok, Socket} | {error, Reason}</name> - <name>open(Port, Options) -> {ok, Socket} | {error, Reason}</name> + <name name="open" arity="1"/> + <name name="open" arity="2"/> <fsummary>Associate a UDP port number with the process calling it</fsummary> - <type> - <v>Port = 0..65535</v> - <v>Options = [Opt]</v> - <v> Opt -- see below</v> - <v>Socket = socket()</v> - <v>Reason = posix()</v> - </type> <desc> - <p>Associates a UDP port number (<c>Port</c>) with the calling + <p>Associates a UDP port number (<c><anno>Port</anno></c>) with the calling process.</p> <p>The available options are:</p> <taglist> @@ -96,7 +107,7 @@ socket() <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> </item> </taglist> - <p>The returned socket <c>Socket</c> is used to send packets + <p>The returned socket <c><anno>Socket</anno></c> is used to send packets from this port with <c>send/4</c>. When UDP packets arrive at the opened port, they are delivered as messages:</p> <code type="none"> @@ -110,66 +121,42 @@ socket() binary if the option <c>binary</c> was specified.</p> <p>Default value for the receive buffer option is <c>{recbuf, 8192}</c>.</p> - <p>If <c>Port == 0</c>, the underlying OS assigns a free UDP + <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns a free UDP port, use <c>inet:port/1</c> to retrieve it.</p> </desc> </func> <func> - <name>send(Socket, Address, Port, Packet) -> ok | {error, Reason}</name> + <name name="send" arity="4"/> <fsummary>Send a packet</fsummary> - <type> - <v>Socket = socket()</v> - <v>Address = string() | atom() | ip_address()</v> - <v>Port = 0..65535</v> - <v>Packet = [char()] | binary()</v> - <v>Reason = not_owner | posix()</v> - </type> <desc> <p>Sends a packet to the specified address and port. - The <c>Address</c> argument can be either a hostname, or an + The <c><anno>Address</anno></c> argument can be either a hostname, or an IP address.</p> </desc> </func> <func> - <name>recv(Socket, Length) -> {ok, {Address, Port, Packet}} | {error, Reason}</name> - <name>recv(Socket, Length, Timeout) -> {ok, {Address, Port, Packet}} | {error, Reason}</name> + <name name="recv" arity="2"/> + <name name="recv" arity="3"/> <fsummary>Receive a packet from a passive socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Length = int()</v> - <v>Address = ip_address()</v> - <v>Port = 0..65535</v> - <v>Packet = [char()] | binary()</v> - <v>Timeout = int() | infinity</v> - <v>Reason = not_owner | posix()</v> - </type> <desc> <p>This function receives a packet from a socket in passive mode.</p> - <p>The optional <c>Timeout</c> parameter specifies a timeout in + <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in milliseconds. The default value is <c>infinity</c>.</p> </desc> </func> <func> - <name>controlling_process(Socket, Pid) -> ok</name> + <name name="controlling_process" arity="2"/> <fsummary>Change controlling process of a socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Pid = pid()</v> - </type> <desc> - <p>Assigns a new controlling process <c>Pid</c> to - <c>Socket</c>. The controlling process is the process which + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to + <c><anno>Socket</anno></c>. The controlling process is the process which receives messages from the socket.</p> </desc> </func> <func> - <name>close(Socket) -> ok | {error, Reason}</name> + <name name="close" arity="1"/> <fsummary>Close a UDP socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Reason = not_owner | posix()</v> - </type> <desc> <p>Closes a UDP socket.</p> </desc> diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml index 077109d6c9..304a9b1d88 100644 --- a/lib/kernel/doc/src/global.xml +++ b/lib/kernel/doc/src/global.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -106,45 +106,37 @@ </description> + <datatypes> + <datatype> + <name name="id"/> + </datatype> + </datatypes> + <funcs> <func> - <name>del_lock(Id)</name> - <name>del_lock(Id, Nodes) -> void()</name> + <name name="del_lock" arity="1"/> + <name name="del_lock" arity="2"/> <fsummary>Delete a lock</fsummary> - <type> - <v>Id = {ResourceId, LockRequesterId}</v> - <v> ResourceId = term()</v> - <v> LockRequesterId = term()</v> - <v>Nodes = [node()]</v> - </type> <desc> - <p>Deletes the lock <c>Id</c> synchronously.</p> + <p>Deletes the lock <c><anno>Id</anno></c> synchronously.</p> </desc> </func> <func> - <name>notify_all_name(Name, Pid1, Pid2) -> none</name> + <name name="notify_all_name" arity="3"/> <fsummary>Name resolving function that notifies both pids</fsummary> - <type> - <v>Name = term()</v> - <v>Pid1 = Pid2 = pid()</v> - </type> <desc> <p>This function can be used as a name resolving function for <c>register_name/3</c> and <c>re_register_name/3</c>. It unregisters both pids, and sends the message - <c>{global_name_conflict, Name, OtherPid}</c> to both + <c>{global_name_conflict, <anno>Name</anno>, OtherPid}</c> to both processes.</p> </desc> </func> <func> - <name>random_exit_name(Name, Pid1, Pid2) -> Pid1 | Pid2</name> + <name name="random_exit_name" arity="3"/> <fsummary>Name resolving function that kills one pid</fsummary> - <type> - <v>Name = term()</v> - <v>Pid1 = Pid2 = pid()</v> - </type> <desc> <p>This function can be used as a name resolving function for <c>register_name/3</c> and <c>re_register_name/3</c>. It @@ -154,33 +146,27 @@ </func> <func> - <name>random_notify_name(Name, Pid1, Pid2) -> Pid1 | Pid2</name> + <name name="random_notify_name" arity="3"/> <fsummary>Name resolving function that notifies one pid</fsummary> - <type> - <v>Name = term()</v> - <v>Pid1 = Pid2 = pid()</v> - </type> <desc> <p>This function can be used as a name resolving function for <c>register_name/3</c> and <c>re_register_name/3</c>. It randomly chooses one of the pids for registration, and sends - the message <c>{global_name_conflict, Name}</c> to the other + the message <c>{global_name_conflict, <anno>Name</anno>}</c> to the other pid.</p> </desc> </func> <func> - <name>register_name(Name, Pid)</name> - <name>register_name(Name, Pid, Resolve) -> yes | no</name> + <name name="register_name" arity="2"/> + <name name="register_name" arity="3"/> <fsummary>Globally register a name for a pid</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Resolve = fun() or {Module, Function} where</v> - <v> Resolve(Name, Pid, Pid2) -> Pid | Pid2 | none</v> - </type> + <type name="method"/> + <type_desc name="method">{<c>Module</c>, <c>Function</c>} + is also allowed + </type_desc> <desc> - <p>Globally associates the name <c>Name</c> with a pid, that is, + <p>Globally associates the name <c><anno>Name</anno></c> with a pid, that is, Globally notifies all nodes of a new global name in a network of Erlang nodes.</p> @@ -188,7 +174,7 @@ of the globally registered names that already exist. The network is also informed of any global names in newly connected nodes. If any name clashes are discovered, - the <c>Resolve</c> function is called. Its purpose is to + the <c><anno>Resolve</anno></c> function is called. Its purpose is to decide which pid is correct. If the function crashes, or returns anything other than one of the pids, the name is unregistered. This function is called once for each name @@ -196,7 +182,7 @@ <p>There are three pre-defined resolve functions: <c>random_exit_name/3</c>, <c>random_notify_name/3</c>, and - <c>notify_all_name/3</c>. If no <c>Resolve</c> function is + <c>notify_all_name/3</c>. If no <c><anno>Resolve</anno></c> function is defined, <c>random_exit_name</c> is used. This means that one of the two registered processes will be selected as correct while the other is killed.</p> @@ -225,78 +211,63 @@ </func> <func> - <name>registered_names() -> [Name]</name> + <name name="registered_names" arity="0"/> <fsummary>All globally registered names</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> <p>Returns a lists of all globally registered names.</p> </desc> </func> <func> - <name>re_register_name(Name, Pid)</name> - <name>re_register_name(Name, Pid, Resolve) -> void()</name> + <name name="re_register_name" arity="2"/> + <name name="re_register_name" arity="3"/> <fsummary>Atomically re-register a name</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Resolve = fun() or {Module, Function} where</v> - <v> Resolve(Name, Pid, Pid2) -> Pid | Pid2 | none</v> - </type> + <type name="method"/> + <type_desc name="method">{<c>Module</c>, <c>Function</c>} + is also allowed + </type_desc> <desc> - <p>Atomically changes the registered name <c>Name</c> on all - nodes to refer to <c>Pid</c>.</p> + <p>Atomically changes the registered name <c><anno>Name</anno></c> on all + nodes to refer to <c><anno>Pid</anno></c>.</p> - <p>The <c>Resolve</c> function has the same behavior as in + <p>The <c><anno>Resolve</anno></c> function has the same behavior as in <c>register_name/2,3</c>.</p> </desc> </func> <func> - <name>send(Name, Msg) -> Pid</name> + <name name="send" arity="2"/> <fsummary>Send a message to a globally registered pid</fsummary> - <type> - <v>Name = term()</v> - <v>Msg = term()</v> - <v>Pid = pid()</v> - </type> <desc> - <p>Sends the message <c>Msg</c> to the pid globally registered - as <c>Name</c>.</p> + <p>Sends the message <c><anno>Msg</anno></c> to the pid globally registered + as <c><anno>Name</anno></c>.</p> - <p>Failure: If <c>Name</c> is not a globally registered + <p>Failure: If <c><anno>Name</anno></c> is not a globally registered name, the calling function will exit with reason - <c>{badarg, {Name, Msg}}</c>.</p> + <c>{badarg, {<anno>Name</anno>, <anno>Msg</anno>}}</c>.</p> </desc> </func> <func> - <name>set_lock(Id)</name> - <name>set_lock(Id, Nodes)</name> - <name>set_lock(Id, Nodes, Retries) -> boolean()</name> + <name name="set_lock" arity="1"/> + <name name="set_lock" arity="2"/> + <name name="set_lock" arity="3"/> <fsummary>Set a lock on the specified nodes</fsummary> - <type> - <v>Id = {ResourceId, LockRequesterId}</v> - <v> ResourceId = term()</v> - <v> LockRequesterId = term()</v> - <v>Nodes = [node()]</v> - <v>Retries = int() >= 0 | infinity</v> - </type> + <type name="id"/> + <type name="retries"/> <desc> <p>Sets a lock on the specified nodes (or on all nodes if none - are specified) on <c>ResourceId</c> for - <c>LockRequesterId</c>. If a lock already exists on - <c>ResourceId</c> for another requester than - <c>LockRequesterId</c>, and <c>Retries</c> is not equal to 0, + are specified) on <c><anno>ResourceId</anno></c> for + <c><anno>LockRequesterId</anno></c>. If a lock already exists on + <c><anno>ResourceId</anno></c> for another requester than + <c><anno>LockRequesterId</anno></c>, and <c><anno>Retries</anno></c> is not equal to 0, the process sleeps for a while and will try to execute - the action later. When <c>Retries</c> attempts have been made, + the action later. When <c><anno>Retries</anno></c> attempts have been made, <c>false</c> is returned, otherwise <c>true</c>. If - <c>Retries</c> is <c>infinity</c>, <c>true</c> is eventually + <c><anno>Retries</anno></c> is <c>infinity</c>, <c>true</c> is eventually returned (unless the lock is never released).</p> - <p>If no value for <c>Retries</c> is given, <c>infinity</c> is + <p>If no value for <c><anno>Retries</anno></c> is given, <c>infinity</c> is used.</p> <p>This function is completely synchronous.</p> @@ -315,7 +286,7 @@ application to detect and rectify a deadlock.</p> <note> - <p>Some values of <c>ResourceId</c> should be avoided or + <p>Some values of <c><anno>ResourceId</anno></c> should be avoided or Erlang/OTP will not work properly. A list of resources to avoid: <c>global</c>, <c>dist_ac</c>, <c>mnesia_table_lock</c>, <c>mnesia_adjust_log_writes</c>, @@ -326,7 +297,7 @@ </func> <func> - <name>sync() -> void()</name> + <name name="sync" arity="0"/> <fsummary>Synchronize the global name server</fsummary> <desc> <p>Synchronizes the global name server with all nodes known to @@ -335,56 +306,45 @@ the global name server will receive global information from all nodes. This function can be called when new nodes are added to the network.</p> + <p>The only possible error reason <c>Reason</c> is + <c>{"global_groups definition error", Error}</c>.</p> </desc> </func> <func> - <name>trans(Id, Fun)</name> - <name>trans(Id, Fun, Nodes)</name> - <name>trans(Id, Fun, Nodes, Retries) -> Res | aborted</name> + <name name="trans" arity="2"/> + <name name="trans" arity="3"/> + <name name="trans" arity="4"/> <fsummary>Micro transaction facility</fsummary> - <type> - <v>Id = {ResourceId, LockRequesterId}</v> - <v> ResourceId = term()</v> - <v> LockRequesterId = term()</v> - <v>Fun = fun() | {M, F}</v> - <v>Nodes = [node()]</v> - <v>Retries = int() >= 0 | infinity</v> - <v>Res = term()</v> - </type> + <type name="retries"/> + <type name="trans_fun"/> <desc> - <p>Sets a lock on <c>Id</c> (using <c>set_lock/3</c>). If this - succeeds, <c>Fun()</c> is evaluated and the result <c>Res</c> + <p>Sets a lock on <c><anno>Id</anno></c> (using <c>set_lock/3</c>). If this + succeeds, <c><anno>Fun</anno>()</c> is evaluated and the result <c><anno>Res</anno></c> is returned. Returns <c>aborted</c> if the lock attempt - failed. If <c>Retries</c> is set to <c>infinity</c>, + failed. If <c><anno>Retries</anno></c> is set to <c>infinity</c>, the transaction will not abort.</p> <p><c>infinity</c> is the default setting and will be used if - no value is given for <c>Retries</c>.</p> + no value is given for <c><anno>Retries</anno></c>.</p> </desc> </func> <func> - <name>unregister_name(Name) -> void()</name> + <name name="unregister_name" arity="1"/> <fsummary>Remove a globally registered name for a pid</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> - <p>Removes the globally registered name <c>Name</c> from + <p>Removes the globally registered name <c><anno>Name</anno></c> from the network of Erlang nodes.</p> </desc> </func> <func> - <name>whereis_name(Name) -> pid() | undefined</name> + <name name="whereis_name" arity="1"/> <fsummary>Get the pid with a given globally registered name</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> <p>Returns the pid with the globally registered name - <c>Name</c>. Returns <c>undefined</c> if the name is not + <c><anno>Name</anno></c>. Returns <c>undefined</c> if the name is not globally registered.</p> </desc> </func> diff --git a/lib/kernel/doc/src/global_group.xml b/lib/kernel/doc/src/global_group.xml index 4facf4a4aa..abf6178fc4 100644 --- a/lib/kernel/doc/src/global_group.xml +++ b/lib/kernel/doc/src/global_group.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1998</year><year>2009</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -42,26 +42,7 @@ <seealso marker="kernel_app">kernel(6)</seealso>, <seealso marker="config">config(4)</seealso>:</p> <code type="none"> -{global_groups, [GroupTuple]}</code> - <p>Types:</p> - <list type="bulleted"> - <item><c>GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]}</c></item> - <item><c>GroupName = atom()</c> (naming a global group)</item> - <item><c>PublishType = normal | hidden</c></item> - <item><c>Node = atom()</c> (naming a node)</item> - </list> - <p>A <c>GroupTuple</c> without <c>PublishType</c> is the same as a - <c>GroupTuple</c> with <c>PublishType == normal</c>.</p> - <p>A node started with the command line flag <c>-hidden</c>, see - <seealso marker="erts:erl">erl(1)</seealso>, is said to be a - <em>hidden</em> node. A hidden node will establish hidden - connections to nodes not part of the same global group, but - normal (visible) connections to nodes part of the same global - group.</p> - <p>A global group defined with <c>PublishType == hidden</c>, is - said to be a hidden global group. All nodes in a hidden global - group are hidden nodes, regardless if they are started with - the <c>-hidden</c> command line flag or not.</p> +{global_groups, [GroupTuple :: group_tuple()]}</code> <p>For the processes and nodes to run smoothly using the global group functionality, the following criteria must be met:</p> <list type="bulleted"> @@ -82,14 +63,44 @@ <p>In the following description, a <em>group node</em> is a node belonging to the same global group as the local node.</p> </description> + <datatypes> + <datatype> + <name name="group_tuple"/> + <desc> + <p>A <c>GroupTuple</c> without <c>PublishType</c> is the same as a + <c>GroupTuple</c> with <c>PublishType == normal</c>.</p> + </desc> + </datatype> + <datatype> + <name name="group_name"/> + </datatype> + <datatype> + <name name="publish_type"/> + <desc> + <p>A node started with the command line flag <c>-hidden</c>, see + <seealso marker="erts:erl">erl(1)</seealso>, is said to be a + <em>hidden</em> node. A hidden node will establish hidden + connections to nodes not part of the same global group, but + normal (visible) connections to nodes part of the same global + group.</p> + <p>A global group defined with <c>PublishType == hidden</c>, is + said to be a hidden global group. All nodes in a hidden global + group are hidden nodes, regardless if they are started with + the <c>-hidden</c> command line flag or not.</p> + </desc> + </datatype> + <datatype> + <name name="name"/> + <desc><p>A registered name.</p></desc> + </datatype> + <datatype> + <name name="where"/> + </datatype> + </datatypes> <funcs> <func> - <name>global_groups() -> {GroupName, GroupNames} | undefined</name> + <name name="global_groups" arity="0"/> <fsummary>Return the global group names</fsummary> - <type> - <v>GroupName = atom()</v> - <v>GroupNames = [GroupName]</v> - </type> <desc> <p>Returns a tuple containing the name of the global group the local node belongs to, and the list of all other known @@ -98,53 +109,52 @@ </desc> </func> <func> - <name>info() -> [{Item, Info}]</name> + <name name="info" arity="0"/> <fsummary>Information about global groups</fsummary> - <type> - <v>Item, Info -- see below</v> - </type> + <type name="info_item"/> + <type name="sync_state"/> <desc> <p>Returns a list containing information about the global groups. Each element of the list is a tuple. The order of the tuples is not defined.</p> <taglist> - <tag><c>{state, State}</c></tag> + <tag><c>{state, <anno>State</anno>}</c></tag> <item> <p>If the local node is part of a global group, - <c>State == synced</c>. If no global groups are defined, - <c>State == no_conf</c>.</p> + <c><anno>State</anno> == synced</c>. If no global groups are defined, + <c><anno>State</anno> == no_conf</c>.</p> </item> - <tag><c>{own_group_name, GroupName}</c></tag> + <tag><c>{own_group_name, <anno>GroupName</anno>}</c></tag> <item> <p>The name (atom) of the group that the local node belongs to.</p> </item> - <tag><c>{own_group_nodes, Nodes}</c></tag> + <tag><c>{own_group_nodes, <anno>Nodes</anno>}</c></tag> <item> <p>A list of node names (atoms), the group nodes.</p> </item> - <tag><c>{synced_nodes, Nodes}</c></tag> + <tag><c>{synced_nodes, <anno>Nodes</anno>}</c></tag> <item> <p>A list of node names, the group nodes currently synchronized with the local node.</p> </item> - <tag><c>{sync_error, Nodes}</c></tag> + <tag><c>{sync_error, <anno>Nodes</anno>}</c></tag> <item> <p>A list of node names, the group nodes with which the local node has failed to synchronize.</p> </item> - <tag><c>{no_contact, Nodes}</c></tag> + <tag><c>{no_contact, <anno>Nodes</anno>}</c></tag> <item> <p>A list of node names, the group nodes to which there are currently no connections.</p> </item> - <tag><c>{other_groups, Groups}</c></tag> + <tag><c>{other_groups, <anno>Groups</anno>}</c></tag> <item> - <p><c>Groups</c> is a list of tuples - <c>{GroupName, Nodes}</c>, specifying the name and nodes + <p><c><anno>Groups</anno></c> is a list of tuples + <c>{<anno>GroupName</anno>, <anno>Nodes</anno>}</c>, specifying the name and nodes of the other global groups.</p> </item> - <tag><c>{monitoring, Pids}</c></tag> + <tag><c>{monitoring, <anno>Pids</anno>}</c></tag> <item> <p>A list of pids, specifying the processes which have subscribed to <c>nodeup</c> and <c>nodedown</c> messages.</p> @@ -153,73 +163,52 @@ </desc> </func> <func> - <name>monitor_nodes(Flag) -> ok </name> + <name name="monitor_nodes" arity="1"/> <fsummary>Subscribe to node status changes</fsummary> - <type> - <v>Flag = bool()</v> - </type> <desc> - <p>Depending on <c>Flag</c>, the calling process starts - subscribing (<c>Flag == true</c>) or stops subscribing - (<c>Flag == false</c>) to node status change messages.</p> + <p>Depending on <c><anno>Flag</anno></c>, the calling process starts + subscribing (<c><anno>Flag</anno> == true</c>) or stops subscribing + (<c><anno>Flag</anno> == false</c>) to node status change messages.</p> <p>A process which has subscribed will receive the messages <c>{nodeup, Node}</c> and <c>{nodedown, Node}</c> when a group node connects or disconnects, respectively.</p> </desc> </func> <func> - <name>own_nodes() -> Nodes</name> + <name name="own_nodes" arity="0"/> <fsummary>Return the group nodes</fsummary> - <type> - <v>Nodes = [Node]</v> - <v> Node = node()</v> - </type> <desc> <p>Returns the names of all group nodes, regardless of their current status.</p> </desc> </func> <func> - <name>registered_names(Where) -> Names</name> + <name name="registered_names" arity="1"/> <fsummary>Return globally registered names</fsummary> - <type> - <v>Where = {node, Node} | {group, GroupName}</v> - <v> Node = node()</v> - <v> GroupName = atom()</v> - <v>Names = [Name]</v> - <v> Name = atom()</v> - </type> <desc> <p>Returns a list of all names which are globally registered on the specified node or in the specified global group.</p> </desc> </func> <func> - <name>send(Name, Msg) -> pid() | {badarg, {Name, Msg}}</name> - <name>send(Where, Name, Msg) -> pid() | {badarg, {Name, Msg}}</name> + <name name="send" arity="2"/> + <name name="send" arity="3"/> <fsummary>Send a message to a globally registered pid</fsummary> - <type> - <v>Where = {node, Node} | {group, GroupName}</v> - <v> Node = node()</v> - <v> GroupName = atom()</v> - <v>Name = atom()</v> - <v>Msg = term()</v> - </type> <desc> - <p>Searches for <c>Name</c>, globally registered on + <p>Searches for <c><anno>Name</anno></c>, globally registered on the specified node or in the specified global group, or -- - if the <c>Where</c> argument is not provided -- in any global + if the <c><anno>Where</anno></c> argument is not provided -- in any global group. The global groups are searched in the order in which they appear in the value of the <c>global_groups</c> configuration parameter.</p> - <p>If <c>Name</c> is found, the message <c>Msg</c> is sent to + <p>If <c><anno>Name</anno></c> is found, the message <c><anno>Msg</anno></c> is sent to the corresponding pid. The pid is also the return value of the function. If the name is not found, the function returns - <c>{badarg, {Name, Msg}}</c>.</p> + <c>{badarg, {<anno>Name</anno>, <anno>Msg</anno>}}</c>.</p> </desc> </func> <func> - <name>sync() -> ok</name> + <name name="sync" arity="0"/> <fsummary>Synchronize the group nodes</fsummary> <desc> <p>Synchronizes the group nodes, that is, the global name @@ -235,23 +224,17 @@ </desc> </func> <func> - <name>whereis_name(Name) -> pid() | undefined</name> - <name>whereis_name(Where, Name) -> pid() | undefined</name> + <name name="whereis_name" arity="1"/> + <name name="whereis_name" arity="2"/> <fsummary>Get the pid with a given globally registered name</fsummary> - <type> - <v>Where = {node, Node} | {group, GroupName}</v> - <v> Node = node()</v> - <v> GroupName = atom()</v> - <v>Name = atom()</v> - </type> <desc> - <p>Searches for <c>Name</c>, globally registered on + <p>Searches for <c><anno>Name</anno></c>, globally registered on the specified node or in the specified global group, or -- if - the <c>Where</c> argument is not provided -- in any global + the <c><anno>Where</anno></c> argument is not provided -- in any global group. The global groups are searched in the order in which they appear in the value of the <c>global_groups</c> configuration parameter.</p> - <p>If <c>Name</c> is found, the corresponding pid is returned. + <p>If <c><anno>Name</anno></c> is found, the corresponding pid is returned. If the name is not found, the function returns <c>undefined</c>.</p> </desc> diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 0df699572d..e2dbcbe63d 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -76,11 +76,8 @@ </description> <funcs> <func> - <name>set_cmd(Cmd) -> ok | {error, {bad_cmd, Cmd}}</name> + <name name="set_cmd" arity="1"/> <fsummary>Set a temporary reboot command</fsummary> - <type> - <v>Cmd = string()</v> - </type> <desc> <p>Sets a temporary reboot command. This command is used if a <c>HEART_COMMAND</c> other than the one specified with @@ -88,12 +85,12 @@ the system. The new Erlang runtime system will (if it misbehaves) use the environment variable <c>HEART_COMMAND</c> to reboot.</p> - <p>Limitations: The length of the <c>Cmd</c> command string + <p>Limitations: The length of the <c><anno>Cmd</anno></c> command string must be less than 2047 characters.</p> </desc> </func> <func> - <name>clear_cmd() -> ok</name> + <name name="clear_cmd" arity="0"/> <fsummary>Clear the temporary boot command</fsummary> <desc> <p>Clears the temporary boot command. If the system terminates, @@ -101,11 +98,8 @@ </desc> </func> <func> - <name>get_cmd() -> {ok, Cmd}</name> + <name name="get_cmd" arity="0"/> <fsummary>Get the temporary reboot command</fsummary> - <type> - <v>Cmd = string()</v> - </type> <desc> <p>Get the temporary reboot command. If the command is cleared, the empty string will be returned.</p> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index f05a224f33..fd843b00d9 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -54,33 +54,6 @@ $ <input>erl -sname test -kernel \</input> <input>inet_default_listen_options '[{delay_send,true}]'</input></pre> <p>Note that the default option <c>{active, true}</c> currently cannot be changed, for internal reasons.</p> - </description> - - <section> - <title>DATA TYPES</title> - <code type="none"> -#hostent{h_addr_list = [ip_address()] % list of addresses for this host - h_addrtype = inet | inet6 - h_aliases = [hostname()] % list of aliases - h_length = int() % length of address in bytes - h_name = hostname() % official name for host - The record is defined in the Kernel include file "inet.hrl" - Add the following directive to the module: - -include_lib("kernel/include/inet.hrl"). - -hostname() = atom() | string() - -ip_address() = {N1,N2,N3,N4} % IPv4 - | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 - Ni = 0..255 - Ki = 0..65535 - -posix() - an atom which is named from the Posix error codes used in - Unix, and in the runtime libraries of most C compilers - -socket() - see gen_tcp(3), gen_udp(3)</code> <p>Addresses as inputs to functions can be either a string or a tuple. For instance, the IP address 150.236.20.73 can be passed to <c>gethostbyaddr/1</c> either as the string "150.236.20.73" @@ -109,24 +82,58 @@ fe80::204:acff:fe17:bf38 {ok,{192,168,42,2}} 2> <input>inet_parse:address("FFFF::192.168.42.2").</input> {ok,{65535,0,0,0,0,0,49320,10754}}</pre> - </section> + </description> + + <datatypes> + <datatype> + <name name="hostent"/> + <desc> + <p>The record is defined in the Kernel include file "inet.hrl". + Add the following directive to the module:</p> +<code>-include_lib("kernel/include/inet.hrl").</code></desc> + </datatype> + <datatype> + <name name="hostname"/> + </datatype> + <datatype> + <name name="ip_address"/> + </datatype> + <datatype> + <name name="ip4_address"/> + </datatype> + <datatype> + <name name="ip6_address"/> + </datatype> + <datatype> + <name name="posix"/> + <desc><p>An atom which is named from the Posix error codes + used in Unix, and in the runtime libraries of most + C compilers. See + <seealso marker="#error_codes">POSIX Error Codes</seealso>.</p> + </desc> + </datatype> + <datatype> + <name><marker id="type-socket">socket()</marker></name> + <desc><p>See <seealso marker="gen_tcp#type-socket">gen_tcp(3)</seealso> + and <seealso marker="gen_udp#type-socket">gen_udp(3)</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="family_option"/> + </datatype> + </datatypes> + <funcs> <func> - <name>close(Socket) -> ok</name> + <name name="close" arity="1"/> <fsummary>Close a socket of any type</fsummary> - <type> - <v>Socket = socket()</v> - </type> <desc> <p>Closes a socket of any type.</p> </desc> </func> <func> - <name>get_rc() -> [{Par, Val}]</name> + <name name="get_rc" arity="0"/> <fsummary>Return a list of IP configuration parameters</fsummary> - <type> - <v>Par, Val -- see below</v> - </type> <desc> <p>Returns the state of the Inet configuration database in form of a list of recorded configuration parameters. (See the @@ -135,116 +142,74 @@ fe80::204:acff:fe17:bf38 </desc> </func> <func> - <name>format_error(Posix) -> string()</name> + <name name="format_error" arity="1"/> <fsummary>Return a descriptive string for an error reason</fsummary> - <type> - <v>Posix = posix()</v> - </type> <desc> <p>Returns a diagnostic error string. See the section below - for possible <c>Posix</c> values and the corresponding + for possible <c><anno>Posix</anno></c> values and the corresponding strings.</p> </desc> </func> <func> - <name>getaddr(Host, Family) -> {ok, Address} | {error, posix()}</name> + <name name="getaddr" arity="2"/> <fsummary>Return the IP-address for a host</fsummary> - <type> - <v>Host = ip_address() | string() | atom()</v> - <v>Family = inet | inet6</v> - <v>Address = ip_address()</v> - <v>posix() = term()</v> - </type> <desc> - <p>Returns the IP-address for <c>Host</c> as a tuple of - integers. <c>Host</c> can be an IP-address, a single hostname + <p>Returns the IP-address for <c><anno>Host</anno></c> as a tuple of + integers. <c><anno>Host</anno></c> can be an IP-address, a single hostname or a fully qualified hostname.</p> </desc> </func> <func> - <name>getaddrs(Host, Family) -> {ok, Addresses} | {error, posix()}</name> + <name name="getaddrs" arity="2"/> <fsummary>Return the IP-addresses for a host</fsummary> - <type> - <v>Host = ip_address() | string() | atom()</v> - <v>Addresses = [ip_address()]</v> - <v>Family = inet | inet6</v> - </type> <desc> - <p>Returns a list of all IP-addresses for <c>Host</c>. - <c>Host</c> can be an IP-address, a single hostname or a fully + <p>Returns a list of all IP-addresses for <c><anno>Host</anno></c>. + <c><anno>Host</anno></c> can be an IP-address, a single hostname or a fully qualified hostname.</p> </desc> </func> <func> - <name>gethostbyaddr(Address) -> {ok, Hostent} | {error, posix()}</name> + <name name="gethostbyaddr" arity="1"/> <fsummary>Return a hostent record for the host with the given address</fsummary> - <type> - <v>Address = string() | ip_address()</v> - <v>Hostent = #hostent{}</v> - </type> <desc> <p>Returns a <c>hostent</c> record given an address.</p> </desc> </func> <func> - <name>gethostbyname(Name) -> {ok, Hostent} | {error, posix()}</name> + <name name="gethostbyname" arity="1"/> <fsummary>Return a hostent record for the host with the given name</fsummary> - <type> - <v>Hostname = hostname()</v> - <v>Hostent = #hostent{}</v> - </type> <desc> <p>Returns a <c>hostent</c> record given a hostname.</p> </desc> </func> <func> - <name>gethostbyname(Name, Family) -> {ok, Hostent} | {error, posix()}</name> + <name name="gethostbyname" arity="2"/> <fsummary>Return a hostent record for the host with the given name</fsummary> - <type> - <v>Hostname = hostname()</v> - <v>Family = inet | inet6</v> - <v>Hostent = #hostent{}</v> - </type> <desc> <p>Returns a <c>hostent</c> record given a hostname, restricted to the given address family.</p> </desc> </func> <func> - <name>gethostname() -> {ok, Hostname}</name> + <name name="gethostname" arity="0"/> <fsummary>Return the local hostname</fsummary> - <type> - <v>Hostname = string()</v> - </type> <desc> <p>Returns the local hostname. Will never fail.</p> </desc> </func> <func> - <name>getifaddrs() -> {ok,Iflist} | {error,posix}</name> + <name name="getifaddrs" arity="0"/> <fsummary>Return a list of interfaces and their addresses</fsummary> - <type> - <v>Iflist = {Ifname,[Ifopt]}</v> - <v>Ifname = string()</v> - <v>Ifopt = {flag,[Flag]} | {addr,Addr} | {netmask,Netmask} - | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} - | {hwaddr,Hwaddr}</v> - <v>Flag = up | broadcast | loopback | pointtopoint - | running | multicast</v> - <v>Addr = Netmask = Broadadddr = Dstaddr = ip_address()</v> - <v>Hwaddr = [byte()]</v> - </type> - </func> <desc> <p> Returns a list of 2-tuples containing interface names and the - interface's addresses. <c>Ifname</c> is a Unicode string. - <c>Hwaddr</c> is hardware dependent, e.g on Ethernet interfaces + interface's addresses. <c><anno>Ifname</anno></c> is a Unicode string. + <c><anno>Hwaddr</anno></c> is hardware dependent, e.g on Ethernet interfaces it is the 6-byte Ethernet address (MAC address (EUI-48 address)). </p> <p> - The <c>{addr,Addr}</c>, <c>{netmask,_}</c> and <c>{broadaddr,_}</c> + The <c>{addr,<anno>Addr</anno>}</c>, <c>{netmask,_}</c> and <c>{broadaddr,_}</c> tuples are repeated in the result list iff the interface has multiple addresses. If you come across an interface that has multiple <c>{flag,_}</c> or <c>{hwaddr,_}</c> tuples you have @@ -252,8 +217,8 @@ fe80::204:acff:fe17:bf38 The <c>{flag,_}</c> tuple is mandatory, all other optional. </p> <p> - Do not rely too much on the order of <c>Flag</c> atoms or - <c>Ifopt</c> tuples. There are some rules, though: + Do not rely too much on the order of <c><anno>Flag</anno></c> atoms or + <c><anno>Ifopt</anno></c> tuples. There are some rules, though: <list> <item> Immediately after <c>{addr,_}</c> follows <c>{netmask,_}</c> @@ -261,7 +226,7 @@ fe80::204:acff:fe17:bf38 <item> Immediately thereafter follows <c>{broadaddr,_}</c> if the <c>broadcast</c> flag is <em>not</em> set and the - <c>pointtopoint</c>flag <em>is</em> set. + <c>pointtopoint</c> flag <em>is</em> set. </item> <item> Any <c>{netmask,_}</c>, <c>{broadaddr,_}</c> or @@ -277,11 +242,12 @@ fe80::204:acff:fe17:bf38 </p> <p> On Windows, the data is fetched from quite different OS API - functions, so the <c>Netmask</c> and <c>Broadaddr</c> - values may be calculated, just as some <c>Flag</c> values. + functions, so the <c><anno>Netmask</anno></c> and <c><anno>Broadaddr</anno></c> + values may be calculated, just as some <c><anno>Flag</anno></c> values. You have been warned. Report flagrant bugs. </p> </desc> + </func> <func> <name>getopts(Socket, Options) -> {ok, OptionValues} | {error, posix()}</name> @@ -291,13 +257,14 @@ fe80::204:acff:fe17:bf38 <v>Options = [Opt | RawOptReq]</v> <v>Opt = atom()</v> <v>RawOptReq = {raw, Protocol, OptionNum, ValueSpec}</v> - <v>Protocol = int()</v> - <v>OptionNum = int()</v> + <v>Protocol = integer()</v> + <v>OptionNum = integer()</v> <v>ValueSpec = ValueSize | ValueBin</v> - <v>ValueSize = int()</v> + <v>ValueSize = integer()</v> <v>ValueBin = binary()</v> <v>OptionValues = [{Opt, Val} | {raw, Protocol, OptionNum, ValueBin}]</v> </type> + <type name="socket_getopt"/> <desc> <p>Gets one or more options for a socket. See <seealso marker="#setopts/2">setopts/2</seealso> @@ -419,13 +386,8 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name>peername(Socket) -> {ok, {Address, Port}} | {error, posix()}</name> + <name name="peername" arity="1"/> <fsummary>Return the address and port for the other end of a connection</fsummary> - <type> - <v>Socket = socket()</v> - <v>Address = ip_address()</v> - <v>Port = int()</v> - </type> <desc> <p>Returns the address and port for the other end of a connection.</p> @@ -436,20 +398,15 @@ fe80::204:acff:fe17:bf38 <fsummary>Return the local port number for a socket</fsummary> <type> <v>Socket = socket()</v> - <v>Port = int()</v> + <v>Port = integer()</v> </type> <desc> <p>Returns the local port number for a socket.</p> </desc> </func> <func> - <name>sockname(Socket) -> {ok, {Address, Port}} | {error, posix()}</name> + <name name="sockname" arity="1"/> <fsummary>Return the local address and port number for a socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Address = ip_address()</v> - <v>Port = int()</v> - </type> <desc> <p>Returns the local address and port number for a socket.</p> </desc> @@ -460,8 +417,8 @@ fe80::204:acff:fe17:bf38 <type> <v>Socket = term()</v> <v>Options = [{Opt, Val} | {raw, Protocol, Option, ValueBin}]</v> - <v>Protocol = int()</v> - <v>OptionNum = int()</v> + <v>Protocol = integer()</v> + <v>OptionNum = integer()</v> <v>ValueBin = binary()</v> <v> Opt, Val -- see below</v> </type> diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index d8fe23544b..bf73ccf13d 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2009</year><year>2009</year> + <year>2009</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -76,64 +76,38 @@ query is tried for the <c>alt_nameservers</c>.</p> </section> - - - - <section> - <title>DATA TYPES</title> - <p>As defined in the module - <seealso marker="kernel:inet">inet</seealso>:</p> - <code type="none"> -hostent() = #hostent{} -posix() = some atom()s -ip_address() = tuple of integers of arity 4 or 8</code> - + <datatypes> <p>Resolver types:</p> - <code type="none">These correspond to resolver options: - -res_option() = - [ {alt_nameservers, [ nameserver() ]} - | {edns, 0 | false} % Use EDNS - | {inet6, bool()} % Return IPv6 addresses - | {nameservers, [ nameserver() ]} % List of nameservers - | {recurse, bool()} % Request server recursion - | {retry, integer()} % UDP retries - | {timeout, integer()} % UDP query timeout - | {udp_payload_size, integer()} % EDNS payload size - | {usevc, bool()} ] % Use TCP (Virtual Circuit) - -nameserver() = {ip_address(),Port} - Port = integer(1..65535) - -res_error() = - formerr | - qfmterror | - servfail | - nxdomain | - notimp | - refused | - badvers | - timeout -</code> - - <p>DNS types:</p> - <marker id="dns_types"/> - <code type="none">dns_name() = string() with no adjacent dots - -rr_type() = a | aaaa | cname | gid | hinfo | ns | mb | md | mg | mf - | minfo | mx | naptr | null | ptr | soa | spf | srv | txt - | uid | uinfo | unspec | wks - -query_type() = axfr | mailb | maila | any | rr_type() - -dns_class() = in | chaos | hs | any - + <datatype> + <name name="res_option"/> + </datatype> + <datatype> + <name name="nameserver"/> + </datatype> + <datatype> + <name name="res_error"/> + </datatype> + + <p><marker id="dns_types"/>DNS types:</p> + <datatype> + <name name="dns_name"/> + <desc><p>A string with no adjacent dots.</p></desc> + </datatype> + <datatype> + <name name="rr_type"/> + </datatype> + <datatype> + <name name="dns_class"/> + </datatype> + <datatype> + <name name="dns_msg"/> + <desc> + <p>This is the start of a hiearchy of opaque data structures + that can be examined with access functions in inet_dns that + return lists of {Field,Value} tuples. The arity 2 functions + just return the value for a given field. +<pre> dns_msg() = DnsMsg - This is the start of a hiearchy of opaque data structures - that can be examined with access functions in inet_dns - that return lists of {Field,Value} tuples. The arity 2 - functions just return the value for a given field. - inet_dns:msg(DnsMsg) -> [ {header, dns_header()} | {qdlist, dns_query()} @@ -143,19 +117,21 @@ dns_msg() = DnsMsg inet_dns:msg(DnsMsg, header) -> dns_header() % for example inet_dns:msg(DnsMsg, Field) -> Value -dhs_header() = DnsHeader +dns_header() = DnsHeader inet_dns:header(DnsHeader) -> [ {id, integer()} - | {qr, bool()} + | {qr, boolean()} | {opcode, 'query' | iquery | status | integer()} - | {aa, bool()} - | {tc, bool()} - | {rd, bool()} - | {ra, bool()} - | {pr, bool()} + | {aa, boolean()} + | {tc, boolean()} + | {rd, boolean()} + | {ra, boolean()} + | {pr, boolean()} | {rcode, integer(0..16)} ] inet_dns:header(DnsHeader, Field) -> Value +query_type() = axfr | mailb | maila | any | rr_type() + dns_query() = DnsQuery inet_dns:dns_query(DnsQuery) -> [ {domain, dns_name()} @@ -179,32 +155,6 @@ dns_rr() = DnsRr | {data, dns_data()} ] inet_dns:rr(DnsRr, Field) -> Value -dns_data() = % for dns_type() - [ dns_name() % ns, md, mf, cname, mb, mg, mr, ptr - | ip_address(v4) % a - | ip_address(v6) % aaaa - | {MName,RName,Serial,Refresh,Retry,Expiry,Minimum} % soa - | {ip_address(v4),Proto,BitMap} % wks - | {CpuString,OsString} % hinfo - | {RM,EM} % minfo - | {Prio,dns_name()} % mx - | {Prio,Weight,Port,dns_name()} % srv - | {Order,Preference,Flags,Services,Regexp,dns_name()} % naptr - | [ string() ] % txt, spf - | binary() ] % null, integer() -MName, RName = dns_name() -Serial, Refresh, Retry, Expiry, Minimum = integer(), -Proto = integer() -BitMap = binary() -CpuString, OsString = string() -RM = EM = dns_name() -Prio, Weight, Port = integer() -Order, Preference = integer() -Flags, Services = string(), -Regexp = string(utf8) - - - There is an info function for the types above: inet_dns:record_type(dns_msg()) -> msg; @@ -214,26 +164,25 @@ inet_dns:record_type(dns_rr()) -> rr; inet_dns:record_type(_) -> undefined. So; inet_dns:(inet_dns:record_type(X))(X) will convert -any of these data structures into a {Field,Value} list.</code> - </section> - +any of these data structures into a {Field,Value} list.</pre></p> + </desc> + </datatype> + <datatype> + <name name="dns_data"/> + <desc><p><c><anno>Regexp</anno></c> is a string with characters encoded in the + UTF-8 coding standard.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>getbyname(Name, Type) -> {ok,hostent()} | {error,Reason}</name> - <name>getbyname(Name, Type, Timeout) -> - {ok,hostent()} | {error,Reason} - </name> + <name name="getbyname" arity="2"/> + <name name="getbyname" arity="3"/> <fsummary>Resolve a DNS record of the given type for the given host </fsummary> - <type> - <v>Name = dns_name()</v> - <v>Type = rr_type()</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Resolve a DNS record of the given type for the given host, of class <c>in</c>. On success returns a <c>hostent()</c> record with @@ -252,17 +201,10 @@ any of these data structures into a {Field,Value} list.</code> </func> <func> - <name>gethostbyaddr(Address) -> {ok,hostent()} | {error,Reason}</name> - <name>gethostbyaddr(Address, Timeout) -> - {ok,hostent()} | {error,Reason} - </name> + <name name="gethostbyaddr" arity="1"/> + <name name="gethostbyaddr" arity="2"/> <fsummary>Return a hostent record for the host with the given address </fsummary> - <type> - <v>Address = ip_address()</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Backend functions used by <seealso marker="kernel:inet#gethostbyaddr/1"> @@ -273,20 +215,11 @@ any of these data structures into a {Field,Value} list.</code> </func> <func> - <name>gethostbyname(Name) -> {ok,hostent()} | Reason}</name> - <name>gethostbyname(Name, Family) -> - {ok,hostent()} | {error,Reason}} - </name> - <name>gethostbyname(Name, Family, Timeout) -> - {ok,hostent()} | {error,Reason} - </name> + <name name="gethostbyname" arity="1"/> + <name name="gethostbyname" arity="2"/> + <name name="gethostbyname" arity="3"/> <fsummary>Return a hostent record for the host with the given name </fsummary> - <type> - <v>Name = dns_name()</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Backend functions used by <seealso marker="kernel:inet#gethostbyname/1"> @@ -305,26 +238,16 @@ any of these data structures into a {Field,Value} list.</code> </func> <func> - <name>lookup(Name, Class, Type) -> [ dns_data() ] - </name> - <name>lookup(Name, Class, Type, Opts) -> [ dns_data() ] - </name> - <name>lookup(Name, Class, Type, Opts, Timeout) -> [ dns_data() ] - </name> + <name name="lookup" arity="3"/> + <name name="lookup" arity="4"/> + <name name="lookup" arity="5"/> <fsummary>Resolve the DNS data for the record of the given type and class for the given name </fsummary> - <type> - <v>Name = dns_name() | ip_address()</v> - <v>Type = rr_type()</v> - <v>Opts = res_option() | verbose</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Resolve the DNS data for the record of the given type and class for the given name. On success filters out the answer records - with the correct <c>Class</c> and <c>Type</c> and returns + with the correct <c><anno>Class</anno></c> and <c><anno>Type</anno></c> and returns a list of their data fields. So a lookup for type <c>any</c> will give an empty answer since the answer records have specific types that are not <c>any</c>. An empty answer @@ -332,44 +255,33 @@ any of these data structures into a {Field,Value} list.</code> </p><p> Calls <seealso marker="#resolve/3">resolve/2..4</seealso> with the same arguments and filters the result, so - <c>Opts</c> is explained there. + <c><anno>Opts</anno></c> is explained there. </p> </desc> </func> <func> - <name>resolve(Name, Class, Type) -> {ok,dns_msg()} | Error - </name> - <name>resolve(Name, Class, Type, Opts) -> {ok,dns_msg()} | Error - </name> - <name>resolve(Name, Class, Type, Opts, Timeout) -> {ok,dns_msg()} | Error - </name> + <name name="resolve" arity="3"/> + <name name="resolve" arity="4"/> + <name name="resolve" arity="5"/> <fsummary>Resolve a DNS record of the given type and class for the given name </fsummary> - <type> - <v>Name = dns_name() | ip_address()</v> - <v>Type = rr_type()</v> - <v>Opts = res_option() | verbose | atom()</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Error = {error,Reason} | {error,{Reason,dns_msg()}}</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Resolve a DNS record of the given type and class for the given name. The returned <c>dns_msg()</c> can be examined using access functions in <c>inet_db</c> as described in <seealso marker="#dns_types">DNS types</seealso>. </p><p> - If <c>Name</c> is an <c>ip_address()</c>, the domain name + If <c><anno>Name</anno></c> is an <c>ip_address()</c>, the domain name to query for is generated as the standard reverse ".IN-ADDR.ARPA." name for an IPv4 address, or the ".IP6.ARPA." name for an IPv6 address. In this case you most probably want to use - <c>Class = in</c> and <c>Type = ptr</c> but it + <c><anno>Class</anno> = in</c> and <c><anno>Type</anno> = ptr</c> but it is not done automatically. </p><p> - <c>Opts</c> override the corresponding resolver options. + <c><anno>Opts</anno></c> override the corresponding resolver options. If the option <c>nameservers</c> is given, it is also assumed that it is the complete list of nameserves, so the resolver option <c>alt_nameserves</c> is ignored. @@ -382,14 +294,14 @@ any of these data structures into a {Field,Value} list.</code> of queries, replies retransmissions, etc, similar to from utilities like <c>dig</c>, <c>nslookup</c> et.al. </p><p> - If <c>Opt</c> is an arbitrary atom it is interpreted - as <c>{Opt,true}</c> unless the atom string starts with - <c>"no"</c> making the interpretation <c>{Opt,false}</c>. + If <c><anno>Opt</anno></c> is an arbitrary atom it is interpreted + as <c>{<anno>Opt</anno>,true}</c> unless the atom string starts with + <c>"no"</c> making the interpretation <c>{<anno>Opt</anno>,false}</c>. For example: <c>usevc</c> is an alias for <c>{usevc,true}</c>, and <c>nousevc</c> an alias for <c>{usevc,false}</c>. </p><p> The <c>inet6</c> option currently has no effect on this function. - You probably want to use <c>Type = a | aaaa</c> instead. + You probably want to use <c><anno>Type</anno> = a | aaaa</c> instead. </p> </desc> </func> @@ -430,24 +342,18 @@ any of these data structures into a {Field,Value} list.</code> <funcs> <func> - <name>nslookup(Name, Class, Type) -> {ok,dns_msg()} | {error,Reason} - </name> - <name>nslookup(Name, Class, Type, Timeout) -> - {ok,dns_msg()} | {error,Reason} - </name> - <name>nslookup(Name, Class, Type, Nameservers) -> - {ok,dns_msg()} | {error,Reason} - </name> + <name name="nslookup" arity="3"/> + <name name="nslookup" arity="4" clause_i="1"/> + <name name="nslookup" arity="4" clause_i="2"/> <fsummary>Resolve a DNS record of the given type and class for the given name </fsummary> - <type> - <v>Name = dns_name() | ip_address()</v> - <v>Type = rr_type()</v> - <v>Nameservers = [ nameserver() ]</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> + <type variable="Name"/> + <type variable="Class"/> + <type variable="Type"/> + <type variable="Timeout" name_i="2"/> + <type variable="Nameservers"/> + <type variable="Reason"/> <desc> <p>Resolve a DNS record of the given type and class for the given name. </p> @@ -455,22 +361,11 @@ any of these data structures into a {Field,Value} list.</code> </func> <func> - <name>nnslookup(Name, Class, Type, Nameservers) -> - {ok,dns_msg()} | {error,posix()} - </name> - <name>nnslookup(Name, Class, Type, Nameservers, Timeout) -> - {ok,dns_msg()} | {error,posix()} - </name> + <name name="nnslookup" arity="4"/> + <name name="nnslookup" arity="5"/> <fsummary>Resolve a DNS record of the given type and class for the given name </fsummary> - <type> - <v>Name = dns_name() | ip_address()</v> - <v>Type = rr_type()</v> - <v>Nameservers = [ nameserver() ]</v> - <v>Timeout = integer() >= 0 | infinity</v> - <v>Reason = posix() | res_error()</v> - </type> <desc> <p>Resolve a DNS record of the given type and class for the given name. </p> diff --git a/lib/kernel/doc/src/net_adm.xml b/lib/kernel/doc/src/net_adm.xml index 7ec4f7f0e7..f2aac9282c 100644 --- a/lib/kernel/doc/src/net_adm.xml +++ b/lib/kernel/doc/src/net_adm.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,94 +34,70 @@ </description> <funcs> <func> - <name>dns_hostname(Host) -> {ok, Name} | {error, Host}</name> + <name name="dns_hostname" arity="1"/> <fsummary>Official name of a host</fsummary> - <type> - <v>Host = atom() | string()</v> - <v>Name = string()</v> - </type> <desc> - <p>Returns the official name of <c>Host</c>, or - <c>{error, Host}</c> if no such name is found. See also + <p>Returns the official name of <c><anno>Host</anno></c>, or + <c>{error, <anno>Host</anno>}</c> if no such name is found. See also <c>inet(3)</c>.</p> </desc> </func> <func> - <name>host_file() -> Hosts | {error, Reason}</name> + <name name="host_file" arity="0"/> <fsummary>Read the <c>.hosts.erlang</c>file</fsummary> - <type> - <v>Hosts = [Host]</v> - <v> Host = atom()</v> - <v>Reason = term()</v> - </type> <desc> <p>Reads the <c>.hosts.erlang</c> file, see the section <em>Files</em> below. Returns the hosts in this file as a - list, or returns <c>{error, Reason}</c> if the file could not - be read. See <c>file(3)</c> for possible values of - <c>Reason</c>.</p> + list, or returns <c>{error, <anno>Reason</anno>}</c> if the file could not + be read or the Erlang terms on the file could not be interpreted.</p> </desc> </func> <func> - <name>localhost() -> Name</name> + <name name="localhost" arity="0"/> <fsummary>Name of the local host</fsummary> - <type> - <v>Name = string()</v> - </type> <desc> <p>Returns the name of the local host. If Erlang was started - with the <c>-name</c> command line flag, <c>Name</c> is + with the <c>-name</c> command line flag, <c><anno>Name</anno></c> is the fully qualified name.</p> </desc> </func> <func> - <name>names() -> {ok, [{Name, Port}]} | {error, Reason}</name> - <name>names(Host) -> {ok, [{Name, Port}]} | {error, Reason}</name> + <name name="names" arity="0"/> + <name name="names" arity="1"/> <fsummary>Names of Erlang nodes at a host</fsummary> - <type> - <v>Name = string()</v> - <v>Port = int()</v> - <v>Reason = address | term()</v> - </type> <desc> <p>Similar to <c>epmd -names</c>, see <c>epmd(1)</c>. - <c>Host</c> defaults to the local host. Returns the names and + <c><anno>Host</anno></c> defaults to the local host. Returns the names and associated port numbers of the Erlang nodes that <c>epmd</c> at the specified host has registered.</p> <p>Returns <c>{error, address}</c> if <c>epmd</c> is not - running. See <c>inet(3)</c> for other possible values of - <c>Reason</c>.</p> + running.</p> <pre> (arne@dunn)1> <input>net_adm:names().</input> {ok,[{"arne",40262}]}</pre> </desc> </func> <func> - <name>ping(Node) -> pong | pang</name> + <name name="ping" arity="1"/> <fsummary>Set up a connection to a node</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> - <p>Tries to set up a connection to <c>Node</c>. Returns + <p>Tries to set up a connection to <c><anno>Node</anno></c>. Returns <c>pang</c> if it fails, or <c>pong</c> if it is successful.</p> </desc> </func> <func> - <name>world() -> [node()]</name> - <name>world(Arg) -> [node()]</name> + <name name="world" arity="0"/> + <name name="world" arity="1"/> + <type name="verbosity"/> <fsummary>Lookup and connect to all nodes at all hosts in <c>.hosts.erlang</c></fsummary> - <type> - <v>Arg = silent | verbose</v> - </type> <desc> <p>This function calls <c>names(Host)</c> for all hosts which are specified in the Erlang host file <c>.hosts.erlang</c>, collects the replies and then evaluates <c>ping(Node)</c> on all those nodes. Returns the list of all nodes that were, successfully pinged.</p> - <p><c>Arg</c> defaults to <c>silent</c>. - If <c>Arg == verbose</c>, the function writes information about which + <p><c><anno>Arg</anno></c> defaults to <c>silent</c>. + If <c><anno>Arg</anno> == verbose</c>, the function writes information about which nodes it is pinging to stdout.</p> <p>This function can be useful when a node is started, and the names of the other nodes in the network are not initially @@ -131,14 +107,10 @@ </desc> </func> <func> - <name>world_list(Hosts) -> [node()]</name> - <name>world_list(Hosts, Arg) -> [node()]</name> + <name name="world_list" arity="1"/> + <name name="world_list" arity="2"/> + <type name="verbosity"/> <fsummary>Lookup and connect to all nodes at specified hosts</fsummary> - <type> - <v>Hosts = [Host]</v> - <v> Host = atom()</v> - <v>Arg = silent | verbose</v> - </type> <desc> <p>As <c>world/0,1</c>, but the hosts are given as argument instead of being read from <c>.hosts.erlang</c>.</p> diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index a18226e779..96e2aa665d 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -64,50 +64,38 @@ $ <input>erl -sname foobar</input></pre> </description> <funcs> <func> - <name>allow(Nodes) -> ok | error</name> + <name name="allow" arity="1"/> <fsummary>Limit access to a specified set of nodes</fsummary> - <type> - <v>Nodes = [node()]</v> - </type> <desc> <p>Limits access to the specified set of nodes. Any access - attempts made from (or to) nodes not in <c>Nodes</c> will be + attempts made from (or to) nodes not in <c><anno>Nodes</anno></c> will be rejected.</p> - <p>Returns <c>error</c> if any element in <c>Nodes</c> is not + <p>Returns <c>error</c> if any element in <c><anno>Nodes</anno></c> is not an atom.</p> </desc> </func> <func> - <name>connect_node(Node) -> true | false | ignored</name> + <name name="connect_node" arity="1"/> <fsummary>Establish a connection to a node</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> - <p>Establishes a connection to <c>Node</c>. Returns <c>true</c> + <p>Establishes a connection to <c><anno>Node</anno></c>. Returns <c>true</c> if successful, <c>false</c> if not, and <c>ignored</c> if the local node is not alive.</p> </desc> </func> <func> - <name>monitor_nodes(Flag) -> ok | Error</name> - <name>monitor_nodes(Flag, Options) -> ok | Error</name> + <name name="monitor_nodes" arity="1"/> + <name name="monitor_nodes" arity="2"/> <fsummary>Subscribe to node status change messages</fsummary> - <type> - <v>Flag = true | false</v> - <v>Options = [Option]</v> - <v> Option -- see below</v> - <v>Error = error | {error, term()}</v> - </type> <desc> <p>The calling process subscribes or unsubscribes to node status change messages. A <c>nodeup</c> message is delivered to all subscribing process when a new node is connected, and a <c>nodedown</c> message is delivered when a node is disconnected.</p> - <p>If <c>Flag</c> is <c>true</c>, a new subscription is started. - If <c>Flag</c> is <c>false</c>, all previous subscriptions -- - started with the same <c>Options</c> -- are stopped. Two + <p>If <c><anno>Flag</anno></c> is <c>true</c>, a new subscription is started. + If <c><anno>Flag</anno></c> is <c>false</c>, all previous subscriptions -- + started with the same <c><anno>Options</anno></c> -- are stopped. Two option lists are considered the same if they contain the same set of options.</p> <p>As of <c>kernel</c> version 2.11.4, and <c>erts</c> version @@ -139,23 +127,23 @@ $ <input>erl -sname foobar</input></pre> <p>Note, that this is <em>not</em> guaranteed for <c>kernel</c> versions before 2.13.</p> <p>The format of the node status change messages depends on - <c>Options</c>. If <c>Options</c> is [], which is the default, + <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is [], which is the default, the format is:</p> <code type="none"> {nodeup, Node} | {nodedown, Node} Node = node()</code> - <p>If <c>Options /= []</c>, the format is:</p> + <p>If <c><anno>Options</anno> /= []</c>, the format is:</p> <code type="none"> {nodeup, Node, InfoList} | {nodedown, Node, InfoList} Node = node() InfoList = [{Tag, Val}]</code> <p><c>InfoList</c> is a list of tuples. Its contents depends on - <c>Options</c>, see below.</p> + <c><anno>Options</anno></c>, see below.</p> <p>Also, when <c>OptionList == []</c> only visible nodes, that is, nodes that appear in the result of <seealso marker="erts:erlang#nodes/0">nodes/0</seealso>, are monitored.</p> - <p><c>Option</c> can be any of the following:</p> + <p><c><anno>Option</anno></c> can be any of the following:</p> <taglist> <tag><c>{node_type, NodeType}</c></tag> <item> @@ -209,61 +197,51 @@ $ <input>erl -sname foobar</input></pre> </desc> </func> <func> - <name>get_net_ticktime() -> Res</name> + <name name="get_net_ticktime" arity="0"/> <fsummary>Get <c>net_ticktime</c></fsummary> - <type> - <v>Res = NetTicktime | {ongoing_change_to, NetTicktime}</v> - <v> NetTicktime = int()</v> - </type> <desc> <p>Gets <c>net_ticktime</c> (see <seealso marker="kernel_app">kernel(6)</seealso>).</p> - <p>Currently defined return values (<c>Res</c>):</p> + <p>Currently defined return values (<c><anno>Res</anno></c>):</p> <taglist> - <tag><c>NetTicktime</c></tag> + <tag><c><anno>NetTicktime</anno></c></tag> <item> - <p><c>net_ticktime</c> is <c>NetTicktime</c> seconds.</p> + <p><c>net_ticktime</c> is <c><anno>NetTicktime</anno></c> seconds.</p> </item> - <tag><c>{ongoing_change_to, NetTicktime}</c></tag> + <tag><c>{ongoing_change_to, <anno>NetTicktime</anno>}</c></tag> <item> <p><c>net_kernel</c> is currently changing - <c>net_ticktime</c> to <c>NetTicktime</c> seconds.</p> + <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p> </item> </taglist> </desc> </func> <func> - <name>set_net_ticktime(NetTicktime) -> Res</name> - <name>set_net_ticktime(NetTicktime, TransitionPeriod) -> Res</name> + <name name="set_net_ticktime" arity="1"/> + <name name="set_net_ticktime" arity="2"/> <fsummary>Set <c>net_ticktime</c></fsummary> - <type> - <v>NetTicktime = int() > 0</v> - <v>TransitionPeriod = int() >= 0</v> - <v>Res = unchanged | change_initiated | {ongoing_change_to, NewNetTicktime}</v> - <v> NewNetTicktime = int() > 0</v> - </type> <desc> <p>Sets <c>net_ticktime</c> (see <seealso marker="kernel_app">kernel(6)</seealso>) to - <c>NetTicktime</c> seconds. <c>TransitionPeriod</c> defaults + <c><anno>NetTicktime</anno></c> seconds. <c><anno>TransitionPeriod</anno></c> defaults to 60.</p> <p>Some definitions:</p> <p></p> <taglist> <tag>The minimum transition traffic interval (<c>MTTI</c>)</tag> <item> - <p><c>minimum(NetTicktime, PreviousNetTicktime)*1000 div 4</c> milliseconds.</p> + <p><c>minimum(<anno>NetTicktime</anno>, PreviousNetTicktime)*1000 div 4</c> milliseconds.</p> </item> <tag>The transition period</tag> <item> <p>The time of the least number of consecutive <c>MTTI</c>s - to cover <c>TransitionPeriod</c> seconds following + to cover <c><anno>TransitionPeriod</anno></c> seconds following the call to <c>set_net_ticktime/2</c> (i.e. - ((<c>TransitionPeriod*1000 - 1) div MTTI + 1)*MTTI</c> + ((<c><anno>TransitionPeriod</anno>*1000 - 1) div MTTI + 1)*MTTI</c> milliseconds).</p> </item> </taglist> - <p>If <c><![CDATA[NetTicktime < PreviousNetTicktime]]></c>, the actual + <p>If <c><![CDATA[<anno>NetTicktime</anno> < PreviousNetTicktime]]></c>, the actual <c>net_ticktime</c> change will be done at the end of the transition period; otherwise, at the beginning. During the transition period, <c>net_kernel</c> will ensure that @@ -271,7 +249,7 @@ $ <input>erl -sname foobar</input></pre> every <c>MTTI</c> millisecond.</p> <note> <p>The <c>net_ticktime</c> changes have to be initiated on all - nodes in the network (with the same <c>NetTicktime</c>) + nodes in the network (with the same <c><anno>NetTicktime</anno></c>) before the end of any transition period on any node; otherwise, connections may erroneously be disconnected.</p> </note> @@ -280,18 +258,18 @@ $ <input>erl -sname foobar</input></pre> <tag><c>unchanged</c></tag> <item> <p><c>net_ticktime</c> already had the value of - <c>NetTicktime</c> and was left unchanged.</p> + <c><anno>NetTicktime</anno></c> and was left unchanged.</p> </item> <tag><c>change_initiated</c></tag> <item> <p><c>net_kernel</c> has initiated the change of - <c>net_ticktime</c> to <c>NetTicktime</c> seconds.</p> + <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p> </item> - <tag><c>{ongoing_change_to, NewNetTicktime}</c></tag> + <tag><c>{ongoing_change_to, <anno>NewNetTicktime</anno>}</c></tag> <item> <p>The request was <em>ignored</em>; because, <c>net_kernel</c> was busy changing <c>net_ticktime</c> to - <c>NewTicktime</c> seconds.</p> + <c><anno>NewNetTicktime</anno></c> seconds.</p> </item> </taglist> </desc> @@ -315,7 +293,7 @@ $ <input>erl -sname foobar</input></pre> </desc> </func> <func> - <name>stop() -> ok | {error, not_allowed | not_found}</name> + <name name="stop" arity="0"/> <fsummary>Turn a node into a non-distributed Erlang runtime system</fsummary> <desc> <p>Turns a distributed node into a non-distributed node. For diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 065b24c53d..f92837dfe5 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 2c9cc33eb7..56fc1834ec 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,13 +38,10 @@ </description> <funcs> <func> - <name>cmd(Command) -> string()</name> + <name name="cmd" arity="1"/> <fsummary>Execute a command in a shell of the target OS</fsummary> - <type> - <v>Command = string() | atom()</v> - </type> <desc> - <p>Executes <c>Command</c> in a command shell of the target OS, + <p>Executes <c><anno>Command</anno></c> in a command shell of the target OS, captures the standard output of the command and returns this result as a string. This function is a replacement of the previous <c>unix:cmd/1</c>; on a Unix platform they are @@ -60,23 +57,18 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </desc> </func> <func> - <name>find_executable(Name) -> Filename | false</name> - <name>find_executable(Name, Path) -> Filename | false</name> + <name name="find_executable" arity="1"/> + <name name="find_executable" arity="2"/> <fsummary>Absolute filename of a program</fsummary> - <type> - <v>Name = string()</v> - <v>Path = string()</v> - <v>Filename = string()</v> - </type> <desc> <p>These two functions look up an executable program given its name and a search path, in the same way as the underlying operating system. <c>find_executable/1</c> uses the current execution path (that is, the environment variable PATH on Unix and Windows).</p> - <p><c>Path</c>, if given, should conform to the syntax of + <p><c><anno>Path</anno></c>, if given, should conform to the syntax of execution paths on the operating system. The absolute - filename of the executable program <c>Name</c> is returned, + filename of the executable program <c><anno>Name</anno></c> is returned, or <c>false</c> if the program was not found.</p> </desc> </func> @@ -137,7 +129,7 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> <name>timestamp() -> {MegaSecs, Secs, MicroSecs}</name> <fsummary>Returna a timestamp from the OS in the erlang:now/0 format</fsummary> <type> - <v>MegaSecs = Secs = MicroSecs = int()</v> + <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v> </type> <desc> <p>Returns a tuple in the same format as <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>. The difference is that this function returns what the operating system thinks (a.k.a. the wall clock time) without any attempts at time correction. The result of two different calls to this function is <em>not</em> guaranteed to be different.</p> @@ -165,19 +157,15 @@ format_utc_timestamp() -> </desc> </func> <func> - <name>type() -> {Osfamily, Osname} | Osfamily</name> + <name name="type" arity="0"/> <fsummary>Return the OS family and, in some cases, OS name of the current operating system</fsummary> - <type> - <v>Osfamily = win32 | unix | vxworks</v> - <v>Osname = atom()</v> - </type> <desc> - <p>Returns the <c>Osfamily</c> and, in some cases, <c>Osname</c> + <p>Returns the <c><anno>Osfamily</anno></c> and, in some cases, <c><anno>Osname</anno></c> of the current operating system.</p> - <p>On Unix, <c>Osname</c> will have same value as + <p>On Unix, <c><anno>Osname</anno></c> will have same value as <c>uname -s</c> returns, but in lower case. For example, on Solaris 1 and 2, it will be <c>sunos</c>.</p> - <p>In Windows, <c>Osname</c> will be either <c>nt</c> (on + <p>In Windows, <c><anno>Osname</anno></c> will be either <c>nt</c> (on Windows NT), or <c>windows</c> (on Windows 95).</p> <p>On VxWorks the OS family alone is returned, that is <c>vxworks</c>.</p> @@ -185,17 +173,13 @@ format_utc_timestamp() -> <p>Think twice before using this function. Use the <c>filename</c> module if you want to inspect or build file names in a portable way. - Avoid matching on the <c>Osname</c> atom.</p> + Avoid matching on the <c><anno>Osname</anno></c> atom.</p> </note> </desc> </func> <func> - <name>version() -> {Major, Minor, Release} | VersionString</name> + <name name="version" arity="0"/> <fsummary>Return the Operating System version</fsummary> - <type> - <v>Major = Minor = Release = integer()</v> - <v>VersionString = string()</v> - </type> <desc> <p>Returns the operating system version. On most systems, this function returns a tuple, but a string diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml index 7463fd10f5..d26ff0fc6b 100644 --- a/lib/kernel/doc/src/pg2.xml +++ b/lib/kernel/doc/src/pg2.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,13 +60,16 @@ to avoid name clashes.</p> </warning> </description> + <datatypes> + <datatype> + <name name="name"/> + <desc><p>The name of a process group.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>create(Name) -> void()</name> + <name name="create" arity="1"/> <fsummary>Create a new, empty process group</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> <p>Creates a new, empty process group. The group is globally visible on all nodes. If the group exists, nothing happens. @@ -74,24 +77,16 @@ </desc> </func> <func> - <name>delete(Name) -> void()</name> + <name name="delete" arity="1"/> <fsummary>Delete a process group</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> <p>Deletes a process group. </p> </desc> </func> <func> - <name>get_closest_pid(Name) -> Pid | {error, Reason}</name> + <name name="get_closest_pid" arity="1"/> <fsummary>Common dispatch function</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Reason = {no_process, Name} | {no_such_group, Name}</v> - </type> <desc> <p>This is a useful dispatch function which can be used from client functions. It returns a process on the local node, if @@ -100,13 +95,8 @@ </desc> </func> <func> - <name>get_members(Name) -> [Pid] | {error, Reason}</name> + <name name="get_members" arity="1"/> <fsummary>Return all processes in a group</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Reason = {no_such_group, Name}</v> - </type> <desc> <p>Returns all processes in the group <c>Name</c>. This function should be used from within a client function that @@ -115,13 +105,8 @@ </desc> </func> <func> - <name>get_local_members(Name) -> [Pid] | {error, Reason}</name> + <name name="get_local_members" arity="1"/> <fsummary>Return all local processes in a group</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Reason = {no_such_group, Name}</v> - </type> <desc> <p>Returns all processes running on the local node in the group <c>Name</c>. This function should to be used from @@ -131,13 +116,8 @@ </desc> </func> <func> - <name>join(Name, Pid) -> ok | {error, Reason}</name> + <name name="join" arity="2"/> <fsummary>Join a process to a group</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Reason = {no_such_group, Name}</v> - </type> <desc> <p>Joins the process <c>Pid</c> to the group <c>Name</c>. A process can join a group several times; it must then @@ -146,13 +126,8 @@ </desc> </func> <func> - <name>leave(Name, Pid) -> ok | {error, Reason}</name> + <name name="leave" arity="2"/> <fsummary>Make a process leave a group</fsummary> - <type> - <v>Name = term()</v> - <v>Pid = pid()</v> - <v>Reason = {no_such_group, Name}</v> - </type> <desc> <p>Makes the process <c>Pid</c> leave the group <c>Name</c>. If the process is not a member of the group, <c>ok</c> is @@ -161,24 +136,17 @@ </desc> </func> <func> - <name>which_groups() -> [Name]</name> + <name name="which_groups" arity="0"/> <fsummary>Return a list of all known groups</fsummary> - <type> - <v>Name = term()</v> - </type> <desc> <p>Returns a list of all known groups. </p> </desc> </func> <func> - <name>start()</name> - <name>start_link() -> {ok, Pid} | {error, Reason}</name> + <name name="start" arity="0"/> + <name name="start_link" arity="0"/> <fsummary>Start the pg2 server</fsummary> - <type> - <v>Pid = pid()</v> - <v>Reason = term()</v> - </type> <desc> <p>Starts the pg2 server. Normally, the server does not need to be started explicitly, as it is started dynamically if it diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index 2b81de170d..b01ff16c85 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,40 +37,34 @@ for collecting information on a remote node, or for running a function with some specific side effects on the remote node.</p> </description> + <datatypes> + <datatype> + <name name="key"/> + <desc> + <p>As returned by <seealso marker="#async_call/4"> + <c>async_call/4</c>.</seealso></p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>call(Node, Module, Function, Args) -> Res | {badrpc, Reason}</name> + <name name="call" arity="4"/> <fsummary>Evaluate a function call on a node</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Res = term()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Args)</c> on the node - <c>Node</c> and returns the corresponding value <c>Res</c>, or - <c>{badrpc, Reason}</c> if the call fails.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node + <c><anno>Node</anno></c> and returns the corresponding value <c><anno>Res</anno></c>, or + <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.</p> </desc> </func> <func> - <name>call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason}</name> + <name name="call" arity="5"/> <fsummary>Evaluate a function call on a node</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Res = term()</v> - <v>Reason = timeout | term()</v> - <v>Timeout = int() | infinity</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Args)</c> on the node - <c>Node</c> and returns the corresponding value <c>Res</c>, or - <c>{badrpc, Reason}</c> if the call fails. <c>Timeout</c> is + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node + <c><anno>Node</anno></c> and returns the corresponding value <c><anno>Res</anno></c>, or + <c>{badrpc, <anno>Reason</anno>}</c> if the call fails. <c><anno>Timeout</anno></c> is a timeout value in milliseconds. If the call times out, - <c>Reason</c> is <c>timeout</c>.</p> + <c><anno>Reason</anno></c> is <c>timeout</c>.</p> <p>If the reply arrives after the call times out, no message will contaminate the caller's message queue, since this function spawns off a middleman process to act as (a void) @@ -80,17 +74,10 @@ </desc> </func> <func> - <name>block_call(Node, Module, Function, Args) -> Res | {badrpc, Reason}</name> + <name name="block_call" arity="4"/> <fsummary>Evaluate a function call on a node in the RPC server's context</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Res = term()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Like <c>call/4</c>, but the RPC server at <c>Node</c> does + <p>Like <c>call/4</c>, but the RPC server at <c><anno>Node</anno></c> does not create a separate process to handle the call. Thus, this function can be used if the intention of the call is to block the RPC server from any other incoming requests until @@ -101,50 +88,31 @@ </desc> </func> <func> - <name>block_call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason}</name> + <name name="block_call" arity="5"/> <fsummary>Evaluate a function call on a node in the RPC server's context</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Timeout = int() | infinity</v> - <v>Res = term()</v> - <v>Reason = term()</v> - </type> <desc> <p>Like <c>block_call/4</c>, but with a timeout value in the same manner as <c>call/5</c>.</p> </desc> </func> <func> - <name>async_call(Node, Module, Function, Args) -> Key</name> + <name name="async_call" arity="4"/> <fsummary>Evaluate a function call on a node, asynchronous version</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Key -- see below</v> - </type> <desc> <p>Implements <em>call streams with promises</em>, a type of RPC which does not suspend the caller until the result is finished. Instead, a key is returned which can be used at a later stage to collect the value. The key can be viewed as a promise to deliver the answer.</p> - <p>In this case, the key <c>Key</c> is returned, which can be + <p>In this case, the key <c><anno>Key</anno></c> is returned, which can be used in a subsequent call to <c>yield/1</c> or <c>nb_yield/1,2</c> to retrieve the value of evaluating - <c>apply(Module, Function, Args)</c> on the node <c>Node</c>.</p> + <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node <c><anno>Node</anno></c>.</p> </desc> </func> <func> - <name>yield(Key) -> Res | {badrpc, Reason}</name> + <name name="yield" arity="1"/> <fsummary>Deliver the result of evaluating a function call on a node (blocking)</fsummary> - <type> - <v>Key -- see async_call/4</v> - <v>Res = term()</v> - <v>Reason = term()</v> - </type> <desc> <p>Returns the promised answer from a previous <c>async_call/4</c>. If the answer is available, it is @@ -153,87 +121,46 @@ </desc> </func> <func> - <name>nb_yield(Key) -> {value, Val} | timeout</name> + <name name="nb_yield" arity="1"/> <fsummary>Deliver the result of evaluating a function call on a node (non-blocking)</fsummary> - <type> - <v>Key -- see async_call/4</v> - <v>Val = Res | {badrpc, Reason}</v> - <v> Res = term()</v> - <v> Reason = term()</v> - </type> <desc> - <p>Equivalent to <c>nb_yield(Key, 0)</c>.</p> + <p>Equivalent to <c>nb_yield(<anno>Key</anno>, 0)</c>.</p> </desc> </func> <func> - <name>nb_yield(Key, Timeout) -> {value, Val} | timeout</name> + <name name="nb_yield" arity="2"/> <fsummary>Deliver the result of evaluating a function call on a node (non-blocking)</fsummary> - <type> - <v>Key -- see async_call/4</v> - <v>Timeout = int() | infinity</v> - <v>Val = Res | {badrpc, Reason}</v> - <v> Res = term()</v> - <v> Reason = term()</v> - </type> <desc> <p>This is a non-blocking version of <c>yield/1</c>. It returns - the tuple <c>{value, Val}</c> when the computation has - finished, or <c>timeout</c> when <c>Timeout</c> milliseconds + the tuple <c>{value, <anno>Val</anno>}</c> when the computation has + finished, or <c>timeout</c> when <c><anno>Timeout</anno></c> milliseconds has elapsed.</p> </desc> </func> <func> - <name>multicall(Module, Function, Args) -> {ResL, BadNodes}</name> + <name name="multicall" arity="3"/> <fsummary>Evaluate a function call on a number of nodes</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>ResL = [term()]</v> - <v>BadNodes = [node()]</v> - </type> <desc> - <p>Equivalent to <c>multicall([node()|nodes()], Module, Function, Args, infinity)</c>.</p> + <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> </desc> </func> <func> - <name>multicall(Nodes, Module, Function, Args) -> {ResL, BadNodes}</name> + <name name="multicall" arity="4" clause_i="1"/> <fsummary>Evaluate a function call on a number of nodes</fsummary> - <type> - <v>Nodes = [node()]</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>ResL = [term()]</v> - <v>BadNodes = [node()]</v> - </type> <desc> - <p>Equivalent to <c>multicall(Nodes, Module, Function, Args, infinity)</c>.</p> + <p>Equivalent to <c>multicall(<anno>Nodes</anno>, <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> </desc> </func> <func> - <name>multicall(Module, Function, Args, Timeout) -> {ResL, BadNodes}</name> + <name name="multicall" arity="4" clause_i="2"/> <fsummary>Evaluate a function call on a number of nodes</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Timeout = int() | infinity</v> - <v>ResL = [term()]</v> - <v>BadNodes = [node()]</v> - </type> <desc> - <p>Equivalent to <c>multicall([node()|nodes()], Module, Function, Args, Timeout)</c>.</p> + <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, <anno>Timeout</anno>)</c>.</p> </desc> </func> <func> - <name>multicall(Nodes, Module, Function, Args, Timeout) -> {ResL, BadNodes}</name> + <name name="multicall" arity="5"/> <fsummary>Evaluate a function call on a number of nodes</fsummary> - <type> - <v>Nodes = [node()]</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Timeout = int() | infinity</v> - <v>ResL = [term()]</v> - <v>BadNodes = [node()]</v> - </type> <desc> <p>In contrast to an RPC, a multicall is an RPC which is sent concurrently from one client to multiple servers. This is @@ -243,12 +170,12 @@ making a series of RPCs on all the nodes, but the multicall is faster as all the requests are sent at the same time and are collected one by one as they come back.</p> - <p>The function evaluates <c>apply(Module, Function, Args)</c> + <p>The function evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the specified nodes and collects the answers. It returns - <c>{ResL, Badnodes}</c>, where <c>Badnodes</c> is a list + <c>{<anno>ResL</anno>, <anno>BadNodes</anno>}</c>, where <c><anno>BadNodes</anno></c> is a list of the nodes that terminated or timed out during computation, - and <c>ResL</c> is a list of the return values. - <c>Timeout</c> is a time (integer) in milliseconds, or + and <c><anno>ResL</anno></c> is a list of the return values. + <c><anno>Timeout</anno></c> is a time (integer) in milliseconds, or <c>infinity</c>.</p> <p>The following example is useful when new object code is to be loaded on all nodes in the network, and also indicates @@ -264,93 +191,60 @@ </desc> </func> <func> - <name>cast(Node, Module, Function, Args) -> void()</name> + <name name="cast" arity="4"/> <fsummary>Run a function on a node ignoring the result</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Args)</c> on the node - <c>Node</c>. No response is delivered and the calling + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node + <c><anno>Node</anno></c>. No response is delivered and the calling process is not suspended until the evaluation is complete, as is the case with <c>call/4,5</c>.</p> </desc> </func> <func> - <name>eval_everywhere(Module, Funtion, Args) -> void()</name> + <name name="eval_everywhere" arity="3"/> <fsummary>Run a function on all nodes, ignoring the result</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Equivalent to <c>eval_everywhere([node()|nodes()], Module, Function, Args)</c>.</p> + <p>Equivalent to <c>eval_everywhere([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c>.</p> </desc> </func> <func> - <name>eval_everywhere(Nodes, Module, Function, Args) -> void()</name> + <name name="eval_everywhere" arity="4"/> <fsummary>Run a function on specific nodes, ignoring the result</fsummary> - <type> - <v>Nodes = [node()]</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Args)</c> on + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the specified nodes. No answers are collected.</p> </desc> </func> <func> - <name>abcast(Name, Msg) -> void()</name> + <name name="abcast" arity="2"/> <fsummary>Broadcast a message asynchronously to a registered process on all nodes</fsummary> - <type> - <v>Name = atom()</v> - <v>Msg = term()</v> - </type> <desc> - <p>Equivalent to <c>abcast([node()|nodes()], Name, Msg)</c>.</p> + <p>Equivalent to <c>abcast([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> </desc> </func> <func> - <name>abcast(Nodes, Name, Msg) -> void()</name> + <name name="abcast" arity="3"/> <fsummary>Broadcast a message asynchronously to a registered process on specific nodes</fsummary> - <type> - <v>Nodes = [node()]</v> - <v>Name = atom()</v> - <v>Msg = term()</v> - </type> <desc> - <p>Broadcasts the message <c>Msg</c> asynchronously to - the registered process <c>Name</c> on the specified nodes.</p> + <p>Broadcasts the message <c><anno>Msg</anno></c> asynchronously to + the registered process <c><anno>Name</anno></c> on the specified nodes.</p> </desc> </func> <func> - <name>sbcast(Name, Msg) -> {GoodNodes, BadNodes}</name> + <name name="sbcast" arity="2"/> <fsummary>Broadcast a message synchronously to a registered process on all nodes</fsummary> - <type> - <v>Name = atom()</v> - <v>Msg = term()</v> - <v>GoodNodes = BadNodes = [node()]</v> - </type> <desc> - <p>Equivalent to <c>sbcast([node()|nodes()], Name, Msg)</c>.</p> + <p>Equivalent to <c>sbcast([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> </desc> </func> <func> - <name>sbcast(Nodes, Name, Msg) -> {GoodNodes, BadNodes}</name> + <name name="sbcast" arity="3"/> <fsummary>Broadcast a message synchronously to a registered process on specific nodes</fsummary> - <type> - <v>Name = atom()</v> - <v>Msg = term()</v> - <v>Nodes = GoodNodes = BadNodes = [node()]</v> - </type> <desc> - <p>Broadcasts the message <c>Msg</c> synchronously to - the registered process <c>Name</c> on the specified nodes.</p> - <p>Returns <c>{GoodNodes, BadNodes}</c>, where <c>GoodNodes</c> - is the list of nodes which have <c>Name</c> as a registered + <p>Broadcasts the message <c><anno>Msg</anno></c> synchronously to + the registered process <c><anno>Name</anno></c> on the specified nodes.</p> + <p>Returns <c>{<anno>GoodNodes</anno>, <anno>BadNodes</anno>}</c>, where <c><anno>GoodNodes</anno></c> + is the list of nodes which have <c><anno>Name</anno></c> as a registered process.</p> <p>The function is synchronous in the sense that it is known that all servers have received the message when the call @@ -362,67 +256,46 @@ </desc> </func> <func> - <name>server_call(Node, Name, ReplyWrapper, Msg) -> Reply | {error, Reason}</name> + <name name="server_call" arity="4"/> <fsummary>Interact with a server on a node</fsummary> - <type> - <v>Node = node()</v> - <v>Name = atom()</v> - <v>ReplyWrapper = Msg = Reply = term()</v> - <v>Reason = term()</v> - </type> <desc> <p>This function can be used when interacting with a server - called <c>Name</c> at node <c>Node</c>. It is assumed that + called <c><anno>Name</anno></c> at node <c><anno>Node</anno></c>. It is assumed that the server receives messages in the format - <c>{From, Msg}</c> and replies using <c>From ! {ReplyWrapper, Node, Reply}</c>. This function makes such + <c>{From, <anno>Msg</anno>}</c> and replies using <c>From ! {<anno>ReplyWrapper</anno>, <anno>Node</anno>, <anno>Reply</anno>}</c>. This function makes such a server call and ensures that the entire call is packed into an atomic transaction which either succeeds or fails. It never hangs, unless the server itself hangs.</p> - <p>The function returns the answer <c>Reply</c> as produced by - the server <c>Name</c>, or <c>{error, Reason}</c>.</p> + <p>The function returns the answer <c><anno>Reply</anno></c> as produced by + the server <c><anno>Name</anno></c>, or <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>multi_server_call(Name, Msg) -> {Replies, BadNodes}</name> + <name name="multi_server_call" arity="2"/> <fsummary>Interact with the servers on a number of nodes</fsummary> - <type> - <v>Name = atom()</v> - <v>Msg = term()</v> - <v>Replies = [Reply]</v> - <v> Reply = term()</v> - <v>BadNodes = [node()]</v> - </type> <desc> - <p>Equivalent to <c>multi_server_call([node()|nodes()], Name, Msg)</c>.</p> + <p>Equivalent to <c>multi_server_call([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> </desc> </func> <func> - <name>multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes}</name> + <name name="multi_server_call" arity="3"/> <fsummary>Interact with the servers on a number of nodes</fsummary> - <type> - <v>Nodes = [node()]</v> - <v>Name = atom()</v> - <v>Msg = term()</v> - <v>Replies = [Reply]</v> - <v> Reply = term()</v> - <v>BadNodes = [node()]</v> - </type> <desc> <p>This function can be used when interacting with servers - called <c>Name</c> on the specified nodes. It is assumed that - the servers receive messages in the format <c>{From, Msg}</c> - and reply using <c>From ! {Name, Node, Reply}</c>, where + called <c><anno>Name</anno></c> on the specified nodes. It is assumed that + the servers receive messages in the format <c>{From, <anno>Msg</anno>}</c> + and reply using <c>From ! {<anno>Name</anno>, Node, <anno>Reply</anno>}</c>, where <c>Node</c> is the name of the node where the server is - located. The function returns <c>{Replies, Badnodes}</c>, - where <c>Replies</c> is a list of all <c>Reply</c> values and - <c>BadNodes</c> is a list of the nodes which did not exist, or + located. The function returns <c>{<anno>Replies</anno>, <anno>BadNodes</anno>}</c>, + where <c><anno>Replies</anno></c> is a list of all <c><anno>Reply</anno></c> values and + <c><anno>BadNodes</anno></c> is a list of the nodes which did not exist, or where the server did not exist, or where the server terminated before sending any reply.</p> </desc> </func> <func> - <name>safe_multi_server_call(Name, Msg) -> {Replies, BadNodes}</name> - <name>safe_multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes}</name> + <name name="safe_multi_server_call" arity="2"/> + <name name="safe_multi_server_call" arity="3"/> <fsummary>Interact with the servers on a number of nodes (deprecated)</fsummary> <desc> <warning> @@ -432,66 +305,47 @@ <p>In Erlang/OTP R6B and earlier releases, <c>multi_server_call/2,3</c> could not handle the case where the remote node exists, but there is no server called - <c>Name</c>. Instead this function had to be used. In + <c><anno>Name</anno></c>. Instead this function had to be used. In Erlang/OTP R7B and later releases, however, the functions are equivalent, except for this function being slightly slower.</p> </desc> </func> <func> - <name>parallel_eval(FuncCalls) -> ResL</name> + <name name="parallel_eval" arity="1"/> <fsummary>Evaluate several function calls on all nodes in parallel</fsummary> - <type> - <v>FuncCalls = [{Module, Function, Args}]</v> - <v> Module = Function = atom()</v> - <v> Args = [term()]</v> - <v>ResL = [term()]</v> - </type> <desc> - <p>For every tuple in <c>FuncCalls</c>, evaluates - <c>apply(Module, Function, Args)</c> on some node in + <p>For every tuple in <c><anno>FuncCalls</anno></c>, evaluates + <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on some node in the network. Returns the list of return values, in the same - order as in <c>FuncCalls</c>.</p> + order as in <c><anno>FuncCalls</anno></c>.</p> </desc> </func> <func> - <name>pmap({Module, Function}, ExtraArgs, List1) -> List2</name> + <name name="pmap" arity="3"/> <fsummary>Parallell evaluation of mapping a function over a list </fsummary> - <type> - <v>Module = Function = atom()</v> - <v>ExtraArgs = [term()]</v> - <v>List1 = [Elem]</v> - <v> Elem = term()</v> - <v>List2 = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, [Elem|ExtraArgs])</c>, - for every element <c>Elem</c> in <c>List1</c>, in parallel. + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, [<anno>Elem</anno>|<anno>ExtraArgs</anno>])</c>, + for every element <c><anno>Elem</anno></c> in <c><anno>List1</anno></c>, in parallel. Returns the list of return values, in the same order as in - <c>List1</c>.</p> + <c><anno>List1</anno></c>.</p> </desc> </func> <func> - <name>pinfo(Pid) -> [{Item, Info}] | undefined</name> + <name name="pinfo" arity="1"/> <fsummary>Information about a process</fsummary> - <type> - <v>Pid = pid()</v> - <v>Item, Info -- see erlang:process_info/1</v> - </type> <desc> <p>Location transparent version of the BIF - <c>process_info/1</c>.</p> + <seealso marker="erts:erlang#process_info/1"> + <c>process_info/1</c></seealso>.</p> </desc> </func> <func> - <name>pinfo(Pid, Item) -> {Item, Info} | undefined | []</name> + <name name="pinfo" arity="2"/> <fsummary>Information about a process</fsummary> - <type> - <v>Pid = pid()</v> - <v>Item, Info -- see erlang:process_info/1</v> - </type> <desc> <p>Location transparent version of the BIF - <c>process_info/2</c>.</p> + <seealso marker="erts:erlang#process_info/2"> + <c>process_info/2</c></seealso>.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 6c043dd767..1ab955bd8a 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1998</year><year>2009</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -47,17 +47,22 @@ from users.</p> </note> </description> + <datatypes> + <datatype> + <name name="token"/> + <desc> + <p>An opaque term (a tuple) representing a trace token.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>set_token(Token) -> PreviousToken</name> + <name name="set_token" arity="1"/> <fsummary>Set the trace token</fsummary> - <type> - <v>Token = PreviousToken = term() | []</v> - </type> <desc> - <p>Sets the trace token for the calling process to <c>Token</c>. - If <c>Token == []</c> then tracing is disabled, otherwise - <c>Token</c> should be an Erlang term returned from + <p>Sets the trace token for the calling process to <c><anno>Token</anno></c>. + If <c><anno>Token</anno> == []</c> then tracing is disabled, otherwise + <c><anno>Token</anno></c> should be an Erlang term returned from <c>get_token/0</c> or <c>set_token/1</c>. <c>set_token/1</c> can be used to temporarily exclude message passing from the trace by setting the trace token to empty like this:</p> @@ -72,18 +77,16 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name>set_token(Component, Val) -> {Component, OldVal}</name> + <name name="set_token" arity="2"/> <fsummary>Set a component of the trace token</fsummary> - <type> - <v>Component = label | serial | Flag</v> - <v> Flag = send | 'receive' | print | timestamp </v> - <v>Val = OldVal -- see below</v> - </type> + <type name="component"/> + <type name="flag"/> + <type name="value"/> <desc> - <p>Sets the individual <c>Component</c> of the trace token to - <c>Val</c>. Returns the previous value of the component.</p> + <p>Sets the individual <c><anno>Component</anno></c> of the trace token to + <c><anno>Val</anno></c>. Returns the previous value of the component.</p> <taglist> - <tag><c>set_token(label, Int)</c></tag> + <tag><c>set_token(label, <anno>Integer</anno>)</c></tag> <item> <p>The <c>label</c> component is an integer which identifies all events belonging to the same sequential @@ -93,31 +96,31 @@ seq_trace:set_token(OldToken), % activate the trace token again </item> <tag><c>set_token(serial, SerialValue)</c></tag> <item> - <p><c>SerialValue = {Previous, Current}</c>. + <p><c>SerialValue = {<anno>Previous</anno>, <anno>Current</anno>}</c>. The <c>serial</c> component contains counters which enables the traced messages to be sorted, should never be set explicitly by the user as these counters are updated automatically. Default is <c>{0, 0}</c>.</p> </item> - <tag><c>set_token(send, Bool)</c></tag> + <tag><c>set_token(send, <anno>Bool</anno>)</c></tag> <item> <p>A trace token flag (<c>true | false</c>) which enables/disables tracing on message sending. Default is <c>false</c>.</p> </item> - <tag><c>set_token('receive', Bool)</c></tag> + <tag><c>set_token('receive', <anno>Bool</anno>)</c></tag> <item> <p>A trace token flag (<c>true | false</c>) which enables/disables tracing on message reception. Default is <c>false</c>.</p> </item> - <tag><c>set_token(print, Bool)</c></tag> + <tag><c>set_token(print, <anno>Bool</anno>)</c></tag> <item> <p>A trace token flag (<c>true | false</c>) which enables/disables tracing on explicit calls to <c>seq_trace:print/1</c>. Default is <c>false</c>.</p> </item> - <tag><c>set_token(timestamp, Bool)</c></tag> + <tag><c>set_token(timestamp, <anno>Bool</anno>)</c></tag> <item> <p>A trace token flag (<c>true | false</c>) which enables/disables a timestamp to be generated for each @@ -127,11 +130,8 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name>get_token() -> TraceToken</name> + <name name="get_token" arity="0"/> <fsummary>Return the value of the trace token</fsummary> - <type> - <v>TraceToken = term() | []</v> - </type> <desc> <p>Returns the value of the trace token for the calling process. If <c>[]</c> is returned, it means that tracing is not active. @@ -141,13 +141,11 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name>get_token(Component) -> {Component, Val}</name> + <name name="get_token" arity="1"/> <fsummary>Return the value of a trace token component</fsummary> - <type> - <v>Component = label | serial | Flag</v> - <v> Flag = send | 'receive' | print | timestamp </v> - <v>Val -- see set_token/2</v> - </type> + <type name="component"/> + <type name="flag"/> + <type name="value"/> <desc> <p>Returns the value of the trace token component <c>Component</c>. See @@ -156,33 +154,26 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name>print(TraceInfo) -> void()</name> + <name name="print" arity="1"/> <fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary> - <type> - <v>TraceInfo = term()</v> - </type> <desc> - <p>Puts the Erlang term <c>TraceInfo</c> into the sequential + <p>Puts the Erlang term <c><anno>TraceInfo</anno></c> into the sequential trace output if the calling process currently is executing within a sequential trace and the <c>print</c> flag of the trace token is set.</p> </desc> </func> <func> - <name>print(Label, TraceInfo) -> void()</name> + <name name="print" arity="2"/> <fsummary>Put the Erlang term <c>TraceInfo</c>into the sequential trace output</fsummary> - <type> - <v>Label = int()</v> - <v>TraceInfo = term()</v> - </type> <desc> <p>Same as <c>print/1</c> with the additional condition that - <c>TraceInfo</c> is output only if <c>Label</c> is equal to + <c><anno>TraceInfo</anno></c> is output only if <c>Label</c> is equal to the label component of the trace token.</p> </desc> </func> <func> - <name>reset_trace() -> void()</name> + <name name="reset_trace" arity="0"/> <fsummary>Stop all sequential tracing on the local node</fsummary> <desc> <p>Sets the trace token to empty for all processes on the @@ -194,26 +185,22 @@ seq_trace:set_token(OldToken), % activate the trace token again </desc> </func> <func> - <name>set_system_tracer(Tracer) -> OldTracer</name> + <name name="set_system_tracer" arity="1"/> <fsummary>Set the system tracer</fsummary> - <type> - <v>Tracer = OldTracer = pid() | port() | false</v> - </type> + <type name="tracer"/> <desc> <p>Sets the system tracer. The system tracer can be either a - process or port denoted by <c>Tracer</c>. Returns the previous + process or port denoted by <c><anno>Tracer</anno></c>. Returns the previous value (which can be <c>false</c> if no system tracer is active).</p> - <p>Failure: <c>{badarg, Info}}</c> if <c>Pid</c> is not an + <p>Failure: <c>{badarg, Info}}</c> if <c><anno>Pid</anno></c> is not an existing local pid.</p> </desc> </func> <func> - <name>get_system_tracer() -> Tracer</name> + <name name="get_system_tracer" arity="0"/> <fsummary>Return the pid() or port() of the current system tracer.</fsummary> - <type> - <v>Tracer = pid() | port() | false</v> - </type> + <type name="tracer"/> <desc> <p>Returns the pid or port identifier of the current system tracer or <c>false</c> if no system tracer is activated.</p> diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml new file mode 100644 index 0000000000..b41addaa0c --- /dev/null +++ b/lib/kernel/doc/src/specs.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="latin1" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_application.xml"/> + <xi:include href="../specs/specs_auth.xml"/> + <xi:include href="../specs/specs_code.xml"/> + <xi:include href="../specs/specs_disk_log.xml"/> + <xi:include href="../specs/specs_erl_boot_server.xml"/> + <xi:include href="../specs/specs_erl_ddll.xml"/> + <xi:include href="../specs/specs_erl_prim_loader_stub.xml"/> + <xi:include href="../specs/specs_erlang_stub.xml"/> + <xi:include href="../specs/specs_error_handler.xml"/> + <xi:include href="../specs/specs_error_logger.xml"/> + <xi:include href="../specs/specs_file.xml"/> + <xi:include href="../specs/specs_gen_tcp.xml"/> + <xi:include href="../specs/specs_gen_udp.xml"/> + <xi:include href="../specs/specs_gen_sctp.xml"/> + <xi:include href="../specs/specs_global.xml"/> + <xi:include href="../specs/specs_global_group.xml"/> + <xi:include href="../specs/specs_heart.xml"/> + <xi:include href="../specs/specs_inet.xml"/> + <xi:include href="../specs/specs_inet_res.xml"/> + <xi:include href="../specs/specs_init_stub.xml"/> + <xi:include href="../specs/specs_net_adm.xml"/> + <xi:include href="../specs/specs_net_kernel.xml"/> + <xi:include href="../specs/specs_os.xml"/> + <xi:include href="../specs/specs_pg2.xml"/> + <xi:include href="../specs/specs_rpc.xml"/> + <xi:include href="../specs/specs_seq_trace.xml"/> + <xi:include href="../specs/specs_user.xml"/> + <xi:include href="../specs/specs_wrap_log_reader.xml"/> + <xi:include href="../specs/specs_zlib_stub.xml"/> + <xi:include href="../specs/specs_packages.xml"/> +</specs> diff --git a/lib/kernel/doc/src/wrap_log_reader.xml b/lib/kernel/doc/src/wrap_log_reader.xml index 18664a029f..6cf480b532 100644 --- a/lib/kernel/doc/src/wrap_log_reader.xml +++ b/lib/kernel/doc/src/wrap_log_reader.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1998</year><year>2009</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -50,18 +50,20 @@ the called node, it is entirely up to the user to be sure that all items are read. </p> </description> + <datatypes> + <datatype> + <name name="continuation"/> + <desc><p>Continuation returned by + <c>open/1,2</c> or <c>chunk/1,2</c>.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>chunk(Continuation)</name> - <name>chunk(Continuation, N) -> {Continuation2, Terms} | {Continuation2, Terms, Badbytes} | {Continuation2, eof} | {error, Reason}</name> + <name name="chunk" arity="1"/> + <name name="chunk" arity="2"/> <fsummary>Read a chunk of objects written to a wrap log.</fsummary> - <type> - <v>Continuation = continuation()</v> - <v>N = int() > 0 | infinity</v> - <v>Continuation2 = continuation()</v> - <v>Terms= [term()]</v> - <v>Badbytes = integer()</v> - </type> + <type name="chunk_ret"/> <desc> <p>This function makes it possible to efficiently read the terms which have been appended to a log. It minimises disk @@ -70,29 +72,29 @@ <p>The first time <c>chunk</c> is called an initial continuation returned from the <c>open/1</c>, <c>open/2</c> must be provided. </p> - <p>When <c>chunk/3</c> is called, <c>N</c> controls the + <p>When <c>chunk/3</c> is called, <c><anno>N</anno></c> controls the maximum number of terms that are read from the log in each chunk. Default is <c>infinity</c>, which means that all the terms contained in the 8K chunk are read. If less than - <c>N</c> terms are returned, this does not necessarily mean + <c><anno>N</anno></c> terms are returned, this does not necessarily mean that end of file is reached. </p> <p>The <c>chunk</c> function returns a tuple - <c>{Continuation2, Terms}</c>, where <c>Terms</c> is a list - of terms found in the log. <c>Continuation2</c> is yet + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list + of terms found in the log. <c><anno>Continuation2</anno></c> is yet another continuation which must be passed on into any subsequent calls to <c>chunk</c>. With a series of calls to <c>chunk</c> it is then possible to extract all terms from a log. </p> <p>The <c>chunk</c> function returns a tuple - <c>{Continuation2, Terms, Badbytes}</c> if the log is opened - in read only mode and the read chunk is corrupt. <c>Badbytes</c> + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened + in read only mode and the read chunk is corrupt. <c><anno>Badbytes</anno></c> indicates the number of non-Erlang terms found in the chunk. Note also that the log is not repaired. </p> - <p><c>chunk</c> returns <c>{Continuation2, eof}</c> when the end of the log is - reached, and <c>{error, Reason}</c> if an error occurs. + <p><c>chunk</c> returns <c>{<anno>Continuation2</anno>, eof}</c> when the end of the log is + reached, and <c>{error, <anno>Reason</anno>}</c> if an error occurs. </p> <p>The returned continuation may or may not be valid in the next call to <c>chunk</c>. This is because the log may wrap and delete @@ -103,37 +105,29 @@ </desc> </func> <func> - <name>close(Continuation) -> ok </name> + <name name="close" arity="1"/> <fsummary>Close a log</fsummary> - <type> - <v>Continuation = continuation()</v> - </type> <desc> <p>This function closes a log file properly. </p> </desc> </func> <func> - <name>open(Filename) -> OpenRet</name> - <name>open(Filename, N) -> OpenRet</name> + <name name="open" arity="1"/> + <name name="open" arity="2"/> <fsummary>Open a log file</fsummary> - <type> - <v>File = string() | atom()</v> - <v>N = integer()</v> - <v>OpenRet = {ok, Continuation} | {error, Reason} </v> - <v>Continuation = continuation()</v> - </type> + <type name="open_ret"/> <desc> - <p><c>Filename</c> specifies the name of the file which is to be read. </p> - <p><c>N</c> specifies the index of the file which is to be read. - If <c>N</c> is omitted the whole wrap log file will be read; if it + <p><c><anno>Filename</anno></c> specifies the name of the file which is to be read. </p> + <p><c><anno>N</anno></c> specifies the index of the file which is to be read. + If <c><anno>N</anno></c> is omitted the whole wrap log file will be read; if it is specified only the specified index file will be read. </p> - <p>The <c>open</c> function returns <c>{ok, Continuation}</c> if the - log/index file was successfully opened. The <c>Continuation</c> + <p>The <c>open</c> function returns <c>{ok, <anno>Continuation</anno>}</c> if the + log/index file was successfully opened. The <c><anno>Continuation</anno></c> is to be used when chunking or closing the file. </p> - <p>The function returns <c>{error, Reason}</c> for all errors. + <p>The function returns <c>{error, <anno>Reason</anno>}</c> for all errors. </p> </desc> </func> diff --git a/lib/kernel/include/inet.hrl b/lib/kernel/include/inet.hrl index 929b2ee294..3e64d4bb79 100644 --- a/lib/kernel/include/inet.hrl +++ b/lib/kernel/include/inet.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,18 +19,11 @@ %% This record is returned by inet:gethostbyaddr/2 and inet:gethostbyname/2. --type hostname() :: atom() | string(). --type ip4_address() :: {0..255,0..255,0..255,0..255}. --type ip6_address() :: {0..65535,0..65535,0..65535,0..65535, - 0..65535,0..65535,0..65535,0..65535}. --type ip_address() :: ip4_address() | ip6_address(). --type ip_port() :: 0..65535. - -record(hostent, { - h_name :: hostname(), %% offical name of host - h_aliases = [] :: [hostname()], %% alias list + h_name :: inet:hostname(), %% offical name of host + h_aliases = [] :: [inet:hostname()], %% alias list h_addrtype :: 'inet' | 'inet6', %% host address type h_length :: non_neg_integer(), %% length of address - h_addr_list = [] :: [ip_address()] %% list of addresses from name server + h_addr_list = [] :: [inet:ip_address()]%% list of addresses from name server }). diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl index 2a193affd4..fa3a4c3d36 100644 --- a/lib/kernel/src/application.erl +++ b/lib/kernel/src/application.erl @@ -32,20 +32,30 @@ %%%----------------------------------------------------------------- +-type start_type() :: 'normal' + | {'takeover', Node :: node()} + | {'failover', Node :: node()}. -type restart_type() :: 'permanent' | 'transient' | 'temporary'. --type application_opt() :: {'description', string()} - | {'vsn', string()} - | {'id', string()} - | {'modules', [atom() | {atom(), any()}]} - | {'registered', [atom()]} - | {'applications', [atom()]} - | {'included_applications', [atom()]} - | {'env', [{atom(), any()}]} - | {'start_phases', [{atom(), any()}] | 'undefined'} - | {'maxT', timeout()} % max timeout - | {'maxP', integer() | 'infinity'} % max processes - | {'mod', {atom(), any()}}. --type application_spec() :: {'application', atom(), [application_opt()]}. +-type application_opt() :: {'description', Description :: string()} + | {'vsn', Vsn :: string()} + | {'id', Id :: string()} + | {'modules', [(Module :: module()) | + {Module :: module(), Version :: term()}]} + | {'registered', Names :: [Name :: atom()]} + | {'applications', [Application :: atom()]} + | {'included_applications', [Application :: atom()]} + | {'env', [{Par :: atom(), Val :: term()}]} + | {'start_phases', + [{Phase :: atom(), PhaseArgs :: term()}] | 'undefined'} + | {'maxT', MaxT :: timeout()} % max timeout + | {'maxP', + MaxP :: pos_integer() | 'infinity'} % max processes + | {'mod', Start :: {Module :: module(), StartArgs :: term()}}. +-type application_spec() :: {'application', + Application :: atom(), + AppSpecKeys :: [application_opt()]}. + +-type(tuple_of(_T) :: tuple()). %%------------------------------------------------------------------ @@ -61,16 +71,29 @@ behaviour_info(_Other) -> %%% application_master. %%%----------------------------------------------------------------- --spec load(Application :: atom() | application_spec()) -> - 'ok' | {'error', term()}. +-spec load(AppDescr) -> 'ok' | {'error', Reason} when + AppDescr :: Application | (AppSpec :: application_spec()), + Application :: atom(), + Reason :: term(). load(Application) -> - load(Application, []). - --spec load(Application :: atom() | application_spec(), - Distributed :: any()) -> 'ok' | {'error', term()}. + load1(Application, []). + +-spec load(AppDescr, Distributed) -> 'ok' | {'error', Reason} when + AppDescr :: Application | (AppSpec :: application_spec()), + Application :: atom(), + Distributed :: {Application,Nodes} + | {Application,Time,Nodes} + | 'default', + Nodes :: [node() | tuple_of(node())], + Time :: pos_integer(), + Reason :: term(). load(Application, DistNodes) -> + load1(Application, DistNodes). + +%% Workaround due to specs. +load1(Application, DistNodes) -> case application_controller:load_application(Application) of ok when DistNodes =/= [] -> AppName = get_appl_name(Application), @@ -85,18 +108,24 @@ load(Application, DistNodes) -> Else end. --spec unload(Application :: atom()) -> 'ok' | {'error', term()}. +-spec unload(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). unload(Application) -> application_controller:unload_application(Application). --spec start(Application :: atom()) -> 'ok' | {'error', term()}. +-spec start(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). start(Application) -> start(Application, temporary). --spec start(Application :: atom() | application_spec(), - RestartType :: restart_type()) -> any(). +-spec start(Application, Type) -> 'ok' | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Reason :: term(). start(Application, RestartType) -> case load(Application) of @@ -120,12 +149,18 @@ start_boot(Application) -> start_boot(Application, RestartType) -> application_controller:start_boot_application(Application, RestartType). --spec takeover(Application :: atom(), RestartType :: restart_type()) -> any(). +-spec takeover(Application, Type) -> 'ok' | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Reason :: term(). takeover(Application, RestartType) -> dist_ac:takeover_application(Application, RestartType). --spec permit(Application :: atom(), Bool :: boolean()) -> 'ok' | {'error', term()}. +-spec permit(Application, Permission) -> 'ok' | {'error', Reason} when + Application :: atom(), + Permission :: boolean(), + Reason :: term(). permit(Application, Bool) -> case Bool of @@ -142,105 +177,146 @@ permit(Application, Bool) -> LocalResult end. --spec stop(Application :: atom()) -> 'ok' | {'error', term()}. +-spec stop(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). stop(Application) -> application_controller:stop_application(Application). --spec which_applications() -> [{atom(), string(), string()}]. +-spec which_applications() -> [{Application, Description, Vsn}] when + Application :: atom(), + Description :: string(), + Vsn :: string(). which_applications() -> application_controller:which_applications(). --spec which_applications(timeout()) -> [{atom(), string(), string()}]. +-spec which_applications(Timeout) -> [{Application, Description, Vsn}] when + Timeout :: timeout(), + Application :: atom(), + Description :: string(), + Vsn :: string(). which_applications(infinity) -> application_controller:which_applications(infinity); which_applications(Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:which_applications(Timeout). --spec loaded_applications() -> [{atom(), string(), string()}]. +-spec loaded_applications() -> [{Application, Description, Vsn}] when + Application :: atom(), + Description :: string(), + Vsn :: string(). loaded_applications() -> application_controller:loaded_applications(). --spec info() -> any(). +-spec info() -> term(). info() -> application_controller:info(). --spec set_env(Application :: atom(), Key :: atom(), Value :: any()) -> 'ok'. +-spec set_env(Application, Par, Val) -> 'ok' when + Application :: atom(), + Par :: atom(), + Val :: term(). set_env(Application, Key, Val) -> application_controller:set_env(Application, Key, Val). --spec set_env(Application :: atom(), Key :: atom(), - Value :: any(), Timeout :: timeout()) -> 'ok'. +-spec set_env(Application, Par, Val, Timeout) -> 'ok' when + Application :: atom(), + Par :: atom(), + Val :: term(), + Timeout :: timeout(). set_env(Application, Key, Val, infinity) -> application_controller:set_env(Application, Key, Val, infinity); set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:set_env(Application, Key, Val, Timeout). --spec unset_env(atom(), atom()) -> 'ok'. +-spec unset_env(Application, Par) -> 'ok' when + Application :: atom(), + Par :: atom(). unset_env(Application, Key) -> application_controller:unset_env(Application, Key). --spec unset_env(atom(), atom(), timeout()) -> 'ok'. +-spec unset_env(Application, Par, Timeout) -> 'ok' when + Application :: atom(), + Par :: atom(), + Timeout :: timeout(). unset_env(Application, Key, infinity) -> application_controller:unset_env(Application, Key, infinity); unset_env(Application, Key, Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:unset_env(Application, Key, Timeout). --spec get_env(atom()) -> 'undefined' | {'ok', term()}. +-spec get_env(Par) -> 'undefined' | {'ok', Val} when + Par :: atom(), + Val :: term(). get_env(Key) -> application_controller:get_pid_env(group_leader(), Key). --spec get_env(atom(), atom()) -> 'undefined' | {'ok', term()}. +-spec get_env(Application, Par) -> 'undefined' | {'ok', Val} when + Application :: atom(), + Par :: atom(), + Val :: term(). get_env(Application, Key) -> application_controller:get_env(Application, Key). --spec get_all_env() -> [{atom(), any()}]. +-spec get_all_env() -> Env when + Env :: [{Par :: atom(), Val :: term()}]. get_all_env() -> application_controller:get_pid_all_env(group_leader()). --spec get_all_env(atom()) -> [{atom(), any()}]. +-spec get_all_env(Application) -> Env when + Application :: atom(), + Env :: [{Par :: atom(), Val :: term()}]. get_all_env(Application) -> application_controller:get_all_env(Application). --spec get_key(atom()) -> 'undefined' | {'ok', term()}. +-spec get_key(Key) -> 'undefined' | {'ok', Val} when + Key :: atom(), + Val :: term(). get_key(Key) -> application_controller:get_pid_key(group_leader(), Key). --spec get_key(atom(), atom()) -> 'undefined' | {'ok', term()}. +-spec get_key(Application, Key) -> 'undefined' | {'ok', Val} when + Application :: atom(), + Key :: atom(), + Val :: term(). get_key(Application, Key) -> application_controller:get_key(Application, Key). --spec get_all_key() -> 'undefined' | [] | {'ok', [{atom(),any()},...]}. +-spec get_all_key() -> [] | {'ok', Keys} when + Keys :: [{Key :: atom(),Val :: term()},...]. get_all_key() -> application_controller:get_pid_all_key(group_leader()). --spec get_all_key(atom()) -> 'undefined' | {'ok', [{atom(),any()},...]}. +-spec get_all_key(Application) -> 'undefined' | Keys when + Application :: atom(), + Keys :: {'ok', [{Key :: atom(),Val :: term()},...]}. get_all_key(Application) -> application_controller:get_all_key(Application). --spec get_application() -> 'undefined' | {'ok', atom()}. +-spec get_application() -> 'undefined' | {'ok', Application} when + Application :: atom(). get_application() -> application_controller:get_application(group_leader()). --spec get_application(Pid :: pid()) -> 'undefined' | {'ok', atom()} - ; (Module :: atom()) -> 'undefined' | {'ok', atom()}. +-spec get_application(PidOrModule) -> 'undefined' | {'ok', Application} when + PidOrModule :: (Pid :: pid()) | (Module :: module()), + Application :: atom(). get_application(Pid) when is_pid(Pid) -> case process_info(Pid, group_leader) of @@ -252,8 +328,8 @@ get_application(Pid) when is_pid(Pid) -> get_application(Module) when is_atom(Module) -> application_controller:get_application_module(Module). --spec start_type() -> 'undefined' | 'local' | 'normal' - | {'takeover', node()} | {'failover', node()}. +-spec start_type() -> StartType | 'undefined' | 'local' when + StartType :: start_type(). start_type() -> application_controller:start_type(group_leader()). diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 5c7fe2421d..25c88a4e1d 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,7 +57,8 @@ start_link() -> %%--Deprecated interface------------------------------------------------ --spec is_auth(Node :: node()) -> 'yes' | 'no'. +-spec is_auth(Node) -> 'yes' | 'no' when + Node :: Node :: node(). is_auth(Node) -> case net_adm:ping(Node) of @@ -65,12 +66,15 @@ is_auth(Node) -> pang -> no end. --spec cookie() -> cookie(). +-spec cookie() -> Cookie when + Cookie :: cookie(). cookie() -> get_cookie(). --spec cookie(Cookies :: [cookie(),...] | cookie()) -> 'true'. +-spec cookie(TheCookie) -> 'true' when + TheCookie :: Cookie | [Cookie], + Cookie :: cookie(). cookie([Cookie]) -> set_cookie(Cookie); @@ -82,7 +86,9 @@ cookie(Cookie) -> node_cookie([Node, Cookie]) -> node_cookie(Node, Cookie). --spec node_cookie(Node :: node(), Cookie :: cookie()) -> 'yes' | 'no'. +-spec node_cookie(Node, Cookie) -> 'yes' | 'no' when + Node :: node(), + Cookie :: cookie(). node_cookie(Node, Cookie) -> set_cookie(Node, Cookie), diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 7f1b5f9ec6..9b8d2db437 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -70,9 +70,10 @@ %%% Contract type specifications %%%---------------------------------------------------------------------- +-opaque continuation() :: #continuation{}. + -type bytes() :: binary() | [byte()]. --type log() :: term(). % XXX: refine -type file_error() :: term(). % XXX: refine -type invalid_header() :: term(). % XXX: refine @@ -87,27 +88,30 @@ -type open_error_rsn() :: 'no_such_log' | {'badarg', term()} - | {'size_mismatch', dlog_size(), dlog_size()} - | {'arg_mismatch', dlog_optattr(), term(), term()} - | {'name_already_open', log()} - | {'open_read_write', log()} - | {'open_read_only', log()} - | {'need_repair', log()} - | {'not_a_log_file', string()} - | {'invalid_index_file', string()} + | {'size_mismatch', CurrentSize :: dlog_size(), + NewSize :: dlog_size()} + | {'arg_mismatch', OptionName :: dlog_optattr(), + CurrentValue :: term(), Value :: term()} + | {'name_already_open', Log :: log()} + | {'open_read_write', Log :: log()} + | {'open_read_only', Log :: log()} + | {'need_repair', Log :: log()} + | {'not_a_log_file', FileName :: file:filename()} + | {'invalid_index_file', FileName :: file:filename()} | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()} - | {'node_already_open', log()}. + | {'node_already_open', Log :: log()}. -type dist_error_rsn() :: 'nodedown' | open_error_rsn(). --type ret() :: {'ok', log()} - | {'repaired', log(), {'recovered', non_neg_integer()}, - {'badbytes', non_neg_integer()}}. +-type ret() :: {'ok', Log :: log()} + | {'repaired', Log :: log(), + {'recovered', Rec :: non_neg_integer()}, + {'badbytes', Bad :: non_neg_integer()}}. -type open_ret() :: ret() | {'error', open_error_rsn()}. -type dist_open_ret() :: {[{node(), ret()}], [{node(), {'error', dist_error_rsn()}}]}. --type all_open_ret() :: open_ret() | dist_open_ret(). --spec open(Args :: dlog_options()) -> all_open_ret(). +-spec open(ArgL) -> open_ret() | dist_open_ret() when + ArgL :: dlog_options(). open(A) -> disk_log_server:open(check_arg(A, #arg{options = A})). @@ -116,40 +120,57 @@ open(A) -> | {'full', log()} | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec log(Log :: log(), Term :: term()) -> 'ok' | {'error', log_error_rsn()}. +-spec log(Log, Term) -> ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + Term :: term(). log(Log, Term) -> req(Log, {log, term_to_binary(Term)}). --spec blog(Log :: log(), Bytes :: bytes()) -> 'ok' | {'error', log_error_rsn()}. +-spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + Bytes :: bytes(). blog(Log, Bytes) -> req(Log, {blog, check_bytes(Bytes)}). --spec log_terms(Log :: log(), Terms :: [term()]) -> 'ok' | {'error', term()}. +-spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when + Log :: log(), + TermList :: [term()]. log_terms(Log, Terms) -> Bs = terms2bins(Terms), req(Log, {log, Bs}). --spec blog_terms(Log :: log(), Bytes :: [bytes()]) -> 'ok' | {'error', term()}. +-spec blog_terms(Log, BytesList) -> + ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + BytesList :: [bytes()]. blog_terms(Log, Bytess) -> Bs = check_bytes_list(Bytess, Bytess), req(Log, {blog, Bs}). -type notify_ret() :: 'ok' | {'error', 'no_such_log'}. --spec alog(Log :: log(), Term :: term()) -> notify_ret(). +-spec alog(Log, Term) -> notify_ret() when + Log :: log(), + Term :: term(). alog(Log, Term) -> notify(Log, {alog, term_to_binary(Term)}). --spec alog_terms(Log :: log(), Terms :: [term()]) -> notify_ret(). +-spec alog_terms(Log, TermList) -> notify_ret() when + Log :: log(), + TermList :: [term()]. alog_terms(Log, Terms) -> Bs = terms2bins(Terms), notify(Log, {alog, Bs}). --spec balog(Log :: log(), Bytes :: bytes()) -> notify_ret(). +-spec balog(Log, Bytes) -> notify_ret() when + Log :: log(), + Bytes :: bytes(). balog(Log, Bytes) -> notify(Log, {balog, check_bytes(Bytes)}). --spec balog_terms(Log :: log(), Bytes :: [bytes()]) -> notify_ret(). +-spec balog_terms(Log, ByteList) -> notify_ret() when + Log :: log(), + ByteList :: [bytes()]. balog_terms(Log, Bytess) -> Bs = check_bytes_list(Bytess, Bytess), notify(Log, {balog, Bs}). @@ -157,18 +178,22 @@ balog_terms(Log, Bytess) -> -type close_error_rsn() ::'no_such_log' | 'nonode' | {'file_error', file:filename(), file_error()}. --spec close(Log :: log()) -> 'ok' | {'error', close_error_rsn()}. +-spec close(Log) -> 'ok' | {'error', close_error_rsn()} when + Log :: log(). close(Log) -> req(Log, close). -type lclose_error_rsn() :: 'no_such_log' | {'file_error', file:filename(), file_error()}. --spec lclose(Log :: log()) -> 'ok' | {'error', lclose_error_rsn()}. +-spec lclose(Log) -> 'ok' | {'error', lclose_error_rsn()} when + Log :: log(). lclose(Log) -> lclose(Log, node()). --spec lclose(Log :: log(), Node :: node()) -> 'ok' | {'error', lclose_error_rsn()}. +-spec lclose(Log, Node) -> 'ok' | {'error', lclose_error_rsn()} when + Log :: log(), + Node :: node(). lclose(Log, Node) -> lreq(Log, close, Node). @@ -178,29 +203,49 @@ lclose(Log, Node) -> | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec truncate(Log :: log()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec truncate(Log) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(). truncate(Log) -> req(Log, {truncate, none, truncate, 1}). --spec truncate(Log :: log(), Head :: term()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec truncate(Log, Head) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(), + Head :: term(). truncate(Log, Head) -> req(Log, {truncate, {ok, term_to_binary(Head)}, truncate, 2}). --spec btruncate(Log :: log(), Head :: bytes()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec btruncate(Log, BHead) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(), + BHead :: bytes(). btruncate(Log, Head) -> req(Log, {truncate, {ok, check_bytes(Head)}, btruncate, 2}). --spec reopen(Log :: log(), Filename :: file:filename()) -> 'ok' | {'error', term()}. +-type reopen_error_rsn() :: no_such_log + | nonode + | {read_only_mode, log()} + | {blocked_log, log()} + | {same_file_name, log()} | + {invalid_index_file, file:filename()} + | {invalid_header, invalid_header()} + | {'file_error', file:filename(), file_error()}. + +-spec reopen(Log, File) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(). reopen(Log, NewFile) -> req(Log, {reopen, NewFile, none, reopen, 2}). --spec reopen(Log :: log(), Filename :: file:filename(), Head :: term()) -> - 'ok' | {'error', term()}. +-spec reopen(Log, File, Head) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(), + Head :: term(). reopen(Log, NewFile, NewHead) -> req(Log, {reopen, NewFile, {ok, term_to_binary(NewHead)}, reopen, 3}). --spec breopen(Log :: log(), Filename :: file:filename(), Head :: bytes()) -> - 'ok' | {'error', term()}. +-spec breopen(Log, File, BHead) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(), + BHead :: bytes(). breopen(Log, NewFile, NewHead) -> req(Log, {reopen, NewFile, {ok, check_bytes(NewHead)}, breopen, 3}). @@ -210,21 +255,36 @@ breopen(Log, NewFile, NewHead) -> | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec inc_wrap_file(Log :: log()) -> 'ok' | {'error', inc_wrap_error_rsn()}. +-spec inc_wrap_file(Log) -> 'ok' | {'error', inc_wrap_error_rsn()} when + Log :: log(). inc_wrap_file(Log) -> req(Log, inc_wrap_file). --spec change_size(Log :: log(), Size :: dlog_size()) -> 'ok' | {'error', term()}. +-spec change_size(Log, Size) -> 'ok' | {'error', Reason} when + Log :: log(), + Size :: dlog_size(), + Reason :: no_such_log | nonode | {read_only_mode, Log} + | {blocked_log, Log} + | {new_size_too_small, CurrentSize :: pos_integer()} + | {badarg, size} + | {file_error, file:filename(), file_error()}. change_size(Log, NewSize) -> req(Log, {change_size, NewSize}). --spec change_notify(Log :: log(), Pid :: pid(), Notify :: boolean()) -> - 'ok' | {'error', term()}. +-spec change_notify(Log, Owner, Notify) -> 'ok' | {'error', Reason} when + Log :: log(), + Owner :: pid(), + Notify :: boolean(), + Reason :: no_such_log | nonode | {blocked_log, Log} + | {badarg, notify} | {not_owner, Owner}. change_notify(Log, Pid, NewNotify) -> req(Log, {change_notify, Pid, NewNotify}). --spec change_header(Log :: log(), Head :: {atom(), term()}) -> - 'ok' | {'error', term()}. +-spec change_header(Log, Header) -> 'ok' | {'error', Reason} when + Log :: log(), + Header :: {head, dlog_head_opt()} | {head_func, mfa()}, + Reason :: no_such_log | nonode | {read_only_mode, Log} + | {blocked_log, Log} | {badarg, head}. change_header(Log, NewHead) -> req(Log, {change_header, NewHead}). @@ -232,17 +292,21 @@ change_header(Log, NewHead) -> | {'blocked_log', log()} | {'file_error', file:filename(), file_error()}. --spec sync(Log :: log()) -> 'ok' | {'error', sync_error_rsn()}. +-spec sync(Log) -> 'ok' | {'error', sync_error_rsn()} when + Log :: log(). sync(Log) -> req(Log, sync). -type block_error_rsn() :: 'no_such_log' | 'nonode' | {'blocked_log', log()}. --spec block(Log :: log()) -> 'ok' | {'error', block_error_rsn()}. +-spec block(Log) -> 'ok' | {'error', block_error_rsn()} when + Log :: log(). block(Log) -> block(Log, true). --spec block(Log :: log(), QueueLogRecords :: boolean()) -> 'ok' | {'error', term()}. +-spec block(Log, QueueLogRecords) -> 'ok' | {'error', block_error_rsn()} when + Log :: log(), + QueueLogRecords :: boolean(). block(Log, QueueLogRecords) -> req(Log, {block, QueueLogRecords}). @@ -250,19 +314,46 @@ block(Log, QueueLogRecords) -> | {'not_blocked', log()} | {'not_blocked_by_pid', log()}. --spec unblock(Log :: log()) -> 'ok' | {'error', unblock_error_rsn()}. +-spec unblock(Log) -> 'ok' | {'error', unblock_error_rsn()} when + Log :: log(). unblock(Log) -> req(Log, unblock). --spec format_error(Error :: term()) -> string(). +-spec format_error(Error) -> io_lib:chars() when + Error :: term(). format_error(Error) -> do_format_error(Error). --spec info(Log :: log()) -> [{atom(), any()}] | {'error', term()}. +-type dlog_info() :: {name, Log :: log()} + | {file, File :: file:filename()} + | {type, Type :: dlog_type()} + | {format, Format :: dlog_format()} + | {size, Size :: dlog_size()} + | {mode, Mode :: dlog_mode()} + | {owners, [{pid(), Notify :: boolean()}]} + | {users, Users :: non_neg_integer()} + | {status, Status :: + ok | {blocked, QueueLogRecords :: boolean()}} + | {node, Node :: node()} + | {distributed, Dist :: local | [node()]} + | {head, Head :: none | {head, term()} | mfa()} + | {no_written_items, NoWrittenItems ::non_neg_integer()} + | {full, Full :: boolean} + | {no_current_bytes, non_neg_integer()} + | {no_current_items, non_neg_integer()} + | {no_items, non_neg_integer()} + | {current_file, pos_integer()} + | {no_overflows, {SinceLogWasOpened :: non_neg_integer(), + SinceLastInfo :: non_neg_integer()}}. +-spec info(Log) -> InfoList | {'error', no_such_log} when + Log :: log(), + InfoList :: [dlog_info()]. info(Log) -> sreq(Log, info). --spec pid2name(Pid :: pid()) -> {'ok', log()} | 'undefined'. +-spec pid2name(Pid) -> {'ok', Log} | 'undefined' when + Pid :: pid(), + Log :: log(). pid2name(Pid) -> disk_log_server:start(), case ets:lookup(?DISK_LOG_PID_TABLE, Pid) of @@ -274,13 +365,31 @@ pid2name(Pid) -> %% It retuns a {Cont2, ObjList} | eof | {error, Reason} %% The initial continuation is the atom 'start' --spec chunk(Log :: log(), Cont :: any()) -> - {'error', term()} | 'eof' | {any(), [any()]} | {any(), [any()], integer()}. +-type chunk_error_rsn() :: no_such_log + | {format_external, log()} + | {blocked_log, log()} + | {badarg, continuation} + | {not_internal_wrap, log()} + | {corrupt_log_file, FileName :: file:filename()} + | {file_error, file:filename(), file_error()}. + +-type chunk_ret() :: {Continuation2 :: continuation(), Terms :: [term()]} + | {Continuation2 :: continuation(), + Terms :: [term()], + Badbytes :: non_neg_integer()} + | eof + | {error, Reason :: chunk_error_rsn()}. + +-spec chunk(Log, Continuation) -> chunk_ret() when + Log :: log(), + Continuation :: start | continuation(). chunk(Log, Cont) -> chunk(Log, Cont, infinity). --spec chunk(Log :: log(), Cont :: any(), N :: pos_integer() | 'infinity') -> - {'error', term()} | 'eof' | {any(), [any()]} | {any(), [any()], integer()}. +-spec chunk(Log, Continuation, N) -> chunk_ret() when + Log :: log(), + Continuation :: start | continuation(), + N :: pos_integer() | infinity. chunk(Log, Cont, infinity) -> %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk. ichunk(Log, Cont, ?MAX_CHUNK_SIZE); @@ -346,13 +455,24 @@ ichunk_bad_end([B | Bs], Mode, Log, C, Bad, A) -> ichunk_bad_end(Bs, Mode, Log, C, Bad, [T | A]) end. --spec bchunk(Log :: log(), Cont :: any()) -> - {'error', any()} | 'eof' | {any(), [binary()]} | {any(), [binary()], integer()}. +-type bchunk_ret() :: {Continuation2 :: continuation(), + Binaries :: [binary()]} + | {Continuation2 :: continuation(), + Binaries :: [binary()], + Badbytes :: non_neg_integer()} + | eof + | {error, Reason :: chunk_error_rsn()}. + +-spec bchunk(Log, Continuation) -> bchunk_ret() when + Log :: log(), + Continuation :: start | continuation(). bchunk(Log, Cont) -> bchunk(Log, Cont, infinity). --spec bchunk(Log :: log(), Cont :: any(), N :: 'infinity' | pos_integer()) -> - {'error', any()} | 'eof' | {any(), [binary()]} | {any(), [binary()], integer()}. +-spec bchunk(Log, Continuation, N) -> bchunk_ret() when + Log :: log(), + Continuation :: start | continuation(), + N :: pos_integer() | infinity. bchunk(Log, Cont, infinity) -> %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk. bichunk(Log, Cont, ?MAX_CHUNK_SIZE); @@ -375,8 +495,14 @@ bichunk_end({C = #continuation{}, R, Bad}) -> bichunk_end(R) -> R. --spec chunk_step(Log :: log(), Cont :: any(), N :: integer()) -> - {'ok', any()} | {'error', term()}. +-spec chunk_step(Log, Continuation, Step) -> + {'ok', any()} | {'error', Reason} when + Log :: log(), + Continuation :: start | continuation(), + Step :: integer(), + Reason :: no_such_log | end_of_log | {format_external, Log} + | {blocked_log, Log} | {badarg, continuation} + | {file_error, file:filename(), file_error()}. chunk_step(Log, Cont, N) when is_integer(N) -> ichunk_step(Log, Cont, N). @@ -387,14 +513,18 @@ ichunk_step(_Log, More, N) when is_record(More, continuation) -> ichunk_step(_Log, _, _) -> {error, {badarg, continuation}}. --spec chunk_info(More :: any()) -> - [{'node', node()},...] | {'error', {'no_continuation', any()}}. +-spec chunk_info(Continuation) -> InfoList | {error, Reason} when + Continuation :: continuation(), + InfoList :: [{node, Node :: node()}, ...], + Reason :: {no_continuation, Continuation}. chunk_info(More = #continuation{}) -> [{node, node(More#continuation.pid)}]; chunk_info(BadCont) -> {error, {no_continuation, BadCont}}. --spec accessible_logs() -> {[_], [_]}. +-spec accessible_logs() -> {[LocalLog], [DistributedLog]} when + LocalLog :: log(), + DistributedLog :: log(). accessible_logs() -> disk_log_server:accessible_logs(). diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 9a94d4d3b9..259967650f 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,18 +53,34 @@ %% Types -- alphabetically %%------------------------------------------------------------------------ +-type dlog_byte() :: [dlog_byte()] | byte(). -type dlog_format() :: 'external' | 'internal'. -type dlog_format_type() :: 'halt_ext' | 'halt_int' | 'wrap_ext' | 'wrap_int'. -type dlog_head() :: 'none' | {'ok', binary()} | mfa(). +-type dlog_head_opt() :: none | term() | binary() | [dlog_byte()]. +-type log() :: term(). % XXX: refine -type dlog_mode() :: 'read_only' | 'read_write'. -type dlog_name() :: atom() | string(). -type dlog_optattr() :: 'name' | 'file' | 'linkto' | 'repair' | 'type' | 'format' | 'size' | 'distributed' | 'notify' | 'head' | 'head_func' | 'mode'. --type dlog_options() :: [{dlog_optattr(), any()}]. +-type dlog_option() :: {name, Log :: log()} + | {file, FileName :: file:filename()} + | {linkto, LinkTo :: none | pid()} + | {repair, Repair :: true | false | truncate} + | {type, Type :: dlog_type} + | {format, Format :: dlog_format()} + | {size, Size :: dlog_size()} + | {distributed, Nodes :: [node()]} + | {notify, boolean()} + | {head, Head :: dlog_head_opt()} + | {head_func, mfa()} + | {mode, Mode :: dlog_mode()}. +-type dlog_options() :: [dlog_option()]. -type dlog_repair() :: 'truncate' | boolean(). -type dlog_size() :: 'infinity' | pos_integer() - | {pos_integer(), pos_integer()}. + | {MaxNoBytes :: pos_integer(), + MaxNoFiles :: pos_integer()}. -type dlog_status() :: 'ok' | {'blocked', 'false' | [_]}. %QueueLogRecords -type dlog_type() :: 'halt' | 'wrap'. @@ -75,7 +91,7 @@ %% record of args for open -record(arg, {name = 0, version = undefined, - file = none :: 'none' | string(), + file = none :: 'none' | file:filename(), repair = true :: dlog_repair(), size = infinity :: dlog_size(), type = halt :: dlog_type(), diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl index b4c5f5e27c..0d68d3e198 100644 --- a/lib/kernel/src/erl_boot_server.erl +++ b/lib/kernel/src/erl_boot_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -59,7 +59,11 @@ -type ip4_address() :: {0..255,0..255,0..255,0..255}. --spec start(Slaves :: [atom()]) -> {'ok', pid()} | {'error', any()}. +-spec start(Slaves) -> {'ok', Pid} | {'error', What} when + Slaves :: [Host], + Host :: atom(), + Pid :: pid(), + What :: any(). start(Slaves) -> case check_arg(Slaves) of @@ -69,7 +73,11 @@ start(Slaves) -> {error, {badarg, Slaves}} end. --spec start_link(Slaves :: [atom()]) -> {'ok', pid()} | {'error', any()}. +-spec start_link(Slaves) -> {'ok', Pid} | {'error', What} when + Slaves :: [Host], + Host :: atom(), + Pid :: pid(), + What :: any(). start_link(Slaves) -> case check_arg(Slaves) of @@ -95,7 +103,10 @@ check_arg([], Result) -> check_arg(_, _Result) -> error. --spec add_slave(Slave :: atom()) -> 'ok' | {'error', any()}. +-spec add_slave(Slave) -> 'ok' | {'error', What} when + Slave :: Host, + Host :: atom(), + What :: any(). add_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -105,7 +116,10 @@ add_slave(Slave) -> {error, {badarg, Slave}} end. --spec delete_slave(Slave :: atom()) -> 'ok' | {'error', any()}. +-spec delete_slave(Slave) -> 'ok' | {'error', What} when + Slave :: Host, + Host :: atom(), + What :: any(). delete_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -131,7 +145,9 @@ add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> delete_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> gen_server:call(boot_server, {delete, {Mask, Addr}}). --spec which_slaves() -> [atom()]. +-spec which_slaves() -> Slaves when + Slaves :: [Host], + Host :: atom(). which_slaves() -> gen_server:call(boot_server, which). diff --git a/lib/kernel/src/erl_ddll.erl b/lib/kernel/src/erl_ddll.erl index ce64589a29..646cac99c5 100644 --- a/lib/kernel/src/erl_ddll.erl +++ b/lib/kernel/src/erl_ddll.erl @@ -44,14 +44,18 @@ start() -> stop() -> ok. --spec load_driver(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec load_driver(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: term(). load_driver(Path, Driver) -> do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]). --spec load(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec load(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc ::term(). load(Path, Driver) -> do_load_driver(Path, Driver, []). @@ -100,30 +104,41 @@ do_unload_driver(Driver,Flags) -> end end. --spec unload_driver(Driver :: driver()) -> 'ok' | {'error', any()}. +-spec unload_driver(Name) -> 'ok' | {'error', ErrorDesc} when + Name :: driver(), + ErrorDesc :: term(). unload_driver(Driver) -> do_unload_driver(Driver,[{monitor,pending_driver},kill_ports]). --spec unload(Driver :: driver()) -> 'ok' | {'error', any()}. +-spec unload(Name) -> 'ok' | {'error', ErrorDesc} when + Name :: driver(), + ErrorDesc :: term(). unload(Driver) -> do_unload_driver(Driver,[]). --spec reload(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec reload(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: pending_process | OpaqueError, + OpaqueError :: term(). reload(Path,Driver) -> do_load_driver(Path, Driver, [{reload,pending_driver}]). --spec reload_driver(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec reload_driver(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: pending_process | OpaqueError, + OpaqueError :: term(). reload_driver(Path,Driver) -> do_load_driver(Path, Driver, [{reload,pending_driver}, {driver_options,[kill_ports]}]). --spec format_error(Code :: atom()) -> string(). +-spec format_error(ErrorDesc) -> string() when + ErrorDesc :: term(). format_error(Code) -> case Code of @@ -135,7 +150,10 @@ format_error(Code) -> erl_ddll:format_error_int(Code) end. --spec info(Driver :: driver()) -> [{atom(), any()}, ...]. +-spec info(Name) -> InfoList when + Name :: driver(), + InfoList :: [InfoItem, ...], + InfoItem :: {Tag :: atom(), Value :: term()}. info(Driver) -> [{processes, erl_ddll:info(Driver,processes)}, @@ -146,7 +164,12 @@ info(Driver) -> {awaiting_load, erl_ddll:info(Driver,awaiting_load)}, {awaiting_unload, erl_ddll:info(Driver,awaiting_unload)}]. --spec info() -> [{string(), [{atom(), any()}]}]. +-spec info() -> AllInfoList when + AllInfoList :: [DriverInfo], + DriverInfo :: {DriverName, InfoList}, + DriverName :: string(), + InfoList :: [InfoItem], + InfoItem :: {Tag :: atom(), Value :: term()}. info() -> {ok,DriverList} = erl_ddll:loaded_drivers(), diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index 6f69f4ccb9..e1f99bf417 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -28,8 +28,11 @@ -export([undefined_function/3, undefined_lambda/3, stub_function/3, breakpoint/3]). --spec undefined_function(Module :: atom(), Function :: atom(), Args :: [_]) -> - any(). +-spec undefined_function(Module, Function, Args) -> + any() when + Module :: atom(), + Function :: atom(), + Args :: list(). undefined_function(Module, Func, Args) -> case ensure_loaded(Module) of @@ -51,8 +54,10 @@ undefined_function(Module, Func, Args) -> crash(Module, Func, Args) end. --spec undefined_lambda(Module :: atom(), Function :: fun(), Args :: [_]) -> - any(). +-spec undefined_lambda(Module, Fun, Args) -> term() when + Module :: atom(), + Fun :: fun(), + Args :: list(). undefined_lambda(Module, Fun, Args) -> case ensure_loaded(Module) of diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index cafdc52e84..f94cca000f 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -69,17 +69,22 @@ start_link() -> %% Used for simple messages; error or information. %%----------------------------------------------------------------- --spec error_msg(Format :: string()) -> 'ok'. +-spec error_msg(Format) -> 'ok' when + Format :: string(). error_msg(Format) -> error_msg(Format,[]). --spec error_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec error_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). error_msg(Format, Args) -> notify({error, group_leader(), {self(), Format, Args}}). --spec format(Format :: string(), Args :: list()) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). format(Format, Args) -> notify({error, group_leader(), {self(), Format, Args}}). @@ -90,12 +95,18 @@ format(Format, Args) -> %% The 'std_error' error_report type can always be used. %%----------------------------------------------------------------- --spec error_report(Report :: any()) -> 'ok'. +-type report() :: + [{Tag :: term(), Data :: term()} | term()] | string() | term(). + +-spec error_report(Report) -> 'ok' when + Report :: report(). error_report(Report) -> error_report(std_error, Report). --spec error_report(Type :: any(), Report :: any()) -> 'ok'. +-spec error_report(Type, Report) -> 'ok' when + Type :: term(), + Report :: report(). error_report(Type, Report) -> notify({error_report, group_leader(), {self(), Type, Report}}). @@ -109,12 +120,15 @@ error_report(Type, Report) -> %% mapped to std_info or std_error accordingly. %%----------------------------------------------------------------- --spec warning_report(Report :: any()) -> 'ok'. +-spec warning_report(Report) -> 'ok' when + Report :: report(). warning_report(Report) -> warning_report(std_warning, Report). --spec warning_report(Type :: any(), Report :: any()) -> 'ok'. +-spec warning_report(Type, Report) -> 'ok' when + Type :: any(), + Report :: report(). warning_report(Type, Report) -> {Tag, NType} = case error_logger:warning_map() of @@ -143,12 +157,15 @@ warning_report(Type, Report) -> %% other types of reports. %%----------------------------------------------------------------- --spec warning_msg(Format :: string()) -> 'ok'. +-spec warning_msg(Format) -> 'ok' when + Format :: string(). warning_msg(Format) -> warning_msg(Format,[]). --spec warning_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec warning_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). warning_msg(Format, Args) -> Tag = case error_logger:warning_map() of @@ -167,12 +184,15 @@ warning_msg(Format, Args) -> %% The 'std_info' info_report type can always be used. %%----------------------------------------------------------------- --spec info_report(Report :: any()) -> 'ok'. +-spec info_report(Report) -> 'ok' when + Report :: report(). info_report(Report) -> info_report(std_info, Report). --spec info_report(Type :: any(), Report :: any()) -> 'ok'. +-spec info_report(Type, Report) -> 'ok' when + Type :: any(), + Report :: report(). info_report(Type, Report) -> notify({info_report, group_leader(), {self(), Type, Report}}). @@ -182,12 +202,15 @@ info_report(Type, Report) -> %% information messages. %%----------------------------------------------------------------- --spec info_msg(Format :: string()) -> 'ok'. +-spec info_msg(Format) -> 'ok' when + Format :: string(). info_msg(Format) -> info_msg(Format,[]). --spec info_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec info_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). info_msg(Format, Args) -> notify({info_msg, group_leader(), {self(), Format, Args}}). @@ -223,17 +246,23 @@ swap_handler(silent) -> swap_handler(false) -> ok. % keep primitive event handler as-is --spec add_report_handler(Module :: atom()) -> any(). +-spec add_report_handler(Handler) -> any() when + Handler :: module(). add_report_handler(Module) when is_atom(Module) -> gen_event:add_handler(error_logger, Module, []). --spec add_report_handler(atom(), any()) -> any(). +-spec add_report_handler(Handler, Args) -> Result when + Handler :: module(), + Args :: gen_event:handler_args(), + Result :: gen_event:add_handler_ret(). add_report_handler(Module, Args) when is_atom(Module) -> gen_event:add_handler(error_logger, Module, Args). --spec delete_report_handler(Module :: atom()) -> any(). +-spec delete_report_handler(Handler) -> Result when + Handler :: module(), + Result :: gen_event:del_handler_ret(). delete_report_handler(Module) when is_atom(Module) -> gen_event:delete_handler(error_logger, Module, []). @@ -250,9 +279,16 @@ simple_logger() -> %% Log all errors to File for all eternity --spec logfile(Request :: {'open', string()}) -> 'ok' | {'error',any()} - ; (Request :: 'close') -> 'ok' | {'error', any()} - ; (Request :: 'filename') -> atom() | string() | {'error', any()}. +-type open_error() :: file:posix() | badarg | system_limit. + +-spec logfile(Request :: {open, Filename}) -> ok | {error, OpenReason} when + Filename ::file:name(), + OpenReason :: allready_have_logfile | open_error() + ; (Request :: close) -> ok | {error, CloseReason} when + CloseReason :: module_not_found + ; (Request :: filename) -> Filename | {error, FilenameReason} when + Filename :: file:name(), + FilenameReason :: no_log_file. logfile({open, File}) -> case lists:member(error_logger_file_h, @@ -280,7 +316,8 @@ logfile(filename) -> %% Possibly turn off all tty printouts, maybe we only want the errors %% to go to a file --spec tty(Flag :: boolean()) -> 'ok'. +-spec tty(Flag) -> 'ok' when + Flag :: boolean(). tty(true) -> Hs = gen_event:which_handlers(error_logger), diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 88bcf9a9cc..f1a8aa9f77 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -79,15 +79,19 @@ -type file_info() :: #file_info{}. -type fd() :: #file_descriptor{}. -type io_device() :: pid() | fd(). --type location() :: integer() | {'bof', integer()} | {'cur', integer()} - | {'eof', integer()} | 'bof' | 'cur' | 'eof'. +-type location() :: integer() | {'bof', Offset :: integer()} + | {'cur', Offset :: integer()} + | {'eof', Offset :: integer()} | 'bof' | 'cur' | 'eof'. -type mode() :: 'read' | 'write' | 'append' | 'exclusive' | 'raw' | 'binary' - | {'delayed_write', non_neg_integer(), non_neg_integer()} - | 'delayed_write' | {'read_ahead', pos_integer()} + | {'delayed_write', + Size :: non_neg_integer(), + Delay :: non_neg_integer()} + | 'delayed_write' | {'read_ahead', Size :: pos_integer()} | 'read_ahead' | 'compressed' | {'encoding', unicode:encoding()}. --type name() :: string() | atom() | [name()] | binary(). +-type deep_list() :: [char() | atom() | deep_list()]. +-type name() :: string() | atom() | deep_list() | (RawFilename :: binary()). -type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot' | 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval' | 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink' @@ -96,10 +100,14 @@ | 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | 'eperm' | 'epipe' | 'erofs' | 'espipe' | 'esrch' | 'estale' | 'exdev'. --type bindings() :: any(). - --type date() :: {pos_integer(), pos_integer(), pos_integer()}. --type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. +-type bindings() :: erl_eval:binding_struct(). + +-type date() :: {Year :: pos_integer(), + Month :: pos_integer(), + Day ::pos_integer()}. +-type time() :: {Hour :: non_neg_integer(), + Minute :: non_neg_integer(), + Second :: non_neg_integer()}. -type date_time() :: {date(), time()}. -type posix_file_advise() :: 'normal' | 'sequential' | 'random' | 'no_reuse' | 'will_need' | 'dont_need'. @@ -107,8 +115,10 @@ %%%----------------------------------------------------------------- %%% General functions --spec format_error(Reason :: posix() | {integer(), atom(), any()}) -> - string(). +-spec format_error(Reason) -> Chars when + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}, + Chars :: string(). format_error({_Line, ?MODULE, undefined_script}) -> "no value returned from script"; @@ -129,7 +139,9 @@ format_error(terminated) -> format_error(ErrorId) -> erl_posix_msg:message(ErrorId). --spec pid2name(Pid :: pid()) -> {'ok', filename()} | 'undefined'. +-spec pid2name(Pid) -> {ok, Filename} | undefined when + Filename :: filename(), + Pid :: pid(). pid2name(Pid) when is_pid(Pid) -> case whereis(?FILE_SERVER) of @@ -148,42 +160,61 @@ pid2name(Pid) when is_pid(Pid) -> %%% File server functions. %%% Functions that do not operate on a single open file. %%% Stateless. --spec get_cwd() -> {'ok', filename()} | {'error', posix()}. +-spec get_cwd() -> {ok, Dir} | {error, Reason} when + Dir :: filename(), + Reason :: posix(). get_cwd() -> call(get_cwd, []). --spec get_cwd(Drive :: string()) -> {'ok', filename()} | {'error', posix()}. +-spec get_cwd(Drive) -> {ok, Dir} | {error, Reason} when + Drive :: string(), + Dir :: filename(), + Reason :: posix() | badarg. get_cwd(Drive) -> check_and_call(get_cwd, [file_name(Drive)]). --spec set_cwd(Dirname :: name()) -> 'ok' | {'error', posix()}. +-spec set_cwd(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. set_cwd(Dirname) -> check_and_call(set_cwd, [file_name(Dirname)]). --spec delete(Name :: name()) -> 'ok' | {'error', posix()}. +-spec delete(Filename) -> ok | {error, Reason} when + Filename :: name(), + Reason :: posix() | badarg. delete(Name) -> check_and_call(delete, [file_name(Name)]). --spec rename(From :: name(), To :: name()) -> 'ok' | {'error', posix()}. +-spec rename(Source, Destination) -> ok | {error, Reason} when + Source :: name(), + Destination :: name(), + Reason :: posix() | badarg. rename(From, To) -> check_and_call(rename, [file_name(From), file_name(To)]). --spec make_dir(Name :: name()) -> 'ok' | {'error', posix()}. +-spec make_dir(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. make_dir(Name) -> check_and_call(make_dir, [file_name(Name)]). --spec del_dir(Name :: name()) -> 'ok' | {'error', posix()}. +-spec del_dir(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. del_dir(Name) -> check_and_call(del_dir, [file_name(Name)]). --spec read_file_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. +-spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when + Filename :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. read_file_info(Name) -> check_and_call(read_file_info, [file_name(Name)]). @@ -193,45 +224,66 @@ read_file_info(Name) -> altname(Name) -> check_and_call(altname, [file_name(Name)]). --spec read_link_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. +-spec read_link_info(Name) -> {ok, FileInfo} | {error, Reason} when + Name :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. read_link_info(Name) -> check_and_call(read_link_info, [file_name(Name)]). --spec read_link(Name :: name()) -> {'ok', filename()} | {'error', posix()}. +-spec read_link(Name) -> {ok, Filename} | {error, Reason} when + Name :: name(), + Filename :: filename(), + Reason :: posix() | badarg. read_link(Name) -> check_and_call(read_link, [file_name(Name)]). --spec write_file_info(Name :: name(), Info :: file_info()) -> - 'ok' | {'error', posix()}. +-spec write_file_info(Filename, FileInfo) -> ok | {error, Reason} when + Filename :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. write_file_info(Name, Info = #file_info{}) -> check_and_call(write_file_info, [file_name(Name), Info]). --spec list_dir(Name :: name()) -> {'ok', [filename()]} | {'error', posix()}. +-spec list_dir(Dir) -> {ok, Filenames} | {error, Reason} when + Dir :: name(), + Filenames :: [filename()], + Reason :: posix() | badarg. list_dir(Name) -> check_and_call(list_dir, [file_name(Name)]). --spec read_file(Name :: name()) -> - {'ok', binary()} | {'error', posix() | 'terminated' | 'system_limit'}. +-spec read_file(Filename) -> {ok, Binary} | {error, Reason} when + Filename :: name(), + Binary :: binary(), + Reason :: posix() | badarg | terminated | system_limit. read_file(Name) -> check_and_call(read_file, [file_name(Name)]). --spec make_link(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. +-spec make_link(Existing, New) -> ok | {error, Reason} when + Existing :: name(), + New :: name(), + Reason :: posix() | badarg. make_link(Old, New) -> check_and_call(make_link, [file_name(Old), file_name(New)]). --spec make_symlink(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. +-spec make_symlink(Name1, Name2) -> ok | {error, Reason} when + Name1 :: name(), + Name2 :: name(), + Reason :: posix() | badarg. make_symlink(Old, New) -> check_and_call(make_symlink, [file_name(Old), file_name(New)]). --spec write_file(Name :: name(), Bin :: iodata()) -> - 'ok' | {'error', posix() | 'terminated' | 'system_limit'}. +-spec write_file(Filename, Bytes) -> ok | {error, Reason} when + Filename :: name(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated | system_limit. write_file(Name, Bin) -> check_and_call(write_file, [file_name(Name), make_binary(Bin)]). @@ -240,8 +292,11 @@ write_file(Name, Bin) -> %% when it is time to change file server protocol again. %% Meanwhile, it is implemented here, slightly less efficient. --spec write_file(Name :: name(), Bin :: iodata(), Modes :: [mode()]) -> - 'ok' | {'error', posix()}. +-spec write_file(Filename, Bytes, Modes) -> ok | {error, Reason} when + Filename :: name(), + Bytes :: iodata(), + Modes :: [mode()], + Reason :: posix() | badarg | terminated | system_limit. write_file(Name, Bin, ModeList) when is_list(ModeList) -> case make_binary(Bin) of @@ -295,8 +350,11 @@ raw_write_file_info(Name, #file_info{} = Info) -> %% Contemporary mode specification - list of options --spec open(Name :: name(), Modes :: [mode()]) -> - {'ok', io_device()} | {'error', posix() | 'system_limit'}. +-spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when + Filename :: name(), + Modes :: [mode()], + IoDevice :: io_device(), + Reason :: posix() | badarg | system_limit. open(Item, ModeList) when is_list(ModeList) -> case lists:member(raw, ModeList) of @@ -349,7 +407,9 @@ open(Item, Mode) -> %%% The File argument must be either a Pid or a handle %%% returned from ?PRIM_FILE:open. --spec close(File :: io_device()) -> 'ok' | {'error', posix() | 'terminated'}. +-spec close(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. close(File) when is_pid(File) -> R = file_request(File, close), @@ -367,9 +427,12 @@ close(#file_descriptor{module = Module} = Handle) -> close(_) -> {error, badarg}. --spec advise(File :: io_device(), Offset :: integer(), - Length :: integer(), Advise :: posix_file_advise()) -> - 'ok' | {'error', posix()}. +-spec advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason} when + IoDevice :: io_device(), + Offset :: integer(), + Length :: integer(), + Advise :: posix_file_advise(), + Reason :: posix() | badarg. advise(File, Offset, Length, Advise) when is_pid(File) -> R = file_request(File, {advise, Offset, Length, Advise}), @@ -379,8 +442,11 @@ advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) -> advise(_, _, _, _) -> {error, badarg}. --spec read(File :: io_device() | atom(), Size :: non_neg_integer()) -> - 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. +-spec read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device() | atom(), + Number :: non_neg_integer(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 -> case io:request(File, {get_chars, '', Sz}) of @@ -395,8 +461,10 @@ read(#file_descriptor{module = Module} = Handle, Sz) read(_, _) -> {error, badarg}. --spec read_line(File :: io_device() | atom()) -> - 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. +-spec read_line(IoDevice) -> {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device() | atom(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. read_line(File) when (is_pid(File) orelse is_atom(File)) -> case io:request(File, {get_line, ''}) of @@ -410,9 +478,12 @@ read_line(#file_descriptor{module = Module} = Handle) -> read_line(_) -> {error, badarg}. --spec pread(File :: io_device(), - LocationNumbers :: [{location(), non_neg_integer()}]) -> - {'ok', [string() | binary() | 'eof']} | {'error', posix()}. +-spec pread(IoDevice, LocNums) -> {ok, DataL} | eof | {error, Reason} when + IoDevice :: io_device(), + LocNums :: [{Location :: location(), Number :: non_neg_integer()}], + DataL :: [Data], + Data :: string() | binary() | eof, + Reason :: posix() | badarg | terminated. pread(File, L) when is_pid(File), is_list(L) -> pread_int(File, L, []); @@ -435,10 +506,13 @@ pread_int(File, [{At, Sz} | T], R) when is_integer(Sz), Sz >= 0 -> pread_int(_, _, _) -> {error, badarg}. --spec pread(File :: io_device(), - Location :: location(), - Size :: non_neg_integer()) -> - 'eof' | {'ok', string() | binary()} | {'error', posix()}. +-spec pread(IoDevice, Location, Number) -> + {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + Number :: non_neg_integer(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. pread(File, At, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> R = file_request(File, {pread, At, Sz}), @@ -449,8 +523,10 @@ pread(#file_descriptor{module = Module} = Handle, Offs, Sz) pread(_, _, _) -> {error, badarg}. --spec write(File :: io_device() | atom(), Byte :: iodata()) -> - 'ok' | {'error', posix() | 'terminated'}. +-spec write(IoDevice, Bytes) -> ok | {error, Reason} when + IoDevice :: io_device() | atom(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated. write(File, Bytes) when (is_pid(File) orelse is_atom(File)) -> case make_binary(Bytes) of @@ -464,8 +540,11 @@ write(#file_descriptor{module = Module} = Handle, Bytes) -> write(_, _) -> {error, badarg}. --spec pwrite(File :: io_device(), L :: [{location(), iodata()}]) -> - 'ok' | {'error', {non_neg_integer(), posix()}}. +-spec pwrite(IoDevice, LocBytes) -> ok | {error, {N, Reason}} when + IoDevice :: io_device(), + LocBytes :: [{Location :: location(), Bytes :: iodata()}], + N :: non_neg_integer(), + Reason :: posix() | badarg | terminated. pwrite(File, L) when is_pid(File), is_list(L) -> pwrite_int(File, L, 0); @@ -486,10 +565,11 @@ pwrite_int(File, [{At, Bytes} | T], R) -> pwrite_int(_, _, _) -> {error, badarg}. --spec pwrite(File :: io_device(), - Location :: location(), - Bytes :: iodata()) -> - 'ok' | {'error', posix()}. +-spec pwrite(IoDevice, Location, Bytes) -> ok | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated. pwrite(File, At, Bytes) when is_pid(File) -> R = file_request(File, {pwrite, At, Bytes}), @@ -499,7 +579,9 @@ pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) -> pwrite(_, _, _) -> {error, badarg}. --spec datasync(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec datasync(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. datasync(File) when is_pid(File) -> R = file_request(File, datasync), @@ -509,7 +591,9 @@ datasync(#file_descriptor{module = Module} = Handle) -> datasync(_) -> {error, badarg}. --spec sync(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec sync(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. sync(File) when is_pid(File) -> R = file_request(File, sync), @@ -519,8 +603,11 @@ sync(#file_descriptor{module = Module} = Handle) -> sync(_) -> {error, badarg}. --spec position(File :: io_device(), Location :: location()) -> - {'ok',integer()} | {'error', posix()}. +-spec position(IoDevice, Location) -> {ok, NewPosition} | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + NewPosition :: integer(), + Reason :: posix() | badarg | terminated. position(File, At) when is_pid(File) -> R = file_request(File, {position,At}), @@ -530,7 +617,9 @@ position(#file_descriptor{module = Module} = Handle, At) -> position(_, _) -> {error, badarg}. --spec truncate(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec truncate(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. truncate(File) when is_pid(File) -> R = file_request(File, truncate), @@ -540,17 +629,26 @@ truncate(#file_descriptor{module = Module} = Handle) -> truncate(_) -> {error, badarg}. --spec copy(Source :: io_device() | name() | {name(), [mode()]}, - Destination :: io_device() | name() | {name(), [mode()]}) -> - {'ok', non_neg_integer()} | {'error', posix()}. +-spec copy(Source, Destination) -> {ok, BytesCopied} | {error, Reason} when + Source :: io_device() | Filename | {Filename, Modes}, + Destination :: io_device() | Filename | {Filename, Modes}, + Filename :: name(), + Modes :: [mode()], + BytesCopied :: non_neg_integer(), + Reason :: posix() | badarg | terminated. copy(Source, Dest) -> copy_int(Source, Dest, infinity). --spec copy(Source :: io_device() | name() | {name(), [mode()]}, - Destination :: io_device() | name() | {name(), [mode()]}, - Length :: non_neg_integer() | 'infinity') -> - {'ok', non_neg_integer()} | {'error', posix()}. +-spec copy(Source, Destination, ByteCount) -> + {ok, BytesCopied} | {error, Reason} when + Source :: io_device() | Filename | {Filename, Modes}, + Destination :: io_device() | Filename | {Filename, Modes}, + Filename :: name(), + Modes :: [mode()], + ByteCount :: non_neg_integer() | infinity, + BytesCopied :: non_neg_integer(), + Reason :: posix() | badarg | terminated. copy(Source, Dest, Length) when is_integer(Length), Length >= 0; @@ -772,8 +870,11 @@ ipread_s32bu_p32bu_2(File, %%% The following functions, built upon the other interface functions, %%% provide a higher-lever interface to files. --spec consult(File :: name()) -> - {'ok', list()} | {'error', posix() | {integer(), atom(), any()}}. +-spec consult(Filename) -> {ok, Terms} | {error, Reason} when + Filename :: name(), + Terms :: [term()], + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. consult(File) -> case open(File, [read]) of @@ -785,8 +886,14 @@ consult(File) -> Error end. --spec path_consult(Paths :: [name()], File :: name()) -> - {'ok', list(), filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason} when + Path :: [Dir], + Dir :: name(), + Filename :: name(), + Terms :: [term()], + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_consult(Path, File) -> case path_open(Path, File, [read]) of @@ -803,13 +910,19 @@ path_consult(Path, File) -> E2 end. --spec eval(File :: name()) -> 'ok' | {'error', posix()}. +-spec eval(Filename) -> ok | {error, Reason} when + Filename :: name(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. eval(File) -> eval(File, erl_eval:new_bindings()). --spec eval(File :: name(), Bindings :: bindings()) -> - 'ok' | {'error', posix()}. +-spec eval(Filename, Bindings) -> ok | {error, Reason} when + Filename :: name(), + Bindings :: bindings(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. eval(File, Bs) -> case open(File, [read]) of @@ -821,14 +934,24 @@ eval(File, Bs) -> Error end. --spec path_eval(Paths :: [name()], File :: name()) -> - {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_eval(Path, Filename) -> {ok, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_eval(Path, File) -> path_eval(Path, File, erl_eval:new_bindings()). --spec path_eval(Paths :: [name()], File :: name(), Bindings :: bindings()) -> - {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_eval(Path, Filename, Bindings) -> + {ok, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Bindings :: bindings(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_eval(Path, File, Bs) -> case path_open(Path, File, [read]) of @@ -845,14 +968,21 @@ path_eval(Path, File, Bs) -> E2 end. --spec script(File :: name()) -> - {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. +-spec script(Filename) -> {ok, Value} | {error, Reason} when + Filename :: name(), + Value :: term(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. script(File) -> script(File, erl_eval:new_bindings()). --spec script(File :: name(), Bindings :: bindings()) -> - {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. +-spec script(Filename, Bindings) -> {ok, Value} | {error, Reason} when + Filename :: name(), + Bindings :: bindings(), + Value :: term(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. script(File, Bs) -> case open(File, [read]) of @@ -864,16 +994,27 @@ script(File, Bs) -> Error end. --spec path_script/2 :: (Paths :: [name()], File :: name()) -> - {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. +-spec path_script(Path, Filename) -> + {ok, Value, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Value :: term(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_script(Path, File) -> path_script(Path, File, erl_eval:new_bindings()). --spec path_script(Paths :: [name()], - File :: name(), - Bindings :: bindings()) -> - {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. +-spec path_script(Path, Filename, Bindings) -> + {ok, Value, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Bindings :: bindings(), + Value :: term(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_script(Path, File, Bs) -> case path_open(Path, File, [read]) of @@ -898,8 +1039,14 @@ path_script(Path, File, Bs) -> %% Searches the Paths for file Filename which can be opened with Mode. %% The path list is ignored if Filename contains an absolute path. --spec path_open(Paths :: [name()], Name :: name(), Modes :: [mode()]) -> - {'ok', io_device(), filename()} | {'error', posix()}. +-spec path_open(Path, Filename, Modes) -> + {ok, IoDevice, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Modes :: [mode()], + IoDevice :: io_device(), + FullName :: filename(), + Reason :: posix() | badarg | system_limit. path_open(PathList, Name, Mode) -> case file_name(Name) of @@ -919,47 +1066,57 @@ path_open(PathList, Name, Mode) -> end end. --spec change_mode(Name :: name(), Mode :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_mode(Filename, Mode) -> ok | {error, Reason} when + Filename :: name(), + Mode :: integer(), + Reason :: posix() | badarg. change_mode(Name, Mode) when is_integer(Mode) -> write_file_info(Name, #file_info{mode=Mode}). --spec change_owner(Name :: name(), OwnerId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_owner(Filename, Uid) -> ok | {error, Reason} when + Filename :: name(), + Uid :: integer(), + Reason :: posix() | badarg. change_owner(Name, OwnerId) when is_integer(OwnerId) -> write_file_info(Name, #file_info{uid=OwnerId}). --spec change_owner(Name :: name(), - OwnerId :: integer(), - GroupId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_owner(Filename, Uid, Gid) -> ok | {error, Reason} when + Filename :: name(), + Uid :: integer(), + Gid :: integer(), + Reason :: posix() | badarg. change_owner(Name, OwnerId, GroupId) when is_integer(OwnerId), is_integer(GroupId) -> write_file_info(Name, #file_info{uid=OwnerId, gid=GroupId}). --spec change_group(Name :: name(), GroupId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_group(Filename, Gid) -> ok | {error, Reason} when + Filename :: name(), + Gid :: integer(), + Reason :: posix() | badarg. change_group(Name, GroupId) when is_integer(GroupId) -> write_file_info(Name, #file_info{gid=GroupId}). --spec change_time(Name :: name(), Time :: date_time()) -> - 'ok' | {'error', posix()}. +-spec change_time(Filename, Mtime) -> ok | {error, Reason} when + Filename :: name(), + Mtime :: date_time(), + Reason :: posix() | badarg. change_time(Name, Time) when is_tuple(Time) -> write_file_info(Name, #file_info{mtime=Time}). --spec change_time(Name :: name(), - ATime :: date_time(), - MTime :: date_time()) -> - 'ok' | {'error', posix()}. +-spec change_time(Filename, Atime, Mtime) -> ok | {error, Reason} when + Filename :: name(), + Atime :: date_time(), + Mtime :: date_time(), + Reason :: posix() | badarg. change_time(Name, Atime, Mtime) when is_tuple(Atime), is_tuple(Mtime) -> diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index cccfa75005..004f03f231 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,11 +33,57 @@ -export([error_string/1]). -export([controlling_process/2]). - +-opaque assoc_id() :: term(). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type sctp_option() :: + {mode, list | binary} | list | binary + | {active, true | false | once} + | {buffer, non_neg_integer()} + | {tos, integer()} + | {priority, integer()} + | {dontroute, boolean()} + | {reuseaddr, boolean()} + | {linger, {boolean(), non_neg_integer()}} + | {sndbuf, non_neg_integer()} + | {recbuf, non_neg_integer()} + | {sctp_rtoinfo, #sctp_rtoinfo{}} + | {sctp_associnfo, #sctp_assocparams{}} + | {sctp_initmsg, #sctp_initmsg{}} + | {sctp_autoclose, timeout()} + | {sctp_nodelay, boolean()} + | {sctp_disable_fragments, boolean()} + | {sctp_i_want_mapped_v4_addr, boolean()} + | {sctp_maxseg, non_neg_integer()} + | {sctp_primary_addr, #sctp_prim{}} + | {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} + | {sctp_adaptation_layer, #sctp_setadaptation{}} + | {sctp_peer_addr_params, #sctp_paddrparams{}} + | {sctp_default_send_param, #sctp_sndrcvinfo{}} + | {sctp_events, #sctp_event_subscribe{}} + | {sctp_delayed_ack_time, #sctp_assoc_value{}} + | {sctp_status, #sctp_status{}} + | {sctp_get_peer_addr_info, #sctp_paddrinfo{}}. +-opaque sctp_socket() :: port(). + +-spec open() -> {ok, Socket} | {error, posix()} when + Socket :: sctp_socket(). open() -> open([]). +-spec open(Port) -> {ok, Socket} | {error, posix()} when + Port :: port_number(), + Socket :: sctp_socket(); + (Opts) -> {ok, Socket} | {error, posix()} when + Opts :: [Opt], + Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), + IP :: ip_address() | any | loopback, + Port :: port_number(), + Socket :: sctp_socket(). + open(Opts) when is_list(Opts) -> Mod = mod(Opts, undefined), case Mod:open(Opts) of @@ -52,11 +98,21 @@ open(Port) when is_integer(Port) -> open(X) -> erlang:error(badarg, [X]). +-spec open(Port, Opts) -> {ok, Socket} | {error, posix()} when + Opts :: [Opt], + Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), + IP :: ip_address() | any | loopback, + Port :: port_number(), + Socket :: sctp_socket(). + open(Port, Opts) when is_integer(Port), is_list(Opts) -> open([{port,Port}|Opts]); open(Port, Opts) -> erlang:error(badarg, [Port,Opts]). +-spec close(Socket) -> ok | {error, posix()} when + Socket :: sctp_socket(). + close(S) when is_port(S) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -68,6 +124,11 @@ close(S) -> +-spec listen(Socket, IsServer) -> ok | {error, Reason} when + Socket :: sctp_socket(), + IsServer :: boolean(), + Reason :: term(). + listen(S, Flag) when is_port(S), is_boolean(Flag) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -77,9 +138,25 @@ listen(S, Flag) when is_port(S), is_boolean(Flag) -> listen(S, Flag) -> erlang:error(badarg, [S,Flag]). +-spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [Opt :: sctp_option()], + Assoc :: #sctp_assoc_change{}. + connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). +-spec connect(Socket, Addr, Port, Opts, Timeout) -> + {ok, Assoc} | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [Opt :: sctp_option()], + Timeout :: timeout(), + Assoc :: #sctp_assoc_change{}. + connect(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, true) of badarg -> @@ -88,9 +165,24 @@ connect(S, Addr, Port, Opts, Timeout) -> Result end. +-spec connect_init(Socket, Addr, Port, Opts) -> + ok | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [sctp_option()]. + connect_init(S, Addr, Port, Opts) -> connect_init(S, Addr, Port, Opts, infinity). +-spec connect_init(Socket, Addr, Port, Opts, Timeout) -> + ok | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [sctp_option()], + Timeout :: timeout(). + connect_init(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, false) of badarg -> @@ -130,12 +222,20 @@ do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) -> badarg. +-spec eof(Socket, Assoc) -> ok | {error, Reason} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{}, + Reason :: term(). eof(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> eof_or_abort(S, AssocId, eof); eof(S, Assoc) -> erlang:error(badarg, [S,Assoc]). +-spec abort(Socket, Assoc) -> ok | {error, posix()} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{}. + abort(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> eof_or_abort(S, AssocId, abort); abort(S, Assoc) -> @@ -151,6 +251,11 @@ eof_or_abort(S, AssocId, Action) -> end. +-spec send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} when + Socket :: sctp_socket(), + SndRcvInfo :: #sctp_sndrcvinfo{}, + Data :: binary | iolist(), + Reason :: term(). %% Full-featured send. Rarely needed. send(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) -> @@ -162,6 +267,13 @@ send(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) -> send(S, SRI, Data) -> erlang:error(badarg, [S,SRI,Data]). +-spec send(Socket, Assoc, Stream, Data) -> ok | {error, Reason} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{} | assoc_id(), + Stream :: integer(), + Data :: binary | iolist(), + Reason :: term(). + send(S, #sctp_assoc_change{assoc_id=AssocId}, Stream, Data) when is_port(S), is_integer(Stream) -> case inet_db:lookup_socket(S) of @@ -179,9 +291,36 @@ send(S, AssocId, Stream, Data) send(S, AssocChange, Stream, Data) -> erlang:error(badarg, [S,AssocChange,Stream,Data]). +-spec recv(Socket) -> {ok, {FromIP, FromPort, AncData, Data}} + | {error, Reason} when + Socket :: sctp_socket(), + FromIP :: ip_address(), + FromPort :: port_number(), + AncData :: [#sctp_sndrcvinfo{}], + Data :: binary() | string() | #sctp_sndrcvinfo{} + | #sctp_assoc_change{} | #sctp_paddr_change{} + | #sctp_adaptation_event{}, + Reason :: posix() | #sctp_send_failed{} | #sctp_paddr_change{} + | #sctp_pdapi_event{} | #sctp_remote_error{} + | #sctp_shutdown_event{}. + recv(S) -> recv(S, infinity). +-spec recv(Socket, Timeout) -> {ok, {FromIP, FromPort, AncData, Data}} + | {error, Reason} when + Socket :: sctp_socket(), + Timeout :: timeout(), + FromIP :: ip_address(), + FromPort :: port_number(), + AncData :: [#sctp_sndrcvinfo{}], + Data :: binary() | string() | #sctp_sndrcvinfo{} + | #sctp_assoc_change{} | #sctp_paddr_change{} + | #sctp_adaptation_event{}, + Reason :: posix() | #sctp_send_failed{} | #sctp_paddr_change{} + | #sctp_pdapi_event{} | #sctp_remote_error{} + | #sctp_shutdown_event{}. + recv(S, Timeout) when is_port(S) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -192,6 +331,8 @@ recv(S, Timeout) -> erlang:error(badarg, [S,Timeout]). +-spec error_string(ErrorNumber) -> ok | string() | unknown_error when + ErrorNumber :: integer(). error_string(0) -> ok; @@ -224,6 +365,9 @@ error_string(X) -> erlang:error(badarg, [X]). +-spec controlling_process(Socket, Pid) -> ok when + Socket :: sctp_socket(), + Pid :: pid(). controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 16a87d71b6..bee61ca84a 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,12 +28,35 @@ -include("inet_int.hrl"). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type socket() :: port(). + %% %% Connect a socket %% + +-spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when + Address :: ip_address() | hostname(), + Port :: port_number(), + Options :: [Opt :: term()], + Socket :: socket(), + Reason :: posix(). + connect(Address, Port, Opts) -> connect(Address,Port,Opts,infinity). +-spec connect(Address, Port, Options, Timeout) -> + {ok, Socket} | {error, Reason} when + Address :: ip_address() | hostname(), + Port :: port_number(), + Options :: [Opt :: term()], + Timeout :: timeout(), + Socket :: socket(), + Reason :: posix(). + connect(Address, Port, Opts, Time) -> Timer = inet:start_timer(Time), Res = (catch connect1(Address,Port,Opts,Timer)), @@ -72,6 +95,13 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> %% %% Listen on a tcp port %% + +-spec listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} when + Port :: port_number(), + Options :: [Opt :: term()], + ListenSocket :: socket(), + Reason :: posix(). + listen(Port, Opts) -> Mod = mod(Opts, undefined), case Mod:getserv(Port) of @@ -85,6 +115,12 @@ listen(Port, Opts) -> %% %% Generic tcp accept %% + +-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when + ListenSocket :: socket(), + Socket :: socket(), + Reason :: closed | timeout | posix(). + accept(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -93,6 +129,12 @@ accept(S) -> Error end. +-spec accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason} when + ListenSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: closed | timeout | posix(). + accept(S, Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -104,6 +146,12 @@ accept(S, Time) when is_port(S) -> %% %% Generic tcp shutdown %% + +-spec shutdown(Socket, How) -> ok | {error, Reason} when + Socket :: socket(), + How :: read | write | read_write, + Reason :: posix(). + shutdown(S, How) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -115,12 +163,22 @@ shutdown(S, How) when is_port(S) -> %% %% Close %% + +-spec close(Socket) -> ok when + Socket :: socket(). + close(S) -> inet:tcp_close(S). %% %% Send %% + +-spec send(Socket, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Packet :: string() | binary(), + Reason :: posix(). + send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -132,6 +190,14 @@ send(S, Packet) when is_port(S) -> %% %% Receive data from a socket (passive mode) %% + +-spec recv(Socket, Length) -> {ok, Packet} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Packet :: string() | binary() | HttpPacket, + Reason :: closed | posix(), + HttpPacket :: term(). + recv(S, Length) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -140,6 +206,14 @@ recv(S, Length) when is_port(S) -> Error end. +-spec recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Timeout :: timeout(), + Packet :: string() | binary() | HttpPacket, + Reason :: closed | posix(), + HttpPacket :: term(). + recv(S, Length, Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -159,6 +233,12 @@ unrecv(S, Data) when is_port(S) -> %% %% Set controlling process %% + +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when + Socket :: socket(), + Pid :: pid(), + Reason :: closed | not_owner | posix(). + controlling_process(S, NewOwner) -> case inet_db:lookup_socket(S) of {ok, _Mod} -> % Just check that this is an open socket diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 99020c7b6c..7d14615c04 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,17 +25,44 @@ -include("inet_int.hrl"). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type socket() :: port(). + +-spec open(Port) -> {ok, Socket} | {error, Reason} when + Port :: port_number(), + Socket :: socket(), + Reason :: posix(). + open(Port) -> open(Port, []). +-spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when + Port :: port_number(), + Opts :: [Opt :: term()], + Socket :: socket(), + Reason :: posix(). + open(Port, Opts) -> Mod = mod(Opts, undefined), {ok,UP} = Mod:getserv(Port), Mod:open(UP, Opts). +-spec close(Socket) -> ok when + Socket :: socket(). + close(S) -> inet:udp_close(S). +-spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Address :: ip_address() | hostname(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + send(S, Address, Port, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -61,6 +88,15 @@ send(S, Packet) when is_port(S) -> Error end. +-spec recv(Socket, Length) -> + {ok, {Address, Port, Packet}} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Address :: ip_address(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + recv(S,Len) when is_port(S), is_integer(Len) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -69,6 +105,16 @@ recv(S,Len) when is_port(S), is_integer(Len) -> Error end. +-spec recv(Socket, Length, Timeout) -> + {ok, {Address, Port, Packet}} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Timeout :: timeout(), + Address :: ip_address(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + recv(S,Len,Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -90,6 +136,10 @@ connect(S, Address, Port) when is_port(S) -> Error end. +-spec controlling_process(Socket, Pid) -> ok when + Socket :: socket(), + Pid :: pid(). + controlling_process(S, NewOwner) -> inet:udp_controlling_process(S, NewOwner). diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 6343acd000..7d15f8bf83 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -166,7 +166,7 @@ start_link() -> stop() -> gen_server:call(global_name_server, stop, infinity). --spec sync() -> 'ok' | {'error', term()}. +-spec sync() -> 'ok' | {'error', Reason :: term()}. sync() -> case check_sync_nodes() of {error, _} = Error -> @@ -175,7 +175,7 @@ sync() -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end. --spec sync([node()]) -> 'ok' | {'error', term()}. +-spec sync([node()]) -> 'ok' | {'error', Reason :: term()}. sync(Nodes) -> case check_sync_nodes(Nodes) of {error, _} = Error -> @@ -184,7 +184,10 @@ sync(Nodes) -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end. --spec send(term(), term()) -> pid(). +-spec send(Name, Msg) -> Pid when + Name :: term(), + Msg :: term(), + Pid :: pid(). send(Name, Msg) -> case whereis_name(Name) of Pid when is_pid(Pid) -> @@ -195,7 +198,8 @@ send(Name, Msg) -> end. %% See OTP-3737. --spec whereis_name(term()) -> pid() | 'undefined'. +-spec whereis_name(Name) -> pid() | 'undefined' when + Name :: term(). whereis_name(Name) -> where(Name). @@ -219,13 +223,19 @@ node_disconnected(Node) -> %% undefined which one of them is used. %% Method blocks the name registration, but does not affect global locking. %%----------------------------------------------------------------- --spec register_name(term(), pid()) -> 'yes' | 'no'. +-spec register_name(Name, Pid) -> 'yes' | 'no' when + Name :: term(), + Pid :: pid(). register_name(Name, Pid) when is_pid(Pid) -> register_name(Name, Pid, fun random_exit_name/3). --type method() :: fun((term(), pid(), pid()) -> pid() | 'none'). +-type method() :: fun((Name :: term(), Pid :: pid(), Pid2 :: pid()) -> + pid() | 'none'). --spec register_name(term(), pid(), method()) -> 'yes' | 'no'. +-spec register_name(Name, Pid, Resolve) -> 'yes' | 'no' when + Name :: term(), + Pid :: pid(), + Resolve :: method(). register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of @@ -257,7 +267,8 @@ check_dupname(Name, Pid) -> end end. --spec unregister_name(term()) -> _. +-spec unregister_name(Name) -> _ when + Name :: term(). unregister_name(Name) -> case where(Name) of undefined -> @@ -273,11 +284,16 @@ unregister_name(Name) -> gen_server:call(global_name_server, {registrar, Fun}, infinity) end. --spec re_register_name(term(), pid()) -> _. +-spec re_register_name(Name, Pid) -> _ when + Name :: term(), + Pid :: pid(). re_register_name(Name, Pid) when is_pid(Pid) -> re_register_name(Name, Pid, fun random_exit_name/3). --spec re_register_name(term(), pid(), method()) -> _. +-spec re_register_name(Name, Pid, Resolve) -> _ when + Name :: term(), + Pid :: pid(), + Resolve :: method(). re_register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> gen_server:multi_call(Nodes, @@ -288,7 +304,8 @@ re_register_name(Name, Pid, Method) when is_pid(Pid) -> ?trace({re_register_name, self(), Name, Pid, Method}), gen_server:call(global_name_server, {registrar, Fun}, infinity). --spec registered_names() -> [term()]. +-spec registered_names() -> [Name] when + Name :: term(). registered_names() -> MS = ets:fun2ms(fun({Name,_Pid,_M,_RP,_R}) -> Name end), ets:select(global_names, MS). @@ -329,19 +346,25 @@ register_name_external(Name, Pid, Method) when is_pid(Pid) -> unregister_name_external(Name) -> unregister_name(Name). --type id() :: {term(), term()}. +-type id() :: {ResourceId :: term(), LockRequesterId :: term()}. --spec set_lock(id()) -> boolean(). +-spec set_lock(Id) -> boolean() when + Id :: id(). set_lock(Id) -> set_lock(Id, [node() | nodes()], infinity, 1). -type retries() :: non_neg_integer() | 'infinity'. --spec set_lock(id(), [node()]) -> boolean(). +-spec set_lock(Id, Nodes) -> boolean() when + Id :: id(), + Nodes :: [node()]. set_lock(Id, Nodes) -> set_lock(Id, Nodes, infinity, 1). --spec set_lock(id(), [node()], retries()) -> boolean(). +-spec set_lock(Id, Nodes, Retries) -> boolean() when + Id :: id(), + Nodes :: [node()], + Retries :: retries(). set_lock(Id, Nodes, Retries) when is_integer(Retries), Retries >= 0 -> set_lock(Id, Nodes, Retries, 1); set_lock(Id, Nodes, infinity) -> @@ -363,11 +386,14 @@ set_lock({_ResourceId, _LockRequesterId} = Id, Nodes, Retries, Times) -> set_lock(Id, Nodes, dec(Retries), Times+1) end. --spec del_lock(id()) -> 'true'. +-spec del_lock(Id) -> 'true' when + Id :: id(). del_lock(Id) -> del_lock(Id, [node() | nodes()]). --spec del_lock(id(), [node()]) -> 'true'. +-spec del_lock(Id, Nodes) -> 'true' when + Id :: id(), + Nodes :: [node()]. del_lock({_ResourceId, _LockRequesterId} = Id, Nodes) -> ?trace({del_lock, {me,self()}, Id, {nodes,Nodes}}), gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}), @@ -375,13 +401,25 @@ del_lock({_ResourceId, _LockRequesterId} = Id, Nodes) -> -type trans_fun() :: function() | {module(), atom()}. --spec trans(id(), trans_fun()) -> term(). +-spec trans(Id, Fun) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Res :: term(). trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity). --spec trans(id(), trans_fun(), [node()]) -> term(). +-spec trans(Id, Fun, Nodes) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Nodes :: [node()], + Res :: term(). trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity). --spec trans(id(), trans_fun(), [node()], retries()) -> term(). +-spec trans(Id, Fun, Nodes, Retries) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Nodes :: [node()], + Retries :: retries(), + Res :: term(). trans(Id, Fun, Nodes, Retries) -> case set_lock(Id, Nodes, Retries) of true -> @@ -1928,7 +1966,10 @@ resolve_it(Method, Name, Pid1, Pid2) -> minmax(P1,P2) -> if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end. --spec random_exit_name(term(), pid(), pid()) -> pid(). +-spec random_exit_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). random_exit_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), error_logger:info_msg("global: Name conflict terminating ~w\n", @@ -1936,12 +1977,19 @@ random_exit_name(Name, Pid, Pid2) -> exit(Max, kill), Min. +-spec random_notify_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). random_notify_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), Max ! {global_name_conflict, Name}, Min. --spec notify_all_name(term(), pid(), pid()) -> 'none'. +-spec notify_all_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). notify_all_name(Name, Pid, Pid2) -> Pid ! {global_name_conflict, Name, Pid2}, Pid2 ! {global_name_conflict, Name, Pid}, diff --git a/lib/kernel/src/global_group.erl b/lib/kernel/src/global_group.erl index 7e141ac5c7..025a9b8a5b 100644 --- a/lib/kernel/src/global_group.erl +++ b/lib/kernel/src/global_group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -62,9 +62,10 @@ -type sync_state() :: 'no_conf' | 'synced'. -type group_name() :: atom(). --type group_tuple() :: {group_name(), [node()]} - | {group_name(), publish_type(), [node()]}. - +-type group_tuple() :: {GroupName :: group_name(), [node()]} + | {GroupName :: group_name(), + PublishType :: publish_type(), + [node()]}. %%%==================================================================================== %%% The state of the global_group process @@ -97,11 +98,14 @@ %%% External exported %%%==================================================================================== --spec global_groups() -> {group_name(), [group_name()]} | 'undefined'. +-spec global_groups() -> {GroupName, GroupNames} | undefined when + GroupName :: group_name(), + GroupNames :: [GroupName]. global_groups() -> request(global_groups). --spec monitor_nodes(boolean()) -> 'ok'. +-spec monitor_nodes(Flag) -> 'ok' when + Flag :: boolean(). monitor_nodes(Flag) -> case Flag of true -> request({monitor_nodes, Flag}); @@ -109,30 +113,41 @@ monitor_nodes(Flag) -> _ -> {error, not_boolean} end. --spec own_nodes() -> [node()]. +-spec own_nodes() -> Nodes when + Nodes :: [Node :: node()]. own_nodes() -> request(own_nodes). -type name() :: atom(). -type where() :: {'node', node()} | {'group', group_name()}. --spec registered_names(where()) -> [name()]. +-spec registered_names(Where) -> Names when + Where :: where(), + Names :: [Name :: name()]. registered_names(Arg) -> request({registered_names, Arg}). --spec send(name(), term()) -> pid() | {'badarg', {name(), term()}}. +-spec send(Name, Msg) -> pid() | {'badarg', {Name, Msg}} when + Name :: name(), + Msg :: term(). send(Name, Msg) -> request({send, Name, Msg}). --spec send(where(), name(), term()) -> pid() | {'badarg', {name(), term()}}. +-spec send(Where, Name, Msg) -> pid() | {'badarg', {Name, Msg}} when + Where :: where(), + Name :: name(), + Msg :: term(). send(Group, Name, Msg) -> request({send, Group, Name, Msg}). --spec whereis_name(name()) -> pid() | 'undefined'. +-spec whereis_name(Name) -> pid() | 'undefined' when + Name :: name(). whereis_name(Name) -> request({whereis_name, Name}). --spec whereis_name(where(), name()) -> pid() | 'undefined'. +-spec whereis_name(Where, Name) -> pid() | 'undefined' when + Where :: where(), + Name :: name(). whereis_name(Group, Name) -> request({whereis_name, Group, Name}). @@ -155,14 +170,14 @@ ng_add_check(Node, OthersNG) -> ng_add_check(Node, PubType, OthersNG) -> request({ng_add_check, Node, PubType, OthersNG}). --type info_item() :: {'state', sync_state()} - | {'own_group_name', group_name()} - | {'own_group_nodes', [node()]} - | {'synched_nodes', [node()]} - | {'sync_error', [node()]} - | {'no_contact', [node()]} - | {'other_groups', [group_tuple()]} - | {'monitoring', [pid()]}. +-type info_item() :: {'state', State :: sync_state()} + | {'own_group_name', GroupName :: group_name()} + | {'own_group_nodes', Nodes :: [node()]} + | {'synched_nodes', Nodes :: [node()]} + | {'sync_error', Nodes :: [node()]} + | {'no_contact', Nodes :: [node()]} + | {'other_groups', Groups :: [group_tuple()]} + | {'monitoring', Pids :: [pid()]}. -spec info() -> [info_item()]. info() -> @@ -1012,6 +1027,7 @@ grp_tuple({Name, normal, Nodes}) -> %%% The special process which checks that all nodes in the own global group %%% agrees on the configuration. %%%==================================================================================== +-spec sync_init(_, _, _, _) -> no_return(). sync_init(Type, Cname, PubType, Nodes) -> {Up, Down} = sync_check_node(lists:delete(node(), Nodes), [], []), sync_check_init(Type, Up, Cname, Nodes, Down, PubType). @@ -1032,9 +1048,11 @@ sync_check_node([Node|Nodes], Up, Down) -> %%% Check that all nodes are in agreement of the global %%% group configuration. %%%------------------------------------------------------------- +-spec sync_check_init(_, _, _, _, _, _) -> no_return(). sync_check_init(Type, Up, Cname, Nodes, Down, PubType) -> sync_check_init(Type, Up, Cname, Nodes, 3, [], Down, PubType). +-spec sync_check_init(_, _, _, _, _, _, _, _) -> no_return(). sync_check_init(_Type, NoContact, _Cname, _Nodes, 0, ErrorNodes, Down, _PubType) -> case ErrorNodes of [] -> diff --git a/lib/kernel/src/global_search.erl b/lib/kernel/src/global_search.erl index b723e18a1b..0bf53e29b8 100644 --- a/lib/kernel/src/global_search.erl +++ b/lib/kernel/src/global_search.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -69,6 +69,7 @@ start(Flag, Arg) -> %%%==================================================================================== %%%==================================================================================== +-spec init_send(_) -> no_return(). init_send({any, NodesList, Name, Msg, From}) -> case whereis_any_loop(NodesList, Name) of undefined -> @@ -115,6 +116,7 @@ init_send({node, Node, Name, Msg, From}) -> %%%==================================================================================== %%%==================================================================================== +-spec init_whereis(_) -> no_return(). init_whereis({any, NodesList, Name, From}) -> R = whereis_any_loop(NodesList, Name), gen_server:cast(global_group, {find_name_res, R, self(), From}), @@ -146,6 +148,7 @@ init_whereis({node, Node, Name, From}) -> %%%==================================================================================== %%%==================================================================================== %%%==================================================================================== +-spec init_names(_) -> no_return(). init_names({group, Nodes, From}) -> case names_group_loop(Nodes) of group_down -> diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index e78acfc7a6..255ae4e51b 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -85,19 +85,21 @@ init(Starter, Parent) -> Starter ! {start_error, self()} end. --spec set_cmd(string()) -> 'ok' | {'error', {'bad_cmd', string()}}. +-spec set_cmd(Cmd) -> 'ok' | {'error', {'bad_cmd', Cmd}} when + Cmd :: string(). set_cmd(Cmd) -> heart ! {self(), set_cmd, Cmd}, wait(). --spec get_cmd() -> 'ok'. +-spec get_cmd() -> {ok, Cmd} when + Cmd :: string(). get_cmd() -> heart ! {self(), get_cmd}, wait(). --spec clear_cmd() -> {'ok', string()}. +-spec clear_cmd() -> ok. clear_cmd() -> heart ! {self(), clear_cmd}, diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 327e0f93f1..5649188c38 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -63,7 +63,8 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). --export_type([ip_address/0, socket/0]). +-export_type([family_option/0, hostent/0, hostname/0, ip4_address/0, + ip6_address/0, ip_address/0, posix/0, socket/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). @@ -79,8 +80,16 @@ %%% --------------------------------- %%% Contract type definitions + +-type hostent() :: #hostent{}. +-type hostname() :: atom() | string(). +-type ip4_address() :: {0..255,0..255,0..255,0..255}. +-type ip6_address() :: {0..65535,0..65535,0..65535,0..65535, + 0..65535,0..65535,0..65535,0..65535}. +-type ip_address() :: ip4_address() | ip6_address(). +-type ip_port() :: 0..65535. +-type posix() :: exbadport | exbadseq | file:posix(). -type socket() :: port(). --type posix() :: atom(). -type socket_setopt() :: {'raw', non_neg_integer(), non_neg_integer(), binary()} | @@ -106,7 +115,7 @@ {'packet', 0 | 1 | 2 | 4 | 'raw' | 'sunrm' | 'asn1' | 'cdr' | 'fcgi' | 'line' | 'tpkt' | 'http' | 'httph' | 'http_bin' | 'httph_bin' } | - {'mode', list() | binary()} | + {'mode', 'list' | 'binary'} | {'port', 'port', 'term'} | {'exit_on_close', boolean()} | {'low_watermark', non_neg_integer()} | @@ -195,12 +204,13 @@ %%% --------------------------------- --spec get_rc() -> [{any(),any()}]. +-spec get_rc() -> [{Par :: any(), Val :: any()}]. get_rc() -> inet_db:get_rc(). --spec close(Socket :: socket()) -> 'ok'. +-spec close(Socket) -> 'ok' when + Socket :: socket(). close(Socket) -> prim_inet:close(Socket), @@ -211,8 +221,10 @@ close(Socket) -> ok end. --spec peername(Socket :: socket()) -> - {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}. +-spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). peername(Socket) -> prim_inet:peername(Socket). @@ -226,8 +238,10 @@ setpeername(Socket, undefined) -> prim_inet:setpeername(Socket, undefined). --spec sockname(Socket :: socket()) -> - {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}. +-spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). sockname(Socket) -> prim_inet:sockname(Socket). @@ -260,8 +274,10 @@ send(Socket, Packet) -> setopts(Socket, Opts) -> prim_inet:setopts(Socket, Opts). --spec getopts(Socket :: socket(), Opts :: [socket_getopt()]) -> - {'ok', [socket_setopt()]} | {'error', posix()}. +-spec getopts(Socket, Options) -> + {'ok', [socket_setopt()]} | {'error', posix()} when + Socket :: socket(), + Options :: [socket_getopt()]. getopts(Socket, Opts) -> prim_inet:getopts(Socket, Opts). @@ -272,7 +288,19 @@ getopts(Socket, Opts) -> getifaddrs(Socket) -> prim_inet:getifaddrs(Socket). --spec getifaddrs() -> {'ok', [string()]} | {'error', posix()}. +-spec getifaddrs() -> {ok, Iflist} | {error, posix()} when + Iflist :: [{Ifname,[Ifopt]}], + Ifname :: string(), + Ifopt :: {flag,[Flag]} | {addr,Addr} | {netmask,Netmask} + | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} + | {hwaddr,Hwaddr}, + Flag :: up | broadcast | loopback | pointtopoint + | running | multicast, + Addr :: ip_address(), + Netmask :: ip_address(), + Broadaddr :: ip_address(), + Dstaddr :: ip_address(), + Hwaddr :: [byte()]. getifaddrs() -> withsocket(fun(S) -> prim_inet:getifaddrs(S) end). @@ -371,7 +399,8 @@ popf(_Socket) -> % use of the DHCP-protocol % should never fail --spec gethostname() -> {'ok', string()}. +-spec gethostname() -> {'ok', Hostname} when + Hostname :: string(). gethostname() -> case inet_udp:open(0,[]) of @@ -402,19 +431,23 @@ getstat(Socket) -> getstat(Socket,What) -> prim_inet:getstat(Socket, What). --spec gethostbyname(Name :: string() | atom()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyname(Hostname) -> {ok, Hostent} | {error, posix()} when + Hostname :: hostname(), + Hostent :: hostent(). gethostbyname(Name) -> gethostbyname_tm(Name, inet, false). --spec gethostbyname(Name :: string() | atom(), Family :: family_option()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyname(Hostname, Family) -> + {ok, Hostent} | {error, posix()} when + Hostname :: hostname(), + Family :: family_option(), + Hostent :: hostent(). gethostbyname(Name,Family) -> gethostbyname_tm(Name, Family, false). --spec gethostbyname(Name :: string() | atom(), +-spec gethostbyname(Name :: hostname(), Family :: family_option(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', #hostent{}} | {'error', posix()}. @@ -439,8 +472,9 @@ gethostbyname_tm(Name,Family,Timer) -> gethostbyname_tm(Name, Family, Timer, Opts). --spec gethostbyaddr(Address :: string() | ip_address()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyaddr(Address) -> {ok, Hostent} | {error, posix()} when + Address :: string() | ip_address(), + Hostent :: hostent(). gethostbyaddr(Address) -> gethostbyaddr_tm(Address, false). @@ -491,14 +525,15 @@ getfd(Socket) -> %% Lookup an ip address %% --spec getaddr(Host :: ip_address() | string() | atom(), - Family :: family_option()) -> - {'ok', ip_address()} | {'error', posix()}. +-spec getaddr(Host, Family) -> {ok, Address} | {error, posix()} when + Host :: ip_address() | hostname(), + Family :: family_option(), + Address :: ip_address(). getaddr(Address, Family) -> getaddr(Address, Family, infinity). --spec getaddr(Host :: ip_address() | string() | atom(), +-spec getaddr(Host :: ip_address() | hostname(), Family :: family_option(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', ip_address()} | {'error', posix()}. @@ -515,9 +550,11 @@ getaddr_tm(Address, Family, Timer) -> Error -> Error end. --spec getaddrs(Host :: ip_address() | string() | atom(), - Family :: family_option()) -> - {'ok', [ip_address()]} | {'error', posix()}. +-spec getaddrs(Host, Family) -> + {ok, Addresses} | {error, posix()} when + Host :: ip_address() | hostname(), + Family :: family_option(), + Addresses :: [ip_address()]. getaddrs(Address, Family) -> getaddrs(Address, Family, infinity). @@ -1237,7 +1274,8 @@ port_list(Name) -> %% utils %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec format_error(posix()) -> string(). +-spec format_error(Posix) -> string() when + Posix :: posix(). format_error(exbadport) -> "invalid port state"; format_error(exbadseq) -> "bad command sequence"; diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 93563c6011..d5a8a2f134 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -47,18 +47,93 @@ false -> ok end). +-type res_option() :: + {alt_nameservers, [nameserver()]} + | {edns, 0 | false} + | {inet6, boolean()} + | {nameservers, [nameserver()]} + | {recurse, boolean()} + | {retry, integer()} + | {timeout, integer()} + | {udp_payload_size, integer()} + | {usevc, boolean()}. + +-type nameserver() :: {inet:ip_address(), Port :: 1..65535}. + +-type res_error() :: formerr | qfmterror | servfail | nxdomain | + notimp | refused | badvers | timeout. + +-type dns_name() :: string(). + +-type rr_type() :: a | aaaa | cname | gid | hinfo | ns | mb | md | mg | mf + | minfo | mx | naptr | null | ptr | soa | spf | srv | txt + | uid | uinfo | unspec | wks. + +-type dns_class() :: in | chaos | hs | any. + +-opaque dns_msg() :: term(). + +-type dns_data() :: + dns_name() + | inet:ip4_address() + | inet:ip6_address() + | {MName :: dns_name(), + RName :: dns_name(), + Serial :: integer(), + Refresh :: integer(), + Retry :: integer(), + Expiry :: integer(), + Minimum :: integer()} + | {inet:ip4_address(), Proto :: integer(), BitMap :: binary()} + | {CpuString :: string(), OsString :: string()} + | {RM :: dns_name(), EM :: dns_name()} + | {Prio :: integer(), dns_name()} + | {Prio :: integer(),Weight :: integer(),Port :: integer(),dns_name()} + | {Order :: integer(),Preference :: integer(),Flags :: string(), + Services :: string(),Regexp :: string(), dns_name()} + | [string()] + | binary(). + %% -------------------------------------------------------------------------- %% resolve: %% %% Nameserver query %% +-spec resolve(Name, Class, Type) -> {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type) -> resolve(Name, Class, Type, [], infinity). +-spec resolve(Name, Class, Type, Opts) -> + {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [Opt], + Opt :: res_option() | verbose | atom(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type, Opts) -> resolve(Name, Class, Type, Opts, infinity). +-spec resolve(Name, Class, Type, Opts, Timeout) -> + {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [Opt], + Opt :: res_option() | verbose | atom(), + Timeout :: timeout(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type, Opts, Timeout) -> case nsdname(Name) of {ok, Nm} -> @@ -76,12 +151,30 @@ resolve(Name, Class, Type, Opts, Timeout) -> %% Convenience wrapper to resolve/3,4,5 that filters out all answer data %% fields of the class and type asked for. +-spec lookup(Name, Class, Type) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(). + lookup(Name, Class, Type) -> lookup(Name, Class, Type, []). +-spec lookup(Name, Class, Type, Opts) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [res_option() | verbose]. + lookup(Name, Class, Type, Opts) -> lookup(Name, Class, Type, Opts, infinity). +-spec lookup(Name, Class, Type, Opts, Timeout) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [res_option() | verbose], + Timeout :: timeout(). + lookup(Name, Class, Type, Opts, Timeout) -> lookup_filter(resolve(Name, Class, Type, Opts, Timeout), Class, Type). @@ -101,17 +194,55 @@ lookup_filter({error,_}, _, _) -> []. %% %% To be deprecated +-spec nslookup(Name, Class, Type) -> {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Reason :: inet:posix() | res_error(). + nslookup(Name, Class, Type) -> do_nslookup(Name, Class, Type, [], infinity). +-spec nslookup(Name, Class, Type, Timeout) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Timeout :: timeout(), + Reason :: inet:posix() | res_error(); + (Name, Class, Type, Nameservers) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Nameservers :: [nameserver()], + Reason :: inet:posix() | res_error(). + nslookup(Name, Class, Type, Timeout) when is_integer(Timeout), Timeout >= 0 -> do_nslookup(Name, Class, Type, [], Timeout); nslookup(Name, Class, Type, NSs) -> % For backwards compatibility nnslookup(Name, Class, Type, NSs). % with OTP R6B only +-spec nnslookup(Name, Class, Type, Nameservers) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Nameservers :: [nameserver()], + Reason :: inet:posix(). + nnslookup(Name, Class, Type, NSs) -> nnslookup(Name, Class, Type, NSs, infinity). +-spec nnslookup(Name, Class, Type, Nameservers, Timeout) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Timeout :: timeout(), + Nameservers :: [nameserver()], + Reason :: inet:posix(). + nnslookup(Name, Class, Type, NSs, Timeout) -> do_nslookup(Name, Class, Type, [{nameservers,NSs}], Timeout). @@ -192,8 +323,19 @@ make_options(Opts, [Name|Names]) -> %% %% -------------------------------------------------------------------------- +-spec gethostbyaddr(Address) -> {ok, Hostent} | {error, Reason} when + Address :: inet:ip_address(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyaddr(IP) -> gethostbyaddr_tm(IP,false). +-spec gethostbyaddr(Address, Timeout) -> {ok, Hostent} | {error, Reason} when + Address :: inet:ip_address(), + Timeout :: timeout(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyaddr(IP,Timeout) -> Timer = inet:start_timer(Timeout), Res = gethostbyaddr_tm(IP,Timer), @@ -249,6 +391,11 @@ res_gethostbyaddr(Addr, IP, Timer) -> %% Caches the answer. %% -------------------------------------------------------------------------- +-spec gethostbyname(Name) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name) -> case inet_db:res_option(inet6) of true -> @@ -257,9 +404,23 @@ gethostbyname(Name) -> gethostbyname_tm(Name, inet, false) end. +-spec gethostbyname(Name, Family) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Family :: inet:family_option(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name,Family) -> gethostbyname_tm(Name,Family,false). +-spec gethostbyname(Name, Family, Timeout) -> + {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Timeout :: timeout(), + Family :: inet:family_option(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name,Family,Timeout) -> Timer = inet:start_timer(Timeout), Res = gethostbyname_tm(Name,Family,Timer), @@ -298,14 +459,27 @@ gethostbyname_tm(_Name, _Family, _Timer) -> %% %% getbyname(domain_name(), Type) => {ok, hostent()} | {error, Reason} %% -%% where domain_name() is domain string or atom and Type is ?S_A, ?S_MX ... +%% where domain_name() is domain string and Type is ?S_A, ?S_MX ... %% %% Caches the answer. %% -------------------------------------------------------------------------- +-spec getbyname(Name, Type) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Type :: rr_type(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + getbyname(Name, Type) -> getbyname_tm(Name,Type,false). +-spec getbyname(Name, Type, Timeout) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Type :: rr_type(), + Timeout :: timeout(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + getbyname(Name, Type, Timeout) -> Timer = inet:start_timer(Timeout), Res = getbyname_tm(Name, Type, Timer), diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 9a4089ab19..60bd96f332 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,8 +39,10 @@ getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,udp). getaddr(Address) -> inet:getaddr(Address, inet). getaddr(Address,Timer) -> inet:getaddr_tm(Address, inet, Timer). +-spec open(_) -> {ok, inet:socket()} | {error, atom()}. open(Port) -> open(Port, []). +-spec open(_, _) -> {ok, inet:socket()} | {error, atom()}. open(Port, Opts) -> case inet:udp_options( [{port,Port}, {recbuf, ?RECBUF} | Opts], @@ -69,6 +71,8 @@ recv(S,Len) -> recv(S,Len,Time) -> prim_inet:recvfrom(S, Len, Time). +-spec close(inet:socket()) -> ok. + close(S) -> inet:udp_close(S). diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl index 737b1ecee9..9b2dac9544 100644 --- a/lib/kernel/src/net_adm.erl +++ b/lib/kernel/src/net_adm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,11 @@ %% Try to read .hosts.erlang file in %% 1. cwd , 2. $HOME 3. init:root_dir() --spec host_file() -> [atom()] | {'error',atom() | {integer(),atom(),_}}. +-spec host_file() -> Hosts | {error, Reason} when + Hosts :: [Host :: atom()], + %% Copied from file:path_consult/2: + Reason :: file:posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. host_file() -> Home = case init:get_argument(home) of @@ -50,7 +54,8 @@ host_file() -> %% Check whether a node is up or down %% side effect: set up a connection to Node if there not yet is one. --spec ping(atom()) -> 'pang' | 'pong'. +-spec ping(Node) -> pong | pang when + Node :: atom(). ping(Node) when is_atom(Node) -> case catch gen:call({net_kernel, Node}, @@ -63,7 +68,8 @@ ping(Node) when is_atom(Node) -> pang end. --spec localhost() -> string(). +-spec localhost() -> Name when + Name :: string(). localhost() -> {ok, Host} = inet:gethostname(), @@ -73,12 +79,20 @@ localhost() -> end. --spec names() -> {'ok', [{string(), integer()}]} | {'error', _}. +-spec names() -> {ok, [{Name, Port}]} | {error, Reason} when + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). names() -> names(localhost()). --spec names(atom() | string()) -> {'ok', [{string(), integer()}]} | {'error', _}. + +-spec names(Host) -> {ok, [{Name, Port}]} | {error, Reason} when + Host :: atom() | string(), + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). names(Hostname) -> case inet:gethostbyname(Hostname) of @@ -88,8 +102,9 @@ names(Hostname) -> Else end. --spec dns_hostname(atom() | string()) -> - {'ok', string()} | {'error', atom() | string()}. +-spec dns_hostname(Host) -> {ok, Name} | {error, Host} when + Host :: atom() | string(), + Name :: string(). dns_hostname(Hostname) -> case inet:gethostbyname(Hostname) of @@ -164,7 +179,8 @@ collect_new(Sofar, Nodelist) -> world() -> world(silent). --spec world(verbosity()) -> [node()]. +-spec world(Arg) -> [node()] when + Arg :: verbosity(). world(Verbose) -> case net_adm:host_file() of @@ -172,12 +188,15 @@ world(Verbose) -> Hosts -> expand_hosts(Hosts, Verbose) end. --spec world_list([atom()]) -> [node()]. +-spec world_list(Hosts) -> [node()] when + Hosts :: [atom()]. world_list(Hosts) when is_list(Hosts) -> expand_hosts(Hosts, silent). --spec world_list([atom()], verbosity()) -> [node()]. +-spec world_list(Hosts, Arg) -> [node()] when + Hosts :: [atom()], + Arg :: verbosity(). world_list(Hosts, Verbose) when is_list(Hosts) -> expand_hosts(Hosts, Verbose). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 5228d4fe01..9e3d730cee 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -145,8 +145,15 @@ %% Interface functions kernel_apply(M,F,A) -> request({apply,M,F,A}). + +-spec allow(Nodes) -> ok | error when + Nodes :: [node()]. allow(Nodes) -> request({allow, Nodes}). + longnames() -> request(longnames). + +-spec stop() -> ok | {error, Reason} when + Reason :: not_allowed | not_found. stop() -> erl_distribution:stop(). node_info(Node) -> get_node_info(Node). @@ -158,10 +165,28 @@ i(Node) -> print_info(Node). verbose(Level) when is_integer(Level) -> request({verbose, Level}). +-spec set_net_ticktime(NetTicktime, TransitionPeriod) -> Res when + NetTicktime :: pos_integer(), + TransitionPeriod :: non_neg_integer(), + Res :: unchanged + | change_initiated + | {ongoing_change_to, NewNetTicktime}, + NewNetTicktime :: pos_integer(). set_net_ticktime(T, TP) when is_integer(T), T > 0, is_integer(TP), TP >= 0 -> ticktime_res(request({new_ticktime, T*250, TP*1000})). + +-spec set_net_ticktime(NetTicktime) -> Res when + NetTicktime :: pos_integer(), + Res :: unchanged + | change_initiated + | {ongoing_change_to, NewNetTicktime}, + NewNetTicktime :: pos_integer(). set_net_ticktime(T) when is_integer(T) -> set_net_ticktime(T, ?DEFAULT_TRANSITION_PERIOD). + +-spec get_net_ticktime() -> Res when + Res :: NetTicktime | {ongoing_change_to, NetTicktime} | ignored, + NetTicktime :: pos_integer(). get_net_ticktime() -> ticktime_res(request(ticktime)). @@ -171,6 +196,9 @@ get_net_ticktime() -> %% flags (we may want to move it elsewhere later). In order to easily %% be backward compatible, errors are created here when process_flag() %% fails. +-spec monitor_nodes(Flag) -> ok | Error when + Flag :: boolean(), + Error :: error | {error, term()}. monitor_nodes(Flag) -> case catch process_flag(monitor_nodes, Flag) of true -> ok; @@ -178,6 +206,13 @@ monitor_nodes(Flag) -> _ -> mk_monitor_nodes_error(Flag, []) end. +-spec monitor_nodes(Flag, Options) -> ok | Error when + Flag :: boolean(), + Options :: [Option], + Option :: {node_type, NodeType} + | nodedown_reason, + NodeType :: visible | hidden | all, + Error :: error | {error, term()}. monitor_nodes(Flag, Opts) -> case catch process_flag({monitor_nodes, Opts}, Flag) of true -> ok; @@ -209,6 +244,8 @@ publish_on_node(Node) when is_atom(Node) -> update_publish_nodes(Ns) -> request({update_publish_nodes, Ns}). +-spec connect_node(Node) -> boolean() | ignored when + Node :: node(). %% explicit connects connect_node(Node) when is_atom(Node) -> request({connect, normal, Node}). diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index d1feae771d..f6769df585 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -24,7 +24,10 @@ -include("file.hrl"). --spec type() -> 'vxworks' | {'unix',atom()} | {'win32',atom()} | {'ose',atom()}. +-spec type() -> vxworks | {Osfamily, Osname} when + Osfamily :: unix | win32, + Osname :: atom(). + type() -> case erlang:system_info(os_type) of {vxworks, _} -> @@ -32,18 +35,27 @@ type() -> Else -> Else end. --spec version() -> string() | {non_neg_integer(),non_neg_integer(),non_neg_integer()}. +-spec version() -> VersionString | {Major, Minor, Release} when + VersionString :: string(), + Major :: non_neg_integer(), + Minor :: non_neg_integer(), + Release :: non_neg_integer(). version() -> erlang:system_info(os_version). --spec find_executable(string()) -> string() | 'false'. +-spec find_executable(Name) -> Filename | 'false' when + Name :: string(), + Filename :: string(). find_executable(Name) -> case os:getenv("PATH") of false -> find_executable(Name, []); Path -> find_executable(Name, Path) end. --spec find_executable(string(), string()) -> string() | 'false'. +-spec find_executable(Name, Path) -> Filename | 'false' when + Name :: string(), + Path :: string(), + Filename :: string(). find_executable(Name, Path) -> Extensions = extensions(), case filename:pathtype(Name) of @@ -147,7 +159,8 @@ extensions() -> end. %% Executes the given command in the default shell for the operating system. --spec cmd(atom() | string() | [string()]) -> string(). +-spec cmd(Command) -> string() when + Command :: atom() | io_lib:chars(). cmd(Cmd) -> validate(Cmd), case type() of diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index 956a900adc..0d5838716e 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -30,17 +30,19 @@ %%% Exported functions %%% --spec start_link() -> {'ok', pid()} | {'error', term()}. +-spec start_link() -> {'ok', pid()} | {'error', any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec start() -> {'ok', pid()} | {'error', term()}. +-spec start() -> {'ok', pid()} | {'error', any()}. start() -> ensure_started(). --spec create(term()) -> 'ok'. +-type name() :: any(). + +-spec create(Name :: name()) -> 'ok'. create(Name) -> ensure_started(), @@ -55,9 +57,7 @@ create(Name) -> ok end. --type name() :: term(). - --spec delete(name()) -> 'ok'. +-spec delete(Name :: name()) -> 'ok'. delete(Name) -> ensure_started(), @@ -67,7 +67,8 @@ delete(Name) -> end), ok. --spec join(name(), pid()) -> 'ok' | {'error', {'no_such_group', term()}}. +-spec join(Name, Pid :: pid()) -> 'ok' | {'error', {'no_such_group', Name}} + when Name :: name(). join(Name, Pid) when is_pid(Pid) -> ensure_started(), @@ -83,7 +84,8 @@ join(Name, Pid) when is_pid(Pid) -> ok end. --spec leave(name(), pid()) -> 'ok' | {'error', {'no_such_group', name()}}. +-spec leave(Name, Pid :: pid()) -> 'ok' | {'error', {'no_such_group', Name}} + when Name :: name(). leave(Name, Pid) when is_pid(Pid) -> ensure_started(), @@ -99,10 +101,9 @@ leave(Name, Pid) when is_pid(Pid) -> ok end. --type get_members_ret() :: [pid()] | {'error', {'no_such_group', name()}}. +-spec get_members(Name) -> [pid()] | {'error', {'no_such_group', Name}} + when Name :: name(). --spec get_members(name()) -> get_members_ret(). - get_members(Name) -> ensure_started(), case ets:member(pg2_table, {group, Name}) of @@ -112,7 +113,8 @@ get_members(Name) -> {error, {no_such_group, Name}} end. --spec get_local_members(name()) -> get_members_ret(). +-spec get_local_members(Name) -> [pid()] | {'error', {'no_such_group', Name}} + when Name :: name(). get_local_members(Name) -> ensure_started(), @@ -123,15 +125,15 @@ get_local_members(Name) -> {error, {no_such_group, Name}} end. --spec which_groups() -> [name()]. +-spec which_groups() -> [Name :: name()]. which_groups() -> ensure_started(), all_groups(). --type gcp_error_reason() :: {'no_process', term()} | {'no_such_group', term()}. - --spec get_closest_pid(term()) -> pid() | {'error', gcp_error_reason()}. +-spec get_closest_pid(Name) -> pid() | {'error', Reason} when + Name :: name(), + Reason :: {'no_process', Name} | {'no_such_group', Name}. get_closest_pid(Name) -> case get_local_members(Name) of @@ -157,7 +159,9 @@ get_closest_pid(Name) -> -record(state, {}). --spec init([]) -> {'ok', #state{}}. +-opaque state() :: #state{}. + +-spec init(Arg :: []) -> {'ok', state()}. init([]) -> Ns = nodes(), @@ -169,13 +173,13 @@ init([]) -> pg2_table = ets:new(pg2_table, [ordered_set, protected, named_table]), {ok, #state{}}. --type call() :: {'create', name()} - | {'delete', name()} - | {'join', name(), pid()} - | {'leave', name(), pid()}. - --spec handle_call(call(), _, #state{}) -> - {'reply', 'ok', #state{}}. +-spec handle_call(Call :: {'create', Name} + | {'delete', Name} + | {'join', Name, Pid :: pid()} + | {'leave', Name, Pid :: pid()}, + From :: {pid(),Tag :: any()}, + State :: state()) -> {'reply', 'ok', state()} + when Name :: name(). handle_call({create, Name}, _From, S) -> assure_group(Name), @@ -195,11 +199,10 @@ handle_call(Request, From, S) -> [Request, From]), {noreply, S}. --type all_members() :: [[name(),...]]. --type cast() :: {'exchange', node(), all_members()} - | {'del_member', name(), pid()}. - --spec handle_cast(cast(), #state{}) -> {'noreply', #state{}}. +-spec handle_cast(Cast :: {'exchange', node(), Names :: [[Name,...]]} + | {'del_member', Name, Pid :: pid()}, + State :: state()) -> {'noreply', state()} + when Name :: name(). handle_cast({exchange, _Node, List}, S) -> store(List), @@ -208,7 +211,8 @@ handle_cast(_, S) -> %% Ignore {del_member, Name, Pid}. {noreply, S}. --spec handle_info(tuple(), #state{}) -> {'noreply', #state{}}. +-spec handle_info(Tuple :: tuple(), State :: state()) -> + {'noreply', state()}. handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S) -> member_died(MonitorRef), @@ -222,7 +226,7 @@ handle_info({new_pg2, Node}, S) -> handle_info(_, S) -> {noreply, S}. --spec terminate(term(), #state{}) -> 'ok'. +-spec terminate(Reason :: any(), State :: state()) -> 'ok'. terminate(_Reason, _S) -> true = ets:delete(pg2_table), diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index e09acb5024..be35f99ed2 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -263,14 +263,28 @@ proxy_user_flush() -> %% THE rpc client interface --spec call(node(), atom(), atom(), [term()]) -> term(). +-spec call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(). call(N,M,F,A) when node() =:= N -> %% Optimize local call local_call(M, F, A); call(N,M,F,A) -> do_call(N, {call,M,F,A,group_leader()}, infinity). --spec call(node(), atom(), atom(), [term()], timeout()) -> term(). +-spec call(Node, Module, Function, Args, Timeout) -> + Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(), + Timeout :: timeout(). call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call local_call(M,F,A); @@ -279,14 +293,28 @@ call(N,M,F,A,infinity) -> call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 -> do_call(N, {call,M,F,A,group_leader()}, Timeout). --spec block_call(node(), atom(), atom(), [term()]) -> term(). +-spec block_call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(). block_call(N,M,F,A) when node() =:= N -> %% Optimize local call local_call(M,F,A); block_call(N,M,F,A) -> do_call(N, {block_call,M,F,A,group_leader()}, infinity). --spec block_call(node(), atom(), atom(), [term()], timeout()) -> term(). +-spec block_call(Node, Module, Function, Args, Timeout) -> + Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(), + Timeout :: timeout(). block_call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call local_call(M, F, A); @@ -339,7 +367,13 @@ rpc_check(X) -> X. %% The entire call is packed into an atomic transaction which %% either succeeds or fails, i.e. never hangs (unless the server itself hangs). --spec server_call(node(), atom(), term(), term()) -> term() | {'error', 'nodedown'}. +-spec server_call(Node, Name, ReplyWrapper, Msg) -> Reply | {error, Reason} when + Node :: node(), + Name :: atom(), + ReplyWrapper :: term(), + Msg :: term(), + Reply :: term(), + Reason :: nodedown. server_call(Node, Name, ReplyWrapper, Msg) when is_atom(Node), is_atom(Name) -> @@ -362,7 +396,11 @@ server_call(Node, Name, ReplyWrapper, Msg) end end. --spec cast(node(), atom(), atom(), [term()]) -> 'true'. +-spec cast(Node, Module, Function, Args) -> true when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. cast(Node, Mod, Fun, Args) when Node =:= node() -> catch spawn(Mod, Fun, Args), @@ -373,12 +411,17 @@ cast(Node, Mod, Fun, Args) -> %% Asynchronous broadcast, returns nothing, it's just send'n prey --spec abcast(atom(), term()) -> 'abcast'. +-spec abcast(Name, Msg) -> abcast when + Name :: atom(), + Msg :: term(). abcast(Name, Mess) -> abcast([node() | nodes()], Name, Mess). --spec abcast([node()], atom(), term()) -> 'abcast'. +-spec abcast(Nodes, Name, Msg) -> abcast when + Nodes :: [node()], + Name :: atom(), + Msg :: term(). abcast([Node|Tail], Name, Mess) -> Dest = {Name,Node}, @@ -396,23 +439,39 @@ abcast([], _,_) -> abcast. %% message when we return from the call, we can't know that they have %% processed the message though. --spec sbcast(atom(), term()) -> {[node()], [node()]}. +-spec sbcast(Name, Msg) -> {GoodNodes, BadNodes} when + Name :: atom(), + Msg :: term(), + GoodNodes :: [node()], + BadNodes :: [node()]. sbcast(Name, Mess) -> sbcast([node() | nodes()], Name, Mess). --spec sbcast([node()], atom(), term()) -> {[node()], [node()]}. +-spec sbcast(Nodes, Name, Msg) -> {GoodNodes, BadNodes} when + Name :: atom(), + Msg :: term(), + Nodes :: [node()], + GoodNodes :: [node()], + BadNodes :: [node()]. sbcast(Nodes, Name, Mess) -> Monitors = send_nodes(Nodes, ?NAME, {sbcast, Name, Mess}, []), rec_nodes(?NAME, Monitors). --spec eval_everywhere(atom(), atom(), [term()]) -> 'abcast'. +-spec eval_everywhere(Module, Function, Args) -> abcast when + Module :: module(), + Function :: atom(), + Args :: [term()]. eval_everywhere(Mod, Fun, Args) -> eval_everywhere([node() | nodes()] , Mod, Fun, Args). --spec eval_everywhere([node()], atom(), atom(), [term()]) -> 'abcast'. +-spec eval_everywhere(Nodes, Module, Function, Args) -> abcast when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()]. eval_everywhere(Nodes, Mod, Fun, Args) -> gen_server:abcast(Nodes, ?NAME, {cast,Mod,Fun,Args,group_leader()}). @@ -453,20 +512,45 @@ unmonitor(Ref) when is_reference(Ref) -> %% Call apply(M,F,A) on all nodes in parallel --spec multicall(atom(), atom(), [term()]) -> {[_], [node()]}. +-spec multicall(Module, Function, Args) -> {ResL, BadNodes} when + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()], + BadNodes :: [node()]. multicall(M, F, A) -> multicall(M, F, A, infinity). --spec multicall([node()], atom(), atom(), [term()]) -> {[_], [node()]} - ; (atom(), atom(), [term()], timeout()) -> {[_], [node()]}. +-spec multicall(Nodes, Module, Function, Args) -> {ResL, BadNodes} when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()], + BadNodes :: [node()]; + (Module, Function, Args, Timeout) -> {ResL, BadNodes} when + Module :: module(), + Function :: atom(), + Args :: [term()], + Timeout :: timeout(), + ResL :: [term()], + BadNodes :: [node()]. multicall(Nodes, M, F, A) when is_list(Nodes) -> multicall(Nodes, M, F, A, infinity); multicall(M, F, A, Timeout) -> multicall([node() | nodes()], M, F, A, Timeout). --spec multicall([node()], atom(), atom(), [term()], timeout()) -> {[_], [node()]}. +-spec multicall(Nodes, Module, Function, Args, Timeout) -> + {ResL, BadNodes} when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()], + Timeout :: timeout(), + ResL :: [term()], + BadNodes :: [node()]. multicall(Nodes, M, F, A, infinity) when is_list(Nodes), is_atom(M), is_atom(F), is_list(A) -> @@ -495,12 +579,21 @@ do_multicall(Nodes, M, F, A, Timeout) -> %% %% There is no apparent order among the replies. --spec multi_server_call(atom(), term()) -> {[_], [node()]}. +-spec multi_server_call(Name, Msg) -> {Replies, BadNodes} when + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. multi_server_call(Name, Msg) -> multi_server_call([node() | nodes()], Name, Msg). --spec multi_server_call([node()], atom(), term()) -> {[_], [node()]}. +-spec multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} when + Nodes :: [node()], + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. multi_server_call(Nodes, Name, Msg) when is_list(Nodes), is_atom(Name) -> @@ -509,9 +602,22 @@ multi_server_call(Nodes, Name, Msg) %% Deprecated functions. Were only needed when communicating with R6 nodes. +-spec safe_multi_server_call(Name, Msg) -> {Replies, BadNodes} when + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. + safe_multi_server_call(Name, Msg) -> multi_server_call(Name, Msg). +-spec safe_multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} when + Nodes :: [node()], + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. + safe_multi_server_call(Nodes, Name, Msg) -> multi_server_call(Nodes, Name, Msg). @@ -539,7 +645,14 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) -> %% rpc's towards the same node. I.e. it returns immediately and %% it returns a Key that can be used in a subsequent yield(Key). --spec async_call(node(), atom(), atom(), [term()]) -> pid(). +-opaque key() :: pid(). + +-spec async_call(Node, Module, Function, Args) -> Key when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Key :: key(). async_call(Node, Mod, Fun, Args) -> ReplyTo = self(), @@ -549,20 +662,27 @@ async_call(Node, Mod, Fun, Args) -> ReplyTo ! {self(), {promise_reply, R}} %% self() is key end). --spec yield(pid()) -> term(). +-spec yield(Key) -> {value, Val} | timeout when + Key :: key(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. yield(Key) when is_pid(Key) -> {value,R} = do_yield(Key, infinity), R. --spec nb_yield(pid(), timeout()) -> {'value', _} | 'timeout'. +-spec nb_yield(Key, Timeout) -> {value, Val} | timeout when + Key :: key(), + Timeout :: timeout(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. nb_yield(Key, infinity=Inf) when is_pid(Key) -> do_yield(Key, Inf); nb_yield(Key, Timeout) when is_pid(Key), is_integer(Timeout), Timeout >= 0 -> do_yield(Key, Timeout). --spec nb_yield(pid()) -> {'value', _} | 'timeout'. +-spec nb_yield(Key) -> {value, Val} | timeout when + Key :: key(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. nb_yield(Key) when is_pid(Key) -> do_yield(Key, 0). @@ -582,7 +702,12 @@ do_yield(Key, Timeout) -> %% ArgL === [{M,F,Args},........] %% Returns a lists of the evaluations in the same order as %% given to ArgL --spec parallel_eval([{atom(), atom(), [_]}]) -> [_]. +-spec parallel_eval(FuncCalls) -> ResL when + FuncCalls :: [{Module, Function, Args}], + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()]. parallel_eval(ArgL) -> Nodes = [node() | nodes()], @@ -599,7 +724,13 @@ map_nodes([{M,F,A}|Tail],[Node|MoreNodes], Original) -> %% Parallel version of lists:map/3 with exactly the same %% arguments and return value as lists:map/3, %% except that it calls exit/1 if a network error occurs. --spec pmap({atom(),atom()}, [term()], [term()]) -> [term()]. +-spec pmap(FuncSpec, ExtraArgs, List1) -> List2 when + FuncSpec :: {Module,Function}, + Module :: module(), + Function :: atom(), + ExtraArgs :: [term()], + List1 :: [Elem :: term()], + List2 :: [term()]. pmap({M,F}, As, List) -> check(parallel_eval(build_args(M,F,As, List, [])), []). @@ -616,15 +747,20 @@ check([], Ack) -> Ack. %% location transparent version of process_info --spec pinfo(pid()) -> [{atom(), _}] | 'undefined'. +-spec pinfo(Pid) -> [{Item, Info}] | undefined when + Pid :: pid(), + Item :: atom(), + Info :: term(). pinfo(Pid) when node(Pid) =:= node() -> process_info(Pid); pinfo(Pid) -> call(node(Pid), erlang, process_info, [Pid]). --spec pinfo(pid(), Item) -> {Item, _} | 'undefined' | [] - when is_subtype(Item, atom()). +-spec pinfo(Pid, Item) -> {Item, Info} | undefined | [] when + Pid :: pid(), + Item :: atom(), + Info :: term(). pinfo(Pid, Item) when node(Pid) =:= node() -> process_info(Pid, Item); diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 78c3040f21..ea5da2de1c 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,15 +38,17 @@ -type flag() :: 'send' | 'receive' | 'print' | 'timestamp'. -type component() :: 'label' | 'serial' | flag(). --type value() :: non_neg_integer() - | {non_neg_integer(), non_neg_integer()} - | boolean(). --type token_pair() :: {component(), value()}. +-type value() :: (Integer :: non_neg_integer()) + | {Previous :: non_neg_integer(), + Current :: non_neg_integer()} + | (Bool :: boolean()). %%--------------------------------------------------------------------------- --type token() :: [] | {integer(), boolean(), _, _, _}. --spec set_token(token()) -> token() | 'ok'. +-opaque token() :: {integer(), boolean(), _, _, _}. +-spec set_token(Token) -> PreviousToken | 'ok' when + Token :: [] | token(), + PreviousToken :: [] | token(). set_token([]) -> erlang:seq_trace(sequential_trace_token,[]); @@ -58,28 +60,35 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> %% expects that, the BIF can however "unofficially" handle atoms as well, and %% atoms can be used if only Erlang nodes are involved --spec set_token(component(), value()) -> token_pair(). +-spec set_token(Component, Val) -> {Component, OldVal} when + Component :: component(), + Val :: value(), + OldVal :: value(). set_token(Type, Val) -> erlang:seq_trace(Type, Val). --spec get_token() -> term(). +-spec get_token() -> [] | token(). get_token() -> element(2,process_info(self(),sequential_trace_token)). --spec get_token(component()) -> token_pair(). - +-spec get_token(Component) -> {Component, Val} when + Component :: component(), + Val :: value(). get_token(Type) -> erlang:seq_trace_info(Type). --spec print(term()) -> 'ok'. +-spec print(TraceInfo) -> 'ok' when + TraceInfo :: term(). print(Term) -> erlang:seq_trace_print(Term), ok. --spec print(integer(), term()) -> 'ok'. +-spec print(Label, TraceInfo) -> 'ok' when + Label :: integer(), + TraceInfo :: term(). print(Label, Term) when is_atom(Label) -> erlang:error(badarg, [Label, Term]); @@ -94,14 +103,17 @@ reset_trace() -> %% reset_trace(Pid) -> % this might be a useful function too --type tracer() :: pid() | port() | 'false'. +-type tracer() :: (Pid :: pid()) | port() | 'false'. --spec set_system_tracer(tracer()) -> tracer(). +-spec set_system_tracer(Tracer) -> OldTracer when + Tracer :: tracer(), + OldTracer :: tracer(). set_system_tracer(Pid) -> erlang:system_flag(sequential_tracer, Pid). --spec get_system_tracer() -> tracer(). +-spec get_system_tracer() -> Tracer when + Tracer :: tracer(). get_system_tracer() -> element(2, erlang:system_info(sequential_tracer)). diff --git a/lib/kernel/src/wrap_log_reader.erl b/lib/kernel/src/wrap_log_reader.erl index fabaa07752..c41e0091e4 100644 --- a/lib/kernel/src/wrap_log_reader.erl +++ b/lib/kernel/src/wrap_log_reader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,6 +41,8 @@ first_no :: non_neg_integer() | 'one' % first read file number }). +-opaque continuation() :: #wrap_reader{}. + %% %% Exported functions %% @@ -50,9 +52,11 @@ %% is not yet reached, we are on the first 'round' of filling the wrap %% files. --type open_ret() :: {'ok', #wrap_reader{}} | {'error', tuple()}. +-type open_ret() :: {'ok', Continuation :: continuation()} + | {'error', Reason :: tuple()}. --spec open(atom() | string()) -> open_ret(). +-spec open(Filename) -> open_ret() when + Filename :: string() | atom(). open(File) when is_atom(File) -> open(atom_to_list(File)); @@ -77,7 +81,9 @@ open(File) when is_list(File) -> Error end. --spec open(atom() | string(), integer()) -> open_ret(). +-spec open(Filename, N) -> open_ret() when + Filename :: string() | atom(), + N :: integer(). open(File, FileNo) when is_atom(File), is_integer(FileNo) -> open(atom_to_list(File), FileNo); @@ -100,22 +106,29 @@ open(File, FileNo) when is_list(File), is_integer(FileNo) -> Error end. --spec close(#wrap_reader{}) -> 'ok' | {'error', atom()}. +-spec close(Continuation) -> 'ok' | {'error', Reason} when + Continuation :: continuation(), + Reason :: file:posix(). close(#wrap_reader{fd = FD}) -> file:close(FD). --type chunk_ret() :: {#wrap_reader{}, [term()]} - | {#wrap_reader{}, [term()], non_neg_integer()} - | {#wrap_reader{}, 'eof'} - | {'error', term()}. +-type chunk_ret() :: {Continuation2, Terms :: [term()]} + | {Continuation2, + Terms :: [term()], + Badbytes :: non_neg_integer()} + | {Continuation2, 'eof'} + | {'error', Reason :: term()}. --spec chunk(#wrap_reader{}) -> chunk_ret(). +-spec chunk(Continuation) -> chunk_ret() when + Continuation :: continuation(). chunk(WR = #wrap_reader{}) -> chunk(WR, ?MAX_CHUNK_SIZE, 0). --spec chunk(#wrap_reader{}, 'infinity' | pos_integer()) -> chunk_ret(). +-spec chunk(Continuation, N) -> chunk_ret() when + Continuation :: continuation(), + N :: infinity | pos_integer(). chunk(WR = #wrap_reader{}, infinity) -> chunk(WR, ?MAX_CHUNK_SIZE, 0); diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc index 7078499fbf..21174340d1 100644 --- a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc @@ -473,6 +473,13 @@ dets:close(N). </pre> <c>mnesia:table_info(Tab, master_nodes)</c> may be used to obtain information about the potential master nodes. </p> + <p>Determining which data to keep after communication failure is outside + the scope of Mnesia. One approach would be to determine which "island" + contains a majority of the nodes. Using the <c>{majority,true}</c> option + for critical tables can be a way of ensuring that nodes that are not part + of a "majority island" are not able to update those tables. Note that this + constitutes a reduction in service on the minority nodes. This would be + a tradeoff in favour of higher consistency guarantees.</p> <p>The function <c>mnesia:force_load_table(Tab)</c> may be used to force load the table regardless of which table load mechanism is activated. diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 16e78ea0af..2a2c7d3a9f 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -161,6 +161,14 @@ If a new item is inserted with the same key as </p> </item> <item> + <p><c>majority</c> This attribute can be either <c>true</c> or + <c>false</c> (default is <c>false</c>). When <c>true</c>, a majority + of the table replicas must be available for an update to succeed. + Majority checking can be enabled on tables with mission-critical data, + where it is vital to avoid inconsistencies due to network splits. + </p> + </item> + <item> <p><c>snmp</c> Each (set based) Mnesia table can be automatically turned into an SNMP ordered table as well. This property specifies the types of the SNMP keys. @@ -650,6 +658,17 @@ mnesia:change_table_copy_type(person, node(), disc_copies) </desc> </func> <func> + <name>change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the majority check setting for the table.</fsummary> + <desc> + <p><c>Majority</c> must be a boolean; the default is <c>false</c>. + When <c>true</c>, a majority of the table's replicas must be available + for an update to succeed. When used on fragmented tables, <c>Tab</c> + must be the name base table. Directly changing the majority setting on + individual fragments is not allowed.</p> + </desc> + </func> + <func> <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes all entries in a table.</fsummary> <desc> @@ -753,6 +772,14 @@ mnesia:change_table_copy_type(person, node(), disc_copies) priority will be loaded first at startup. </p> </item> + <item> + <p><c>{majority, Flag}</c>, where <c>Flag</c> must be a boolean. + If <c>true</c>, any (non-dirty) update to the table will abort unless + a majority of the table's replicas are available for the commit. + When used on a fragmented table, all fragments will be given + the same majority setting. + </p> + </item> <item> <p><c>{ram_copies, Nodelist}</c>, where <c>Nodelist</c> is a list of the nodes where this table @@ -1737,7 +1764,10 @@ mnesia:create_table(person, <c>write</c> and <c>sticky_write</c> are supported. </p> <p>If the user wants to update the record it is more efficient to - use <c>write/sticky_write</c> as the LockKind. + use <c>write/sticky_write</c> as the LockKind. If majority checking + is active on the table, it will be checked as soon as a write lock is + attempted. This can be used to quickly abort if the majority condition + isn't met. </p> </desc> </func> diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 19574a1434..f1c362261a 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 025b32f506..980a9c6213 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -39,6 +39,7 @@ %% Access within an activity - Lock acquisition lock/2, lock/4, + lock_table/2, read_lock_table/1, write_lock_table/1, @@ -92,7 +93,7 @@ add_table_copy/3, del_table_copy/2, move_table_copy/3, add_table_index/2, del_table_index/2, transform_table/3, transform_table/4, - change_table_copy_type/3, + change_table_copy_type/3, change_table_majority/2, read_table_property/2, write_table_property/2, delete_table_property/2, change_table_frag/2, clear_table/1, clear_table/4, @@ -415,6 +416,9 @@ lock(LockItem, LockKind) -> abort(no_transaction) end. +lock_table(Tab, LockKind) -> + lock({table, Tab}, LockKind). + lock(Tid, Ts, LockItem, LockKind) -> case element(1, Tid) of tid -> @@ -467,6 +471,8 @@ lock_table(Tid, Ts, Tab, LockKind) when is_atom(Tab) -> mnesia_locker:rlock_table(Tid, Store, Tab); write -> mnesia_locker:wlock_table(Tid, Store, Tab); + load -> + mnesia_locker:load_lock_table(Tid, Store, Tab); sticky_write -> mnesia_locker:sticky_wlock_table(Tid, Store, Tab); none -> @@ -2455,6 +2461,9 @@ change_table_access_mode(T, Access) -> change_table_load_order(T, O) -> mnesia_schema:change_table_load_order(T, O). +change_table_majority(T, M) -> + mnesia_schema:change_table_majority(T, M). + set_master_nodes(Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index d488d9364a..26537815a3 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -62,6 +62,7 @@ disc_only_copies = [], % [Node] load_order = 0, % Integer access_mode = read_write, % read_write | read_only + majority = false, % true | false index = [], % [Integer] snmp = [], % Snmp Ustruct local_content = false, % true | false diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 0254769758..d4b2c7b5cc 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -72,6 +72,7 @@ add_active_replica/4, update/1, change_table_access_mode/1, + change_table_majority/1, del_active_replica/2, wait_for_tables/2, get_network_copy/2, @@ -690,7 +691,8 @@ handle_call({update_where_to_write, [add, Tab, AddNode], _From}, _Dummy, State) case lists:member(AddNode, Current) and (State#state.schema_is_merged == true) of true -> - mnesia_lib:add_lsort({Tab, where_to_write}, AddNode); + mnesia_lib:add_lsort({Tab, where_to_write}, AddNode), + update_where_to_wlock(Tab); false -> ignore end, @@ -1690,6 +1692,8 @@ add_active_replica(Tab, Node, Storage, AccessMode) -> set(Var, mark_blocked_tab(Blocked, Del)), mnesia_lib:del({Tab, where_to_write}, Node) end, + + update_where_to_wlock(Tab), add({Tab, active_replicas}, Node). del_active_replica(Tab, Node) -> @@ -1699,7 +1703,8 @@ del_active_replica(Tab, Node) -> New = lists:sort(Del), set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit mnesia_lib:del({Tab, active_replicas}, Node), - mnesia_lib:del({Tab, where_to_write}, Node). + mnesia_lib:del({Tab, where_to_write}, Node), + update_where_to_wlock(Tab). change_table_access_mode(Cs) -> W = fun() -> @@ -1708,7 +1713,22 @@ change_table_access_mode(Cs) -> val({Tab, active_replicas})) end, update(W). - + +change_table_majority(Cs) -> + W = fun() -> + Tab = Cs#cstruct.name, + set({Tab, majority}, Cs#cstruct.majority), + update_where_to_wlock(Tab) + end, + update(W). + +update_where_to_wlock(Tab) -> + WNodes = val({Tab, where_to_write}), + Majority = case catch val({Tab, majority}) of + true -> true; + _ -> false + end, + set({Tab, where_to_wlock}, {WNodes, Majority}). %% node To now has tab loaded, but this must be undone %% This code is rpc:call'ed from the tab_copier process diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index 644133cf5d..92fd9dfade 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -871,7 +871,11 @@ insert_op(Tid, _, {op, add_index, Pos, TabDef}, InPlace, InitBy) -> startup -> ignore; _ -> - mnesia_index:init_indecies(Tab, Storage, [Pos]) + case val({Tab,where_to_read}) of + nowhere -> ignore; + _ -> + mnesia_index:init_indecies(Tab, Storage, [Pos]) + end end; insert_op(Tid, _, {op, del_index, Pos, TabDef}, InPlace, InitBy) -> @@ -896,6 +900,14 @@ insert_op(Tid, _, {op, change_table_access_mode,TabDef, _OldAccess, _Access}, In end, insert_cstruct(Tid, Cs, true, InPlace, InitBy); +insert_op(Tid, _, {op, change_table_majority,TabDef, _OldAccess, _Access}, InPlace, InitBy) -> + Cs = mnesia_schema:list2cs(TabDef), + case InitBy of + startup -> ignore; + _ -> mnesia_controller:change_table_majority(Cs) + end, + insert_cstruct(Tid, Cs, true, InPlace, InitBy); + insert_op(Tid, _, {op, change_table_load_order, TabDef, _OldLevel, _Level}, InPlace, InitBy) -> Cs = mnesia_schema:list2cs(TabDef), insert_cstruct(Tid, Cs, true, InPlace, InitBy); diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index 36bcfe8de9..7e926a6258 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -96,6 +96,8 @@ exists/1, fatal/2, get_node_number/0, + have_majority/2, + have_majority/3, fix_error/1, important/2, incr_counter/1, @@ -660,6 +662,14 @@ proc_info(_) -> false. get_node_number() -> {node(), self()}. +have_majority(Tab, HaveNodes) -> + have_majority(Tab, val({Tab, all_nodes}), HaveNodes). + +have_majority(_Tab, AllNodes, HaveNodes) -> + Missing = AllNodes -- HaveNodes, + Present = AllNodes -- Missing, + length(Present) > length(Missing). + read_log_files() -> [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()]. diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 3de329503e..e785b795d1 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -702,7 +702,7 @@ send_table(Pid, Tab, RemoteS) -> prepare_copy(Pid, Tab, Storage) -> Trans = fun() -> - mnesia:write_lock_table(Tab), + mnesia:lock_table(Tab, load), mnesia_subscr:subscribe(Pid, {table, Tab}), update_where_to_write(Tab, node(Pid)), mnesia_lib:db_fixtable(Storage, Tab, true), diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index ca0cc79c45..0492d794f3 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -40,7 +40,8 @@ sticky_wlock_table/3, wlock/3, wlock_no_exist/4, - wlock_table/3 + wlock_table/3, + load_lock_table/3 ]). %% sys callback functions @@ -656,16 +657,17 @@ rwlock(Tid, Store, Oid) -> Lock = write, case need_lock(Store, Tab, Key, Lock) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + check_majority(Majority, Tab, Ns), Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid), ?ets_insert(Store, {{locks, Tab, Key}, Lock}), Res; no -> if Key == ?ALL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); Tab == ?GLOBAL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); true -> dirty_rpc(Node, Tab, Key, Lock) end @@ -677,12 +679,34 @@ rwlock(Tid, Store, Oid) -> %% in the local store under the key == nodes w_nodes(Tab) -> - Nodes = ?catch_val({Tab, where_to_write}), - case Nodes of - [_ | _] -> Nodes; + case ?catch_val({Tab, where_to_wlock}) of + {[_ | _], _} = Where -> Where; _ -> mnesia:abort({no_exists, Tab}) end. +%% If the table has the 'majority' flag set, we can +%% only take a write lock if we see a majority of the +%% nodes. + + +check_majority(true, Tab, HaveNs) -> + check_majority(Tab, HaveNs); +check_majority(false, _, _) -> + ok. + +check_majority(Tab, HaveNs) -> + case ?catch_val({Tab, majority}) of + true -> + case mnesia_lib:have_majority(Tab, HaveNs) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; + _ -> + ok + end. + %% aquire a sticky wlock, a sticky lock is a lock %% which remains at this node after the termination of the %% transaction. @@ -708,12 +732,14 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> + {WNodes, Majority} = w_nodes(Tab), + sticky_check_majority(Lock, Tab, Majority, WNodes), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive {?MODULE, N, granted} -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], granted; {?MODULE, N, {granted, Val}} -> %% for rwlocks case opt_lookup_in_client(Val, Oid, write) of @@ -721,7 +747,7 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> exit({aborted, C}); Val2 -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], Val2 end; {?MODULE, N, {not_granted, Reason}} -> @@ -737,6 +763,16 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. +sticky_check_majority(W, Tab, true, WNodes) when W==write; W==read_write -> + case mnesia_lib:have_majority(Tab, WNodes) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; +sticky_check_majority(_, _, _, _) -> + ok. + not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) -> rlock(Tid, Store, {Tab, ?ALL}), %% needed? wlock(Tid, Store, Oid), %% perfect sync @@ -773,22 +809,33 @@ sticky_wlock_table(Tid, Store, Tab) -> %% local store when we have aquired the lock. %% wlock(Tid, Store, Oid) -> + wlock(Tid, Store, Oid, _CheckMajority = true). + +wlock(Tid, Store, Oid, CheckMajority) -> {Tab, Key} = Oid, case need_lock(Store, Tab, Key, write) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + if CheckMajority -> + check_majority(Majority, Tab, Ns); + true -> + ignore + end, Op = {self(), {write, Tid, Oid}}, ?ets_insert(Store, {{locks, Tab, Key}, write}), get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid); no when Key /= ?ALL, Tab /= ?GLOBAL -> []; no -> - w_nodes(Tab) + element(2, w_nodes(Tab)) end. wlock_table(Tid, Store, Tab) -> wlock(Tid, Store, {Tab, ?ALL}). +load_lock_table(Tid, Store, Tab) -> + wlock(Tid, Store, {Tab, ?ALL}, _CheckMajority = false). + %% Write lock even if the table does not exist wlock_no_exist(Tid, Store, Tab, Ns) -> diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index d1d892a387..fef72ad39c 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -37,6 +37,7 @@ change_table_copy_type/3, change_table_access_mode/2, change_table_load_order/2, + change_table_majority/2, change_table_frag/2, clear_table/1, create_table/1, @@ -178,6 +179,8 @@ do_set_schema(Tab, Cs) -> set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies), set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), + set({Tab, majority}, Cs#cstruct.majority), + set({Tab, all_nodes}, mnesia_lib:cs_to_nodes(Cs)), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], @@ -651,6 +654,7 @@ list2cs(List) when is_list(List) -> Snmp = pick(Name, snmp, List, []), LoadOrder = pick(Name, load_order, List, 0), AccessMode = pick(Name, access_mode, List, read_write), + Majority = pick(Name, majority, List, false), UserProps = pick(Name, user_properties, List, []), verify({alt, [nil, list]}, mnesia_lib:etype(UserProps), {bad_type, Name, {user_properties, UserProps}}), @@ -676,6 +680,7 @@ list2cs(List) when is_list(List) -> snmp = Snmp, load_order = LoadOrder, access_mode = AccessMode, + majority = Majority, local_content = LC, record_name = RecName, attributes = Attrs, @@ -809,7 +814,16 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> Access = Cs#cstruct.access_mode, verify({alt, [read_write, read_only]}, Access, {bad_type, Tab, {access_mode, Access}}), - + Majority = Cs#cstruct.majority, + verify({alt, [true, false]}, Majority, + {bad_type, Tab, {majority, Majority}}), + case Majority of + true -> + verify(false, LC, + {combine_error, Tab, [{local_content,true},{majority,true}]}); + false -> + ok + end, Snmp = Cs#cstruct.snmp, verify(true, mnesia_snmp_hook:check_ustruct(Snmp), {badarg, Tab, {snmp, Snmp}}), @@ -1495,6 +1509,43 @@ make_change_table_load_order(Tab, LoadOrder) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +change_table_majority(Tab, Majority) when is_boolean(Majority) -> + schema_transaction(fun() -> do_change_table_majority(Tab, Majority) end). + +do_change_table_majority(schema, _Majority) -> + mnesia:abort({bad_type, schema}); +do_change_table_majority(Tab, Majority) -> + TidTs = get_tid_ts_and_lock(schema, write), + get_tid_ts_and_lock(Tab, none), + insert_schema_ops(TidTs, make_change_table_majority(Tab, Majority)). + +make_change_table_majority(Tab, Majority) -> + ensure_writable(schema), + Cs = incr_version(val({Tab, cstruct})), + ensure_active(Cs), + OldMajority = Cs#cstruct.majority, + Cs2 = Cs#cstruct{majority = Majority}, + FragOps = case lists:keyfind(base_table, 1, Cs#cstruct.frag_properties) of + {_, Tab} -> + FragNames = mnesia_frag:frag_names(Tab) -- [Tab], + lists:map( + fun(T) -> + get_tid_ts_and_lock(Tab, none), + CsT = incr_version(val({T, cstruct})), + ensure_active(CsT), + CsT2 = CsT#cstruct{majority = Majority}, + verify_cstruct(CsT2), + {op, change_table_majority, cs2list(CsT2), + OldMajority, Majority} + end, FragNames); + false -> []; + {_, _} -> mnesia:abort({bad_type, Tab}) + end, + verify_cstruct(Cs2), + [{op, change_table_majority, cs2list(Cs2), OldMajority, Majority} | FragOps]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + write_table_property(Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 -> schema_transaction(fun() -> do_write_table_property(Tab, Prop) end); write_table_property(Tab, Prop) -> @@ -1734,7 +1785,10 @@ prepare_op(_Tid, {op, announce_im_running, Node, SchemaDef, Running, RemoteRunni Node == node() -> %% Announce has already run on local node ignore; %% from do_merge_schema true -> - NewNodes = mnesia_lib:uniq(Running++RemoteRunning) -- val({current,db_nodes}), + %% If a node has restarted it may still linger in db_nodes, + %% but have been removed from recover_nodes + Current = mnesia_lib:intersect(val({current,db_nodes}), [node()|val(recover_nodes)]), + NewNodes = mnesia_lib:uniq(Running++RemoteRunning) -- Current, mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}), announce_im_running(NewNodes, SchemaCs) end, @@ -2971,6 +3025,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) -> Cs#cstruct.index == RemoteCs#cstruct.index, Cs#cstruct.snmp == RemoteCs#cstruct.snmp, Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode, + Cs#cstruct.majority == RemoteCs#cstruct.majority, Cs#cstruct.load_order == RemoteCs#cstruct.load_order, Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties -> do_merge_versions(AnythingNew, Cs, RemoteCs); diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index bb8e788b40..f62f7cb7c8 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -64,7 +64,8 @@ prev_tab = [], % initiate to a non valid table name prev_types, prev_snmp, - types + types, + majority = [] }). -record(participant, {tid, pid, commit, disc_nodes = [], @@ -1100,9 +1101,12 @@ t_commit(Type) -> case arrange(Tid, Store, Type) of {N, Prep} when N > 0 -> multi_commit(Prep#prep.protocol, + majority_attr(Prep), Tid, Prep#prep.records, Store); {0, Prep} -> - multi_commit(read_only, Tid, Prep#prep.records, Store) + multi_commit(read_only, + majority_attr(Prep), + Tid, Prep#prep.records, Store) end; true -> %% nested commit @@ -1117,6 +1121,10 @@ t_commit(Type) -> do_commit_nested end. +majority_attr(#prep{majority = M}) -> + M. + + %% This function arranges for all objects we shall write in S to be %% in a list of {Node, CommitRecord} %% Important function for the performance of mnesia. @@ -1222,11 +1230,13 @@ prepare_items(Tid, Tab, Key, Items, Prep) -> {blocked, _} -> unblocked = req({unblock_me, Tab}), prepare_items(Tid, Tab, Key, Items, Prep); - _ -> + _ -> + Majority = needs_majority(Tab, Prep), Snmp = val({Tab, snmp}), Recs2 = do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Prep#prep.records), Prep2 = Prep#prep{records = Recs2, prev_tab = Tab, + majority = Majority, prev_types = Types, prev_snmp = Snmp}, check_prep(Prep2, Types) end. @@ -1235,6 +1245,33 @@ do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs) -> Recs2 = prepare_snmp(Tid, Tab, Key, Types, Snmp, Items, Recs), % May exit prepare_nodes(Tid, Types, Items, Recs2, normal). + +needs_majority(Tab, #prep{majority = M}) -> + case lists:keymember(Tab, 1, M) of + true -> + M; + false -> + case ?catch_val({Tab, majority}) of + {'EXIT', _} -> + M; + false -> + M; + true -> + CopyHolders = val({Tab, all_nodes}), + [{Tab, CopyHolders} | M] + end + end. + +have_majority([], _) -> + ok; +have_majority([{Tab, AllNodes} | Rest], Nodes) -> + case mnesia_lib:have_majority(Tab, AllNodes, Nodes) of + true -> + have_majority(Rest, Nodes); + false -> + {error, Tab} + end. + prepare_snmp(Tab, Key, Items) -> case val({Tab, snmp}) of [] -> @@ -1261,10 +1298,15 @@ prepare_snmp(Tid, Tab, Key, Types, Us, Items, Recs) -> prepare_nodes(Tid, Types, [{clear_table, Tab}], Recs, snmp) end. -check_prep(Prep, Types) when Prep#prep.types == Types -> +check_prep(#prep{majority = [], types = Types} = Prep, Types) -> Prep; -check_prep(Prep, Types) when Prep#prep.types == undefined -> - Prep#prep{types = Types}; +check_prep(#prep{majority = M, types = undefined} = Prep, Types) -> + Protocol = if M == [] -> + Prep#prep.protocol; + true -> + asym_trans + end, + Prep#prep{protocol = Protocol, types = Types}; check_prep(Prep, _Types) -> Prep#prep{protocol = asym_trans}. @@ -1311,7 +1353,7 @@ prepare_node(_Node, _Storage, [], Rec, _Kind) -> %% multi_commit((Protocol, Tid, CommitRecords, Store) %% Local work is always performed in users process -multi_commit(read_only, Tid, CR, _Store) -> +multi_commit(read_only, _Maj = [], Tid, CR, _Store) -> %% This featherweight commit protocol is used when no %% updates has been performed in the transaction. @@ -1324,7 +1366,7 @@ multi_commit(read_only, Tid, CR, _Store) -> ?MODULE ! {delete_transaction, Tid}, do_commit; -multi_commit(sym_trans, Tid, CR, Store) -> +multi_commit(sym_trans, _Maj = [], Tid, CR, Store) -> %% This lightweight commit protocol is used when all %% the involved tables are replicated symetrically. %% Their storage types must match on each node. @@ -1376,7 +1418,7 @@ multi_commit(sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(sync_sym_trans, Tid, CR, Store) -> +multi_commit(sync_sym_trans, _Maj = [], Tid, CR, Store) -> %% This protocol is the same as sym_trans except that it %% uses syncronized calls to disk_log and syncronized commits %% when several nodes are involved. @@ -1408,7 +1450,7 @@ multi_commit(sync_sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(asym_trans, Tid, CR, Store) -> +multi_commit(asym_trans, Majority, Tid, CR, Store) -> %% This more expensive commit protocol is used when %% table definitions are changed (schema transactions). %% It is also used when the involved tables are @@ -1469,6 +1511,10 @@ multi_commit(asym_trans, Tid, CR, Store) -> {D2, CR2} = commit_decision(D, CR, [], []), DiscNs = D2#decision.disc_nodes, RamNs = D2#decision.ram_nodes, + case have_majority(Majority, DiscNs ++ RamNs) of + ok -> ok; + {error, Tab} -> mnesia:abort({no_majority, Tab}) + end, Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), ?ets_insert(Store, Pending), {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs), diff --git a/lib/mnesia/test/.gitignore b/lib/mnesia/test/.gitignore new file mode 100644 index 0000000000..1e9a9933ed --- /dev/null +++ b/lib/mnesia/test/.gitignore @@ -0,0 +1,9 @@ + + +# Test generates +MnesiaCore* +Mnesia.* + +tempfile* +mnesia_test_case_info +test_log*
\ No newline at end of file diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index b165924ef2..ae4c9626c7 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -42,6 +42,7 @@ MODULES= \ mnesia_dirty_access_test \ mnesia_atomicity_test \ mnesia_consistency_test \ + mnesia_majority_test \ mnesia_isolation_test \ mnesia_durability_test \ mnesia_recovery_test \ diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl index 8ba8427213..dc8f216c1c 100644 --- a/lib/mnesia/test/mnesia_SUITE.erl +++ b/lib/mnesia/test/mnesia_SUITE.erl @@ -142,6 +142,105 @@ silly() -> mnesia_install_test:silly(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +light(doc) -> + ["The 'light' test suite runs a selected set of test suites and is", + "intended to be the smallest test suite that is meaningful", + "to run. It starts with an installation test (which in essence is the", + "'silly' test case) and then it covers all functions in the API in", + "various depths. All configuration parameters and examples are also", + "covered."]; +light(suite) -> + [ + install, + nice, + evil, + {mnesia_frag_test, light}, + qlc, + registry, + config, + examples + ]. + +install(suite) -> + [{mnesia_install_test, all}]. + +nice(suite) -> + [{mnesia_nice_coverage_test, all}]. + +evil(suite) -> + [{mnesia_evil_coverage_test, all}]. + +qlc(suite) -> + [{mnesia_qlc_test, all}]. + +registry(suite) -> + [{mnesia_registry_test, all}]. + +config(suite) -> + [{mnesia_config_test, all}]. + +examples(suite) -> + [{mnesia_examples_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +medium(doc) -> + ["The 'medium' test suite verfies the ACID (atomicity, consistency", + "isolation and durability) properties and various recovery scenarios", + "These tests may take quite while to run."]; +medium(suite) -> + [ + install, + atomicity, + isolation, + durability, + recovery, + consistency, + majority, + {mnesia_frag_test, medium} + ]. + +atomicity(suite) -> + [{mnesia_atomicity_test, all}]. + +isolation(suite) -> + [{mnesia_isolation_test, all}]. + +durability(suite) -> + [{mnesia_durability_test, all}]. + +recovery(suite) -> + [{mnesia_recovery_test, all}]. + +consistency(suite) -> + [{mnesia_consistency_test, all}]. + +majority(suite) -> + [{mnesia_majority_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +heavy(doc) -> + ["The 'heavy' test suite runs some resource consuming tests and", + "benchmarks"]; +heavy(suite) -> + [measure]. + +measure(suite) -> + [{mnesia_measure_test, all}]. + +prediction(suite) -> + [{mnesia_measure_test, prediction}]. + +fairness(suite) -> + [{mnesia_measure_test, fairness}]. + +benchmarks(suite) -> + [{mnesia_measure_test, benchmarks}]. + +consumption(suite) -> + [{mnesia_measure_test, consumption}]. + +scalability(suite) -> + [{mnesia_measure_test, scalability}]. clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where" "started during the tests"]; diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl index 55205d1222..2fee72f066 100644 --- a/lib/mnesia/test/mnesia_durability_test.erl +++ b/lib/mnesia/test/mnesia_durability_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -119,8 +119,8 @@ load_latest_data(Config) when is_list(Config) -> ?match([], mnesia_test_lib:kill_mnesia([N2])), ?match(ok, mnesia:dirty_write(Rec1)), - ?match([], mnesia_test_lib:kill_mnesia([N1])), ?match([], mnesia_test_lib:kill_mnesia([N3])), + ?match([], mnesia_test_lib:kill_mnesia([N1])), ?match([], mnesia_test_lib:start_mnesia([N2], [])), %% Should wait for N1 diff --git a/lib/mnesia/test/mnesia_majority_test.erl b/lib/mnesia/test/mnesia_majority_test.erl new file mode 100644 index 0000000000..17d1d8bcdd --- /dev/null +++ b/lib/mnesia/test/mnesia_majority_test.erl @@ -0,0 +1,188 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(mnesia_majority_test). +-author('[email protected]'). +-compile(export_all). +-include("mnesia_test_lib.hrl"). + +init_per_testcase(Func, Conf) -> + mnesia_test_lib:init_per_testcase(Func, Conf). + +fin_per_testcase(Func, Conf) -> + mnesia_test_lib:fin_per_testcase(Func, Conf). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all(doc) -> + ["Verify that majority checking works"]; +all(suite) -> + [ + write + , wread + , delete + , clear_table + , frag + , change_majority + , frag_change_majority + ]. + +write(suite) -> []; +write(Config) when is_list(Config) -> + [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2,N3]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok,ok],[]}, + rpc:multicall([N1,N2,N3], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N3]), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)). + +wread(suite) -> []; +wread(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)). + +delete(suite) -> []; +delete(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)). + +clear_table(suite) -> []; +clear_table(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, mnesia:clear_table(t)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, mnesia:clear_table(t)). + +frag(suite) -> []; +frag(Config) when is_list(Config) -> + [N1] = Nodes = ?acquire_nodes(1, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1]}, + {majority,true}, + {frag_properties, [{n_fragments, 2}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)). + +change_majority(suite) -> []; +change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)). + +frag_change_majority(suite) -> []; +frag_change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false}, + {frag_properties, + [{n_fragments, 2}, + {n_ram_copies, 2}, + {node_pool, [N1,N2]}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match(false, mnesia:table_info(t_frag2, majority)), + ?match({aborted,{bad_type,t_frag2}}, + mnesia:change_table_majority(t_frag2, true)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)). diff --git a/lib/mnesia/test/mnesia_qlc_test.erl b/lib/mnesia/test/mnesia_qlc_test.erl index 141de71d01..5f46840ae9 100644 --- a/lib/mnesia/test/mnesia_qlc_test.erl +++ b/lib/mnesia/test/mnesia_qlc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -70,6 +70,7 @@ init_testcases(Type,Config) -> end, All = fun() -> [Write(Id) || Id <- lists:seq(1,10)], ok end, ?match({atomic, ok}, mnesia:sync_transaction(All)), + ?match({atomic, [{b, {b,100-1}, 1}]}, mnesia:transaction(fun() -> mnesia:read({b, {b, 99}}) end)), Nodes. %% Test cases diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 182c240084..9da45975d5 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -413,32 +413,28 @@ test_driver([T|TestCases], Config) -> [L1|L2]; test_driver({Module, TestCases}, Config) when is_list(TestCases)-> test_driver(default_module(Module, TestCases), Config); -test_driver({_, {Module, TestCase}}, Config) -> - test_driver({Module, TestCase}, Config); +test_driver({Module, all}, Config) -> + get_suite(Module, all, Config); +test_driver({Module, G={group, _}}, Config) -> + get_suite(Module, G, Config); +test_driver({_, {group, Module, Group}}, Config) -> + get_suite(Module, {group, Group}, Config); + test_driver({Module, TestCase}, Config) -> Sec = timer:seconds(1) * 1000, - case get_suite(Module, TestCase) of - [] when Config == suite -> + case Config of + suite -> {Module, TestCase, 'IMPL'}; - [] -> + _ -> log("Eval test case: ~w~n", [{Module, TestCase}]), - {T, Res} = - timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]), - log("Tested ~w in ~w sec~n", [TestCase, T div Sec]), - {T div Sec, Res}; - Suite when is_list(Suite), Config == suite -> - Res = test_driver(default_module(Module, Suite), Config), - {{Module, TestCase}, Res}; - Suite when is_list(Suite) -> - log("Expand test case ~w~n", [{Module, TestCase}]), - Def = default_module(Module, Suite), - {T, Res} = timer:tc(?MODULE, test_driver, [Def, Config]), - {T div Sec, {{Module, TestCase}, Res}}; - 'NYI' when Config == suite -> - {Module, TestCase, 'NYI'}; - 'NYI' -> - log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), - {0, {skip, {Module, TestCase}, "NYI"}} + try timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]) of + {T, Res} -> + log("Tested ~w in ~w sec~n", [TestCase, T div Sec]), + {T div Sec, Res} + catch error:function_clause -> + log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), + {0, {skip, {Module, TestCase}, "NYI"}} + end end; test_driver(TestCase, Config) -> DefaultModule = mnesia_SUITE, @@ -449,18 +445,50 @@ test_driver(TestCase, Config) -> default_module(DefaultModule, TestCases) when is_list(TestCases) -> Fun = fun(T) -> case T of + {group, _} -> {true, {DefaultModule, T}}; {_, _} -> true; T -> {true, {DefaultModule, T}} end end, lists:zf(Fun, TestCases). +get_suite(Module, TestCase, Config) -> + case get_suite(Module, TestCase) of + Suite when is_list(Suite), Config == suite -> + Res = test_driver(default_module(Module, Suite), Config), + {{Module, TestCase}, Res}; + Suite when is_list(Suite) -> + log("Expand test case ~w~n", [{Module, TestCase}]), + Def = default_module(Module, Suite), + {T, Res} = timer:tc(?MODULE, test_driver, [Def, Config]), + Sec = timer:seconds(1) * 1000, + {T div Sec, {{Module, TestCase}, Res}}; + 'NYI' when Config == suite -> + {Module, TestCase, 'NYI'}; + 'NYI' -> + log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), + {0, {skip, {Module, TestCase}, "NYI"}} + end. + %% Returns a list (possibly empty) or the atom 'NYI' -get_suite(Mod, Fun) -> - case catch (apply(Mod, Fun, [suite])) of +get_suite(Mod, {group, Suite}) -> + try + Groups = Mod:groups(), + {_, _, TCList} = lists:keyfind(Suite, 1, Groups), + TCList + catch + _:Reason -> + io:format("Not implemented ~p ~p (~p ~p)~n", + [Mod,Suite,Reason, erlang:get_stacktrace()]), + 'NYI' + end; +get_suite(Mod, all) -> + case catch (apply(Mod, all, [])) of {'EXIT', _} -> 'NYI'; List when is_list(List) -> List - end. + end; +get_suite(_Mod, _Fun) -> + []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -503,9 +531,13 @@ wait_for_evaluator(Pid, Mod, Fun, Config) -> test_case_evaluator(Mod, Fun, [Config]) -> NewConfig = Mod:init_per_testcase(Fun, Config), - R = apply(Mod, Fun, [NewConfig]), - Mod:end_per_testcase(Fun, NewConfig), - exit({test_case_ok, R}). + try + R = apply(Mod, Fun, [NewConfig]), + Mod:end_per_testcase(Fun, NewConfig), + exit({test_case_ok, R}) + catch error:function_clause -> + exit({skipped, 'NYI'}) + end. activity_evaluator(Coordinator) -> activity_evaluator_loop(Coordinator), diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl index 55ba4dd761..ca3f0fbf49 100644 --- a/lib/mnesia/test/mnesia_trans_access_test.erl +++ b/lib/mnesia/test/mnesia_trans_access_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1102,9 +1102,9 @@ create_live_table_index_disc_only(Config) when is_list(Config) -> create_live_table_index(Config, disc_only_copies). create_live_table_index(Config, Storage) -> - [Node1] = Nodes = ?acquire_nodes(1, Config), + [N1,N2,N3] = Nodes = ?acquire_nodes(3, Config), Tab = create_live_table_index, - Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}], + Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, Nodes}], ?match({atomic, ok}, mnesia:create_table(Schema)), ValPos = 3, mnesia:dirty_write({Tab, 1, 2}), @@ -1115,9 +1115,35 @@ create_live_table_index(Config, Storage) -> end, ?match({atomic, ok}, mnesia:transaction(Fun)), ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), + IRead = fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) end, + ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)), + ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)), + + %% Bug when adding index when table is still unloaded + %% By setting load order we hopefully will trigger the bug + mnesia:change_table_copy_type(Tab, N2, ram_copies), + mnesia:change_table_copy_type(Tab, N3, ram_copies), + ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)), + ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)), + + Create = fun(N) -> + TabN = list_to_atom("tab_" ++ integer_to_list(N)), + Def = [{ram_copies, Nodes}, {load_order, N}], + mnesia:create_table(TabN, Def) + end, + + ?match([{atomic,ok}|_], [Create(N) || N <- lists:seq(1,50)]), + + ?match([], mnesia_test_lib:stop_mnesia([N2,N3])), + ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}]])), + ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}]])), + + ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), + + ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)), ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, - mnesia:transaction(fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) - end)), + rpc:call(N2, mnesia, transaction, [IRead])), + ?verify_mnesia(Nodes, []). %% Drop table index diff --git a/lib/mnesia/test/mt.erl b/lib/mnesia/test/mt.erl index f69c4a11fd..322bd52130 100644 --- a/lib/mnesia/test/mt.erl +++ b/lib/mnesia/test/mt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -76,17 +76,24 @@ resolve(Suite0) when is_atom(Suite0) -> Suite when is_atom(Suite) -> {Suite, all}; {Suite, Case} -> - {Suite, Case} + {Suite, is_group(Suite,Case)} end; resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) -> case alias(Suite0) of Suite when is_atom(Suite) -> - {Suite, Case}; + {Suite, is_group(Suite,Case)}; {Suite, Case2} -> - {Suite, Case2} + {Suite, is_group(Suite,Case2)} end; resolve(List) when is_list(List) -> [resolve(Case) || Case <- List]. + +is_group(Mod, Case) -> + try {_,_,_} = lists:keyfind(Case, 1, Mod:groups()), + {group, Case} + catch _:{badmatch,_} -> + Case + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Run one or more test cases diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml index b3b9937f1c..73eb992323 100644 --- a/lib/observer/doc/src/notes.xml +++ b/lib/observer/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml index b88c7cf1cd..e15e7dea7d 100644 --- a/lib/odbc/doc/src/notes.xml +++ b/lib/odbc/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index 14b43041ce..30326da114 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2010</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 9743289ca6..692baea0a4 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -465,16 +465,16 @@ analyse(#state{common = C, [MissingApp2 | Apps2] end, app_propagate_is_used_by(C, Apps3), - Apps4 = read_apps(C, Sys, Apps3, []), + {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), %% io:format("Missing app: ~p\n", %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), Sys2 = Sys#sys{apps = Apps4}, - case verify_config(RelApps2, Sys2, Status3) of - {ok, _Warnings} = Status4 -> - {S#state{sys = Sys2}, Status4}; - {error, _} = Status4 -> - {S, Status4} + case verify_config(RelApps2, Sys2, Status4) of + {ok, _Warnings} = Status5 -> + {S#state{sys = Sys2}, Status5}; + {error, _} = Status5 -> + {S, Status5} end. apps_in_rels(Rels, Apps, Status) -> @@ -548,21 +548,24 @@ app_init_is_included(C, {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, - A2 = A#app{is_pre_included = IsPreIncl, + {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> + mod_init_is_included(C, + Mod, + ModCond, + AppCond, + Default, + Acc) + end, + Status2, + Mods), + A2 = A#app{mods = Mods2, + is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, ets:insert(C#common.app_tab, A2), - lists:foreach(fun(Mod) -> - mod_init_is_included(C, - Mod, - ModCond, - AppCond, - Default) - end, - Mods), - {A2, Status2}. - -mod_init_is_included(C, M, ModCond, AppCond, Default) -> + {A2, Status3}. + +mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -595,9 +598,52 @@ mod_init_is_included(C, M, ModCond, AppCond, Default) -> Default end end, + M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, + + Status2 = + case ets:lookup(C#common.mod_tab,M#mod.name) of + [Existing] -> + case {Existing#mod.is_included,IsIncl} of + {false,_} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + M#mod.app_name, "."]), + ets:insert(C#common.mod_tab, M2), + reltool_utils:add_warning(Status,Warning); + {_,false} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + Existing#mod.app_name, "."]), + + %% Don't insert in mod_tab - using Existing + reltool_utils:add_warning(Status,Warning); + {_,_} -> + Error = + lists:concat( + ["Module ",M#mod.name, + " potentially included by ", + "two different applications: ", + Existing#mod.app_name, " and ", + M#mod.app_name, "."]), + %% Don't insert in mod_tab - using Existing + reltool_utils:return_first_error(Status,Error) + end; + [] -> + ets:insert(C#common.mod_tab, M2), + Status + end, + %% print(M#mod.name, hipe, "~p -> ~p\n", [M2, IsIncl]), - ets:insert(C#common.mod_tab, M2). + {M2,Status2}. false_to_undefined(Bool) -> case Bool of @@ -612,21 +658,27 @@ app_propagate_is_included(_C, _Sys, [], Acc) -> Acc. mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), Acc2 = - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, Acc); - false -> - Acc; - undefined -> - Acc - end, + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + %% print(ModName, file, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + case M2#mod.is_included of + true -> + %% Propagate include mark + mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); + false -> + Acc; + undefined -> + Acc + end; + [_] -> + %% This module is currently used from a different application + %% Ignore + Acc + end, mod_propagate_is_included(C, Sys, A, Mods, Acc2); mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> Acc. @@ -740,9 +792,10 @@ mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> mod_propagate_is_used_by(_C, []) -> ok. -read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> - {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - Status = +app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> + {Mods2, IsIncl2, Status2} = + mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), + AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; false -> ok @@ -759,34 +812,52 @@ read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, - status = Status, + status = AppStatus, uses_mods = UsesMods2, used_by_mods = UsedByMods2, uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - read_apps(C, Sys, Apps, [A2 | Acc]); -read_apps(_C, _Sys, [], Acc) -> - lists:reverse(Acc). - -read_apps(C, Sys, A, [#mod{name = ModName} | Mods], Acc, IsIncl) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - Status = do_get_status(M2), - %% print(M2#mod.name, hipe, "status -> ~p\n", [Status]), - {IsIncl2, M3} = - case M2#mod.is_included of - true -> - UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, - ModName)], - {true, M2#mod{status = Status, used_by_mods = UsedByMods}}; - _ -> - {IsIncl, M2#mod{status = Status, used_by_mods = []}} - end, - ets:insert(C#common.mod_tab, M3), - read_apps(C, Sys, A, Mods, [M3 | Acc], IsIncl2); -read_apps(_C, _Sys, _A, [], Acc, IsIncl) -> - {lists:reverse(Acc), IsIncl}. + ets:insert(C#common.app_tab,A2), + app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); +app_recap_dependencies(_C, _Sys, [], Acc, Status) -> + {lists:reverse(Acc), Status}. + +mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + ModStatus = do_get_status(M2), + %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), + {IsIncl2, M3} = + case M2#mod.is_included of + true -> + UsedByMods = + [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + ModName)], + {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; + _ -> + {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} + end, + ets:insert(C#common.mod_tab, M3), + mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> + %% App is explicitely excluded so it is ok that the module + %% record does not exist for this module in this + %% application. + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + [M2] -> + %% A module is potensially included by multiple + %% applications. This is not allowed! + Error = + lists:concat( + ["Module ",ModName, + " potentially included by two different applications: ", + A#app.name, " and ", M2#mod.app_name, "."]), + Status2 = reltool_utils:return_first_error(Status,Error), + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + end; +mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> + {lists:reverse(Acc), IsIncl, Status}. do_get_status(M) -> if diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index dbb8e32aa2..76c064f1e7 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1092,17 +1092,23 @@ move_app(S, {_ItemNo, AppBase}, Action) -> OldApp#app{incl_cond = AppCond}. do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> - {ok, AnalysedApp, Warnings} = reltool_server:set_app(ServerPid, NewApp), + Result = reltool_server:set_app(ServerPid, NewApp), [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], S2 = redraw_apps(S), - case Warnings of - [] -> - ignore; - _ -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING) - end, - {ok, AnalysedApp, S2}. + ReturnApp = + case Result of + {ok, AnalysedApp, []} -> + AnalysedApp; + {ok, AnalysedApp, Warnings} -> + Msg = lists:flatten([[W, $\n] || W <- Warnings]), + display_message(Msg, ?wxICON_WARNING), + AnalysedApp; + {error, Reason} -> + display_message(Reason, ?wxICON_ERROR), + {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), + OldApp + end, + {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, source = SourceCtrl, diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 62fe05238b..767454b66a 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -76,8 +76,8 @@ release_tests_spec: opt $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + chmod -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index b77560db94..9ed79e8c95 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). -define(WORK_DIR, "reltool_work_dir"). @@ -53,7 +54,7 @@ all() -> [start_server, set_config, create_release, create_script, create_target, create_embedded, create_standalone, create_old_target, - otp_9135]. + otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. groups() -> []. @@ -361,6 +362,114 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% OTP-9229 - handle duplicated module names, i.e. same module name +%% exists in two applications. + +%% Include on app, exclude the other +otp_9229_exclude_app(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclApp = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), + ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(false, filelib:is_file(AbsYArchive)), + ?m(non_existing, mod_path(Node,y)), + + ?msym(ok, stop_node(Node)), + + ok. + +%% Include both apps, but exclude common module from one app +otp_9229_exclude_mod(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclMod = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include},{mod, mylib,[{incl_cond,exclude}]}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_mod"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), + ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + YEbin = ["ebin","y-1.0",YArchive], + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(true, filelib:is_file(AbsYArchive)), + ?m(YEbin, mod_path(Node,y)), + + %% Remove path to XEbin and check that mylib is not located in YEbin + Mylib = rpc:call(Node,code,which,[mylib]), + rpc:call(Node,code,del_path,[filename:dirname(Mylib)]), + ?m(non_existing, mod_path(Node,mylib)), + + ?msym(ok, stop_node(Node)), + + ok. + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Library functions erl_libs() -> @@ -407,6 +516,20 @@ os_cmd(Cmd) when is_list(Cmd) -> end end. +%% Returns the location (directory) of the given module. Split, +%% reverted and relative to the lib dir. +mod_path(Node,Mod) -> + case rpc:call(Node,code,which,[Mod]) of + Path when is_list(Path) -> + lists:takewhile( + fun("lib") -> false; + (_) -> true + end, + lists:reverse(filename:split(filename:dirname(Path)))); + Other -> + Other + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Node handling diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src new file mode 100644 index 0000000000..049e8dd6cc --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -0,0 +1,19 @@ +EFLAGS=+debug_info + +OTP9229= \ + otp_9229/x-1.0/ebin/x.@EMULATOR@ \ + otp_9229/x-1.0/ebin/mylib.@EMULATOR@ \ + otp_9229/y-1.0/ebin/y.@EMULATOR@ \ + otp_9229/y-1.0/ebin/mylib.@EMULATOR@ + + +all: $(OTP9229) + +otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl +otp_9229/x-1.0/ebin/mylib.@EMULATOR@: otp_9229/x-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/mylib.erl +otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl +otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app new file mode 100644 index 0000000000..e597704b19 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, x, + [{description, "X CXC 138 11"}, + {vsn, "1.0"}, + {modules, [x, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl new file mode 100644 index 0000000000..17ff84f08f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl @@ -0,0 +1,4 @@ +-module(x). +-export([x/0]). + +x() ->mylib:foo(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app new file mode 100644 index 0000000000..5b327862e3 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, y, + [{description, "Y CXC 138 11"}, + {vsn, "1.0"}, + {modules, [y, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl new file mode 100644 index 0000000000..342e7da7d5 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl @@ -0,0 +1,4 @@ +-module(y). +-export([y/0]). + +y() ->mylib:foo(). diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml index b27a3a0996..599be62241 100644 --- a/lib/runtime_tools/doc/src/notes.xml +++ b/lib/runtime_tools/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile index 2affcf1e40..4073e5af85 100644 --- a/lib/sasl/Makefile +++ b/lib/sasl/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2010. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Macros # -SUB_DIRECTORIES = src doc/src +SUB_DIRECTORIES = src doc/src examples/src include vsn.mk VSN = $(SASL_VSN) diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml index 7941e371a0..73c4825458 100644 --- a/lib/sasl/doc/src/notes.xml +++ b/lib/sasl/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/sasl/examples/ebin/.gitignore b/lib/sasl/examples/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/sasl/examples/ebin/.gitignore diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile new file mode 100644 index 0000000000..4a4e04a536 --- /dev/null +++ b/lib/sasl/examples/src/Makefile @@ -0,0 +1,78 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2010. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(SASL_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/sasl-$(VSN) + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- +EXTRA_ERLC_FLAGS = +warn_unused_vars +ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS) + + +MODULES = target_system + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +clean: + rm -fr $(TARGET_FILES) *~ *.beam + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/src + $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin + (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) + chmod -f -R ug+w $(RELSYSDIR)/examples + +release_docs_spec: + + + + + + + diff --git a/lib/sasl/examples/src/target_system.erl b/lib/sasl/examples/src/target_system.erl new file mode 100644 index 0000000000..0e1e0b2324 --- /dev/null +++ b/lib/sasl/examples/src/target_system.erl @@ -0,0 +1,259 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(target_system). +-export([create/1, create/2, install/2]). + +%% Note: RelFileName below is the *stem* without trailing .rel, +%% .script etc. +%% + +%% create(RelFileName) +%% +create(RelFileName) -> + create(RelFileName,[]). + +create(RelFileName,SystoolsOpts) -> + RelFile = RelFileName ++ ".rel", + Dir = filename:dirname(RelFileName), + PlainRelFileName = filename:join(Dir,"plain"), + PlainRelFile = PlainRelFileName ++ ".rel", + io:fwrite("Reading file: ~p ...~n", [RelFile]), + {ok, [RelSpec]} = file:consult(RelFile), + io:fwrite("Creating file: ~p from ~p ...~n", + [PlainRelFile, RelFile]), + {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + AppVsns} = RelSpec, + PlainRelSpec = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + lists:filter(fun({kernel, _}) -> + true; + ({stdlib, _}) -> + true; + (_) -> + false + end, AppVsns) + }, + {ok, Fd} = file:open(PlainRelFile, [write]), + io:fwrite(Fd, "~p.~n", [PlainRelSpec]), + file:close(Fd), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [PlainRelFileName,PlainRelFileName]), + make_script(PlainRelFileName,SystoolsOpts), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [RelFileName, RelFileName]), + make_script(RelFileName,SystoolsOpts), + + TarFileName = filename:join(Dir,RelFileName ++ ".tar.gz"), + io:fwrite("Creating tar file ~p ...~n", [TarFileName]), + make_tar(RelFileName,SystoolsOpts), + + TmpDir = filename:join(Dir,"tmp"), + io:fwrite("Creating directory ~p ...~n",[TmpDir]), + file:make_dir(TmpDir), + + io:fwrite("Extracting ~p into directory ~p ...~n", [TarFileName,TmpDir]), + extract_tar(TarFileName, TmpDir), + + TmpBinDir = filename:join([TmpDir, "bin"]), + ErtsBinDir = filename:join([TmpDir, "erts-" ++ ErtsVsn, "bin"]), + io:fwrite("Deleting \"erl\" and \"start\" in directory ~p ...~n", + [ErtsBinDir]), + file:delete(filename:join([ErtsBinDir, "erl"])), + file:delete(filename:join([ErtsBinDir, "start"])), + + io:fwrite("Creating temporary directory ~p ...~n", [TmpBinDir]), + file:make_dir(TmpBinDir), + + io:fwrite("Copying file \"~s.boot\" to ~p ...~n", + [PlainRelFileName, filename:join([TmpBinDir, "start.boot"])]), + copy_file(PlainRelFileName++".boot",filename:join([TmpBinDir, "start.boot"])), + + io:fwrite("Copying files \"epmd\", \"run_erl\" and \"to_erl\" from \n" + "~p to ~p ...~n", + [ErtsBinDir, TmpBinDir]), + copy_file(filename:join([ErtsBinDir, "epmd"]), + filename:join([TmpBinDir, "epmd"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), + filename:join([TmpBinDir, "run_erl"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), + filename:join([TmpBinDir, "to_erl"]), [preserve]), + + StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]), + io:fwrite("Creating ~p ...~n", [StartErlDataFile]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + write_file(StartErlDataFile, StartErlData), + + io:fwrite("Recreating tar file ~p from contents in directory ~p ...~n", + [TarFileName,TmpDir]), + {ok, Tar} = erl_tar:open(TarFileName, [write, compressed]), + %% {ok, Cwd} = file:get_cwd(), + %% file:set_cwd("tmp"), + ErtsDir = "erts-"++ErtsVsn, + erl_tar:add(Tar, filename:join(TmpDir,"bin"), "bin", []), + erl_tar:add(Tar, filename:join(TmpDir,ErtsDir), ErtsDir, []), + erl_tar:add(Tar, filename:join(TmpDir,"releases"), "releases", []), + erl_tar:add(Tar, filename:join(TmpDir,"lib"), "lib", []), + erl_tar:close(Tar), + %% file:set_cwd(Cwd), + io:fwrite("Removing directory ~p ...~n",[TmpDir]), + remove_dir_tree(TmpDir), + ok. + + +install(RelFileName, RootDir) -> + TarFile = RelFileName ++ ".tar.gz", + io:fwrite("Extracting ~p ...~n", [TarFile]), + extract_tar(TarFile, RootDir), + StartErlDataFile = filename:join([RootDir, "releases", "start_erl.data"]), + {ok, StartErlData} = read_txt_file(StartErlDataFile), + [ErlVsn, _RelVsn| _] = string:tokens(StartErlData, " \n"), + ErtsBinDir = filename:join([RootDir, "erts-" ++ ErlVsn, "bin"]), + BinDir = filename:join([RootDir, "bin"]), + io:fwrite("Substituting in erl.src, start.src and start_erl.src to\n" + "form erl, start and start_erl ...\n"), + subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir, + [{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}], + [preserve]), + io:fwrite("Creating the RELEASES file ...\n"), + create_RELEASES(RootDir, + filename:join([RootDir, "releases", RelFileName])). + +%% LOCALS + +%% make_script(RelFileName,Opts) +%% +make_script(RelFileName,Opts) -> + systools:make_script(RelFileName, [no_module_tests, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% make_tar(RelFileName,Opts) +%% +make_tar(RelFileName,Opts) -> + RootDir = code:root_dir(), + systools:make_tar(RelFileName, [{erts, RootDir}, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% extract_tar(TarFile, DestDir) +%% +extract_tar(TarFile, DestDir) -> + erl_tar:extract(TarFile, [{cwd, DestDir}, compressed]). + +create_RELEASES(DestDir, RelFileName) -> + release_handler:create_RELEASES(DestDir, RelFileName ++ ".rel"). + +subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> + lists:foreach(fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, + Vars, Opts) + end, Scripts). + +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> + subst_file(filename:join([SrcDir, Script ++ ".src"]), + filename:join([DestDir, Script]), + Vars, Opts). + +subst_file(Src, Dest, Vars, Opts) -> + {ok, Conts} = read_txt_file(Src), + NConts = subst(Conts, Vars), + write_file(Dest, NConts), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +%% subst(Str, Vars) +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Str, using the list +%% of variables in Vars. +%% +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). + +copy_file(Src, Dest, Opts) -> + {ok,_} = file:copy(Src, Dest), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +write_file(FName, Conts) -> + {ok, Fd} = file:open(FName, [write]), + file:write(Fd, Conts), + file:close(Fd). + +read_txt_file(File) -> + {ok, Bin} = file:read_file(File), + {ok, binary_to_list(Bin)}. + +remove_dir_tree(Dir) -> + remove_all_files(".", [Dir]). + +remove_all_files(Dir, Files) -> + lists:foreach(fun(File) -> + FilePath = filename:join([Dir, File]), + case filelib:is_dir(FilePath) of + true -> + {ok, DirFiles} = file:list_dir(FilePath), + remove_all_files(FilePath, DirFiles), + file:del_dir(FilePath); + _ -> + file:delete(FilePath) + end + end, Files). diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 13753565d8..8004ef2c5a 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -53,8 +53,7 @@ start_link(Options) -> gen_server:start_link({local, rb_server}, rb, Options, []). stop() -> - call(stop), - supervisor:delete_child(sasl_sup, rb_server). + supervisor:terminate_child(sasl_sup, rb_server). rescan() -> rescan([]). rescan(Options) -> @@ -205,8 +204,6 @@ handle_call({rescan, Options}, _From, State) -> NewState = State#state{data = Data, max = Max, type = Type, device = Device, abort = Abort, log = Log1}, {reply, ok, NewState}; -handle_call(stop, _From, State) -> - {stop, normal, stopped, State}; handle_call(_, _From, #state{data = undefined}) -> {reply, {error, no_data}, #state{}}; handle_call({list, Type}, _From, State) -> @@ -312,11 +309,14 @@ scan_files(RptDir, Max, Type) -> {ok, Fd} -> case catch file:read(Fd, 1) of {ok, [LastWritten]} -> + file:close(Fd), Files = make_file_list(RptDir, LastWritten), scan_files(RptDir, Files, Max, Type); - _ -> exit("cannot read the index file") + _X -> + file:close(Fd), + exit("cannot read the index file") end; - _ -> exit("cannot read the index file") + _X -> exit("cannot read the index file") end. make_file_list(Dir, FirstFileNo) -> @@ -789,7 +789,7 @@ filter_report([{Key, RegExp, re}|T], Msg) -> filter_report([{Key, RegExp, re, no}|T], Msg) -> case proplists:get_value(Key, Msg) of undefined -> - false; + true; Value -> Subject = lists:flatten(io_lib:format("~p",[Value])), case run_re(Subject, RegExp) of diff --git a/lib/sasl/test/.gitignore b/lib/sasl/test/.gitignore new file mode 100644 index 0000000000..76e706b874 --- /dev/null +++ b/lib/sasl/test/.gitignore @@ -0,0 +1,5 @@ +# +# Don't ignore *.beam files in any sub-directory. +# + +!*.beam diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile new file mode 100644 index 0000000000..ad08c8136b --- /dev/null +++ b/lib/sasl/test/Makefile @@ -0,0 +1,91 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2011. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + sasl_SUITE \ + alarm_handler_SUITE \ + installer \ + release_handler_SUITE \ + systools_SUITE \ + systools_rc_SUITE \ + overload_SUITE \ + rb_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/sasl_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \ + -I$(ERL_TOP)/lib/sasl/src + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make'\ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/sasl/test/alarm_handler_SUITE.erl b/lib/sasl/test/alarm_handler_SUITE.erl new file mode 100644 index 0000000000..a98e8c9c67 --- /dev/null +++ b/lib/sasl/test/alarm_handler_SUITE.erl @@ -0,0 +1,179 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(alarm_handler_SUITE). + +-include_lib("test_server/include/test_server.hrl"). + +%%----------------------------------------------------------------- +%% We will add an own alarm handler in order to verify that the +%% alarm_handler deliver the expected events. +%%----------------------------------------------------------------- + +-export([init_per_suite/1, end_per_suite/1, all/0,groups/0, + init_per_group/2,end_per_group/2, + set_alarm/1, clear_alarm/1, swap/1]). + +-export([init/1, handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + [set_alarm, clear_alarm, swap]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%----------------------------------------------------------------- + +set_alarm(suite) -> []; +set_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line ok = alarm_handler:set_alarm(Alarm1), + reported(set_alarm, Alarm1), + ?line ok = alarm_handler:set_alarm(Alarm2), + reported(set_alarm, Alarm2), + ?line ok = alarm_handler:set_alarm(Alarm3), + reported(set_alarm, Alarm3), + + ?line [Alarm3,Alarm2,Alarm1] = alarm_handler:get_alarms(), + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ?line [] = alarm_handler:get_alarms(), + + test_server:messages_get(), + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +clear_alarm(suite) -> []; +clear_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + alarm_handler:set_alarm(Alarm1), + alarm_handler:set_alarm(Alarm2), + alarm_handler:set_alarm(Alarm3), + test_server:messages_get(), + + ?line ok = alarm_handler:clear_alarm(alarm1), + reported(clear_alarm, alarm1), + ?line ok = alarm_handler:clear_alarm("alarm2"), + reported(clear_alarm, "alarm2"), + ?line ok = alarm_handler:clear_alarm({alarm3}), + reported(clear_alarm, {alarm3}), + ?line [] = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +swap(suite) -> []; +swap(Config) when is_list(Config) -> + ?line Alarm1 = {alarm1, "this is the alarm"}, + ?line Alarm2 = {"alarm2", this_is_the_alarm}, + ?line Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line alarm_handler:set_alarm(Alarm1), + ?line alarm_handler:set_alarm(Alarm2), + ?line alarm_handler:set_alarm(Alarm3), + + ?line foo, + case gen_event:which_handlers(alarm_handler) of + [alarm_handler] -> + ?line ok = gen_event:swap_handler(alarm_handler, + {alarm_handler, swap}, + {?MODULE, self()}), + ?line [?MODULE] = gen_event:which_handlers(alarm_handler), + Alarms = [Alarm3, Alarm2, Alarm1], + reported(swap_alarms, Alarms), + + %% get_alarms is only valid with the default handler installed. + ?line {error, _} = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, + ?MODULE, []), + ?line gen_event:add_handler(alarm_handler, alarm_handler, []), + ok; + _ -> + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ok + end. + +%%----------------------------------------------------------------- +%% Check that the alarm has been received. +%%----------------------------------------------------------------- +reported(Tag, Data) -> + receive + {Tag, Data} -> + test_server:messages_get(), + ok + after 1000 -> + test_server:fail(no_alarm_received) + end. + +%%----------------------------------------------------------------- +%% The error_logger handler (gen_event behaviour). +%% Sends a notification to the Tester process about the events +%% generated by the Tester process. +%%----------------------------------------------------------------- +init(Tester) when is_pid(Tester) -> + {ok, Tester}; +init({Tester, {alarm_handler,Alarms}}) -> % Swap from default handler. + Tester ! {swap_alarms, Alarms}, + {ok, Tester}. + +handle_event({set_alarm, Alarm}, Tester) -> + Tester ! {set_alarm, Alarm}, + {ok, Tester}; +handle_event({clear_alarm, AlarmId}, Tester) -> + Tester ! {clear_alarm, AlarmId}, + {ok, Tester}; +handle_event(_Event, Tester) -> + {ok, Tester}. + +handle_info(_, Tester) -> + {ok, Tester}. + +handle_call(_Query, Tester) -> {ok, {error, bad_query}, Tester}. + +terminate(_Reason, _Tester) -> + my_yes. diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl new file mode 100644 index 0000000000..a114c4b5c9 --- /dev/null +++ b/lib/sasl/test/installer.erl @@ -0,0 +1,778 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(installer). + +%%-compile(export_all). +-export([install_1/2]). +-export([install_2/1]). +-export([install_3/2]). +-export([install_3a/1]). +-export([install_4/1]). +-export([install_5/1]). +-export([install_5a/1]). +-export([install_6/1]). +-export([install_7/1]). +-export([install_8/1]). +-export([install_9/1]). +-export([install_10/1]). +-export([install_11/1]). +-export([client1_1/4]). +-export([client2/3]). +-export([stop/1]). +-export([unpack_p1h/2]). +-export([permanent_p1h/1]). +-export([reg_proc/1]). +-export([registered_loop/1]). + +-define(print(List), {rh_print, TestNode} ! {print, {?MODULE, ?LINE}, List}). +-define(print_line(Line,List), {rh_print, TestNode} ! {print, {?MODULE, Line}, List}). +-define(fail(Term), exit({?MODULE, ?LINE, Term})). +-define(fail_line(Line,Term), exit({?MODULE, Line, Term})). + +-define(check_release(Vsn,Status,Apps), + check_release(TestNode,node(),Vsn,Status,Apps,?LINE)). +-define(check_release_client(Node,Vsn,Status,Apps), + check_release(TestNode,Node,Vsn,Status,Apps,?LINE)). + +-define(check_running_app(App,Vsn), + check_running_app(TestNode,node(),App,Vsn,?LINE)). +-define(check_running_app_client(Node,App,Vsn), + check_running_app(TestNode,Node,App,Vsn,?LINE)). + + +install_1(TestNode,PrivDir) -> + ?print([TestNode]), + ?print(["install_1 start"]), + + % Unpack and install P1H + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?print(["unpack_release P1H ok"]), + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_release P1H ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + X = a:a(), + ?print(["X", X]), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + ?print(["install_1 end OK"]), + ok. + % release_handler_SUITE will reboot this node now! + +install_2(TestNode) -> + ?print(["install_2 start"]), + + % Check that P1H is still unpacked, install it and make_permanent + ?check_release("P1H",unpacked,["a-1.0"]), + ?print(["install_2 P1H unpacked"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_2 install_release ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + ok = release_handler:make_permanent("P1H"). + % release_handler_SUITE will reboot this node now! + +install_3(TestNode,PrivDir) -> + ?print(["install_3 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Unpack and install P1I + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + ?print(["install_3 unpack_release P1I ok"]), + ?check_release("P1I",unpacked,["a-1.1"]), + {ok,"P1H",[{extra, gott}]} = release_handler:check_install_release("P1I"), + {error,_} = release_handler:check_install_release("P1J"), + {ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"), + ?print(["install_3 install_release P1I ok"]), + ?check_release("P1I",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X2 = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = a:b(), + + % Unpack and install P2A + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + ?print(["install_3 unpack_release P2A ok"]), + ?check_release("P2A",unpacked,["a-1.1"]), + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok = release_handler:make_permanent("P1I"), + ?print(["install_3 make_permanent P1I ok"]), + ?check_release("P1I",permanent,["a-1.1"]), + ok. + +install_3a(TestNode) -> + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_3 P2A installed"]), + ok. + + + +install_4(TestNode) -> + ?print(["install_4 start"]), + + % Check that P2A is in use. + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok. + % release_handler_SUITE will reboot this node now! + +install_5(TestNode) -> + ?print(["install_5 start"]), + + % Check that P1I is used + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok. + +install_5a(TestNode) -> + % Install P2A again + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_5 P2A installed"]), + ok. + +install_6(TestNode) -> + ?print(["install_6 start"]), + + % Check that P2A is used + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok = release_handler:make_permanent("P2A"). + % release_handler_SUITE will reboot this node now! + + +install_7(TestNode) -> + ?print(["install_7 start"]), + + % Check that P2A is used + ?check_release("P2A",permanent,["a-1.1"]), + + % Install old P1H + ok = release_handler:reboot_old_release("P1H"), + ok. + +install_8(TestNode) -> + ?print(["install_8 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2A and check that a-1.1 and erts-<latest> are removed + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + true = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + {ok, Dirs} = file:list_dir(code:root_dir()), + ["erts-4.4"] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs), + ok. + % release_handler_SUITE will reboot this node now! + +install_9(TestNode) -> + ?print(["install_9 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Install old P1G + ok = release_handler:reboot_old_release("P1G"), + ok. + +install_10(TestNode) -> + ?print(["install_10 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ?check_release("P1H",old,["a-1.0"]), + + %% Remove P1H and check that both versions of application a is removed + ok = release_handler:remove_release("P1H"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + false = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + ok. + % release_handler_SUITE will reboot this node now! + +install_11(TestNode) -> + ?print(["install_11 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ok. + + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node runs all tests as in installer/1 test case. +%% Thus, the client node will be rebooted several times. +%% The to_erl /tmp/NODENAME@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/NODENAME@HOSTNAME/log +%%----------------------------------------------------------------- + + +client1_1(TestNode,PrivDir,MasterDir,ClientSname) -> + TestHost = test_host(), + ?print(["client1_1 start"]), + + {ok,IP} = inet:getaddr(TestHost,inet), + erl_boot_server:start([IP]), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + trace_disallowed_calls(Node), + + %% Check env var for SASL on client node + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + + %% Unpack P1H on master + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + %% Unpack and install P1H on client + P1HDir = filename:join([Root, "releases", "P1H"]), + + %% The AppDirs argument (last arg to set_unpacked) below is really + %% not necessary, it could just be [] since the path is the same + %% as default. But it is given here in order to force hitting the + %% release_handler:check_path function so it can be checked that + %% it does not use file:read_file_info on the client node, see + %% trace_disallowed_calls/1 and check_disallowed_calls/0 below. + %% (OTP-9142) + {ok, "P1H"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1HDir, "rel1.rel"), + [{a,"1.0",filename:join(MasterDir,lib)}]]), + + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "relup")]), + ?print([{release_handler_state, Node}, + rpc:call(Node, sys, get_status, [release_handler])]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + + Apps = rpc:call(Node, application, which_applications, []), + {a,"A CXC 138 11","1.0"} = lists:keyfind(a, 1, Apps), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_2(TestNode,PrivDir,Node). + +client1_2(TestNode,PrivDir,Node) -> + ?print(["client1_2 start"]), + + %% Check that P1H is still unpacked, install it and make_permanent + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + ?check_release_client(Node,"P1H",current,["a-1.0"]), + ?check_running_app_client(Node,a,"1.0"), + + ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_3(TestNode,PrivDir,Node). + +client1_3(TestNode,PrivDir,Node) -> + ?print(["client1_3 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Unpack P1I on master + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + + MasterRoot = code:root_dir(), + + %% Unpack and install P1I on client + P1IDir = filename:join([MasterRoot, "releases", "P1I"]), + {ok, "P1I"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1IDir, "rel2.rel"),[]]), + + ?check_release_client(Node,"P1I",unpacked,["a-1.1"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "relup")]), + + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, check_install_release, ["P1I"]), + {error,_} = rpc:call(Node, release_handler, check_install_release, ["P1J"]), + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, install_release, ["P1I"]), + + ?check_running_app_client(Node,a,"1.1"), + X2 = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Unpack P2A on master + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + + %% Unpack and install P2A on client + P2ADir = filename:join([MasterRoot, "releases", "P2A"]), + {ok, "P2A"} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join(P2ADir, "rel3.rel"),[]]), + + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "relup")]), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + ok = rpc:call(Node, release_handler, make_permanent, ["P1I"]), + ?check_release_client(Node,"P1I",permanent,["a-1.1"]), + + %% since the install_release below reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + %% Reboots the client ! + + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_4(TestNode,Node). + +client1_4(TestNode,Node) -> + ?print(["client1_4 start"]), + + %% Check that P2A is in use. + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Reboot from P1I + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_5(TestNode,Node). + +client1_5(TestNode,Node) -> + ?print(["client1_5 start"]), + + %% Check that P1I is used + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + + %% since the install_release below will reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install P2A again + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + + %% We are rebooted again. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_6(TestNode,Node). + +client1_6(TestNode,Node) -> + ?print(["client1_6 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Make P2A permanent + ok = rpc:call(Node, release_handler, make_permanent, ["P2A"]), + + %% Reboot from P2A + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_7(TestNode,Node). + +client1_7(TestNode,Node) -> + ?print(["client1_7 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",permanent,["a-1.1"]), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1H + rpc:call(Node, release_handler, reboot_old_release, ["P1H"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_8(TestNode,Node). + +client1_8(TestNode,Node) -> + ?print(["client1_8 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2I from client + ok = rpc:call(Node, release_handler, set_removed, ["P2A"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1I"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_9(TestNode,Node). + +client1_9(TestNode,Node) -> + ?print(["client1_9 start"]), + + %% Check that P2A and P1I does not exists and that PiH is permanent. + Rels = rpc:call(Node, release_handler, which_releases, []), + false = lists:keysearch("P2A", 2, Rels), + false = lists:keysearch("P1I", 2, Rels), + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1G + rpc:call(Node, release_handler, reboot_old_release, ["P1G"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_10(TestNode,Node). + +client1_10(TestNode,Node) -> + ?print(["client1_10 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + ?check_release_client(Node,"P1H",old,["a-1.0"]), + {error,client_node} = rpc:call(Node,release_handler,remove_release,["P1H"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_11(TestNode,Node). + +client1_11(TestNode,Node) -> + ?print(["client1_11 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + + check_disallowed_calls(), + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + ok = release_handler:remove_release("P1H"), + ok. + +%% Start tracing of the file module on the client node. This module +%% shall never be called, since +%% 1) the node is a client from the release_handler's point of view, +%% so all file access should be done via rpc calls to the master +%% 2) it is started with erl_prim_loader loader set to 'inet' so all +%% code loading should be done via the inet to the master +%% (OTP-9142) +%% This function is called each time the client node is started and to +%% check if a call has been made, call check_disallowed_node/0 +trace_disallowed_calls(Node) -> + MasterProc = self(), + rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), + rpc:call(Node,dbg,p,[all,call]), + rpc:call(Node,dbg,tp,[file,[]]). + +check_disallowed_calls() -> + receive + Trace when element(1,Trace)==trace -> + exit({disallowed_function_call,Trace}) + after 0 -> + ok + end. + +start_client(TestNode,Client) -> + {Start, Node} = do_start_client(Client,test_host()), + Cmd = lists:concat(["env NODENAME=",Client," ", + filename:join(code:root_dir(), Start)]), + ?print([{start_client,Client},Cmd]), + Res = os:cmd(Cmd), + ?print([{start_client,result},Res]), + receive + {nodeup, Node} -> + wait_started(TestNode,Node) + after 30000 -> + ?print([{start_client,failed,Node},net_adm:ping(Node)]), + ?fail({"can not start", Node}) + end. + +do_start_client(Client, Host) -> + Node = list_to_atom(lists:concat([Client,"@",Host])), + Start = filename:join(["clients", "type1", Node, "bin", "start"]), + {Start, Node}. + +reboot(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, reboot, []), + check_reboot(TestNode,Node). + +%% This way of checking that the node is rebooted will only work if +%% the nodes are automatically re-connected after the reboot. This +%% happens for master/client (when sasl is started on the client). +check_reboot(TestNode,Node) -> + receive + {nodedown, Node} -> + receive + {nodeup, Node} -> wait_started(TestNode,Node) + after 30000 -> + ?fail({Node, "not rebooted",net_adm:ping(Node)}) + end + after 30000 -> + ?fail({Node, "not closing down",net_adm:ping(Node)}) + end. + +stop_client(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, stop, []), + receive + {nodedown, Node} -> ok + after 30000 -> + ?fail({Node, "not stopping"}) + end. + +wait_started(TestNode,Node) -> + case rpc:call(Node, init, get_status, []) of + {started, _} -> + cover_client(TestNode,Node,start_cover), + Node; + _ -> + timer:sleep(1000), + wait_started(TestNode,Node) + end. + +cover_client(TestNode,Node,Func) -> + R = rpc:call(TestNode,release_handler_SUITE,Func,[Node]), + ?print([{Func,Node,R}]). + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node has the name cli2@HOSTNAME. +%% The client node is not allowed to do ANY release updates +%% as it also have another (non-existing) master node. +%% +%% The to_erl /tmp/cli2@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/cli2@HOSTNAME/log +%%----------------------------------------------------------------- +client2(TestNode,PrivDir,ClientSname) -> + TestHost = test_host(), + ?print(["client2 start"]), + + %% Clean up if previous test case failed + release_handler:remove_release("P1H"), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + + %% Check env var for SASL on client node + ?print([{sasl_env, Node}, rpc:call(Node, application, get_all_env, [sasl])]), + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master,Master2]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + true = (Master2 =:= list_to_atom("master2@"++TestHost)), + + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + {error,{bad_masters,[Master2]}} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join([Root, "releases", "P1H", "rel1.rel"]),[]]), + + {error,{no_such_release,"P1H"}} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + release_handler:remove_release("P1H"), + ok. + + +stop(Now) -> + %% The timestamp is only used for debugging. It is printed by + %% release_handler_SUITE also. + R = init:stop(), + erlang:display({init_stop,Now,R}), + R. + +unpack_p1h(TestNode,PrivDir) -> + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?check_release("P1H",unpacked,["a-1.0"]), + ok. + +permanent_p1h(TestNode) -> + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?check_release("P1H",current,["a-1.0"]), + ok = release_handler:make_permanent("P1H"), + ?check_release("P1H",permanent,["a-1.0"]), + ok. + + +reg_proc(Name) -> + catch unregister(Name), + Pid = spawn_link(?MODULE, registered_loop, [Name]), + global:register_name(Name, Pid), + ok. + +registered_loop(_Name) -> + receive + kill -> + exit(killed) + end. + +check_release(TestNode,Node,Vsn,Status,Apps,Line) -> + case rpc:call(Node,release_handler,which_releases,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_release,Node,Vsn,Status,Error}); + Rels -> + ?print_line(Line,["check_release:", Rels]), + {"SASL-test", Vsn, Libs, Status} = lists:keyfind(Vsn, 2, Rels), + true = lists:all(fun(App) -> lists:member(App,Libs) end,Apps), + ok + end. + +check_running_app(TestNode,Node,App,Vsn,Line) -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_running_app,Node,App,Vsn,Error}); + Apps -> + ?print_line(Line,["check_running_app:", Apps]), + {App, _, Vsn} = lists:keyfind(a, 1, Apps), + ok + end. + +test_host() -> + {ok,Host} = inet:gethostname(), + Host. + +unpack_release(PrivDir,Rel) -> + copy(filename:join([PrivDir,Rel,Rel++".tar.gz"]), + filename:join(code:root_dir(),releases)), + release_handler:unpack_release(Rel). + +copy(Src, DestDir) -> + Dest = filename:join(DestDir, filename:basename(Src)), + {ok,_} = file:copy(Src, Dest), + ok. + diff --git a/lib/sasl/test/overload_SUITE.erl b/lib/sasl/test/overload_SUITE.erl new file mode 100644 index 0000000000..92b1aaed6e --- /dev/null +++ b/lib/sasl/test/overload_SUITE.erl @@ -0,0 +1,175 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(overload_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> [info, set_config_data, set_env_vars, request, timeout]. +all(suite) -> all(). + +init_per_testcase(_Case,Config) -> + restart_sasl(), + Config. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +%%%----------------------------------------------------------------- +info(suite) -> []; +info(_Config) -> + ?line Info = overload:get_overload_info(), + ?line [{total_intensity,0.0}, + {accept_intensity,0.0}, + {max_intensity,0.8}, + {weight,0.1}, + {total_requests,0}, + {accepted_requests,0}] = Info. + +%%%----------------------------------------------------------------- +set_config_data(suite) -> []; +set_config_data(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = overload:set_config_data(0.5,0.4), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. + +%%%----------------------------------------------------------------- +set_env_vars(suite) -> []; +set_env_vars(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = application:set_env(sasl,overload_max_intensity,0.5), + ?line ok = application:set_env(sasl,overload_weight,0.4), + ?line ok = application:stop(sasl), + ?line ok = application:start(sasl), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. +set_env_vars(cleanup,_Config) -> + application:unset_env(sasl,overload_max_intensity), + application:unset_env(sasl,overload_weight), + ok. + +%%%----------------------------------------------------------------- +request(suite) -> []; +request(_Config) -> + %% Find number of request that can be done with default settings + %% and no delay + ?line overload:set_config_data(0.8, 0.1), + ?line NDefault = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NDefault: ~p",[NDefault]), + + %% Check that the number of requests increases when max_intensity + %% increases + ?line overload:set_config_data(2, 0.1), + ?line NLargeMI = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeMI: ~p",[NLargeMI]), + ?line true = NLargeMI > NDefault, + + %% Check that the number of requests decreases when weight + %% increases + ?line overload:set_config_data(0.8, 1), + ?line NLargeWeight = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeWeight: ~p",[NLargeWeight]), + ?line true = NLargeWeight < NDefault, + + %% Check that number of requests increases when delay between + %% requests increases. + %% (Keeping same config and comparing to large weight in order to + %% minimize the time needed for this case.) + ?line overload:set_config_data(0.8, 1), + ?line NLargeTime = do_many_requests(500), + ?line restart_sasl(), + ?line ?t:format("NLargeTime: ~p",[NLargeTime]), + ?line true = NLargeTime > NLargeWeight, + ok. + +%%%----------------------------------------------------------------- +timeout(suite) -> []; +timeout(_Config) -> + ?line overload:set_config_data(0.8, 1), + ?line _N = do_many_requests(0), + + %% Check that the overload alarm is raised + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% Fake a clear timeout in overload.erl and check that, since it + %% came very soon after the overload situation, the alarm is not + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% A bit later, try again and check that this time the alarm is + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [] = alarm_handler:get_alarms(), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +%%%----------------------------------------------------------------- +%%% Call overload:request/0 up to 30 times with the given time delay +%%% between. Stop when 'reject' is returned. +do_many_requests(T) -> + 30 - do_requests(30,T). + +do_requests(0,_) -> + ?t:fail(never_rejected); +do_requests(N,T) -> + case overload:request() of + accept -> + timer:sleep(T), + do_requests(N-1,T); + reject -> + N + end. + +%%%----------------------------------------------------------------- +%%% Restart the sasl application +restart_sasl() -> + application:stop(sasl), + application:start(sasl), + ok. + +%%%----------------------------------------------------------------- +%%% Check that max_intensity and weight is set as expected +check_info(MI,W,Info) -> + case {lists:keyfind(max_intensity,1,Info), lists:keyfind(weight,1,Info)} of + {{_,MI},{_,W}} -> ok; + _ -> ?t:fail({unexpected_info,MI,W,Info}) + end. + + diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl new file mode 100644 index 0000000000..b53c382609 --- /dev/null +++ b/lib/sasl/test/rb_SUITE.erl @@ -0,0 +1,606 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(rb_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +-define(SUP,rb_SUITE_sup). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all() -> + no_group_cases() ++ [{group,running_error_logger}]. + +no_group_cases() -> + [help, + start_error_stop]. + +groups() -> + [{running_error_logger,[shuffle],[show, + list, + rescan, + start_stop_log, + grep, + filter_filter, + filter_date, + filter_filter_and_date, + filter_re_no + ]}]. + + +all(suite) -> + no_group_cases() ++ + [{conf, + install_mf_h, + element(3,lists:keyfind(running_error_logger,1,groups())), + remove_mf_h} + ]. + + +init_per_suite(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line RbDir = filename:join(PrivDir,rb), + ?line ok = file:make_dir(RbDir), + NewConfig = [{rb_dir,RbDir}|Config], + reset_sasl(NewConfig), + NewConfig. + +end_per_suite(_Config) -> + ok. + +init_per_group(running_error_logger,Config) -> + install_mf_h(Config). + +end_per_group(running_error_logger,Config) -> + remove_mf_h(Config). + +init_per_testcase(_Case,Config) -> + case whereis(?SUP) of + undefined -> ok; + Pid -> kill(Pid) + end, + empty_error_logs(Config), + Config. + +kill(Pid) -> + Ref = erlang:monitor(process,Pid), + exit(Pid,kill), + receive {'DOWN', Ref, process, Pid, _Info} -> ok end. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + + +%%%----------------------------------------------------------------- + +help() -> help(suite). +help(suite) -> []; +help(_Config) -> + ?line Help = capture(fun() -> rb:h() end), + ?line "Report Browser Tool - usage" = hd(Help), + ?line "rb:stop - stop the rb_server" = lists:last(Help), + ok. + + +start_error_stop() -> start_error_stop(suite). +start_error_stop(suite) -> []; +start_error_stop(Config) -> + ?line RbDir = ?config(rb_dir,Config), + + ?line {error,{"cannot locate report directory",_}} = rb:start(), + + + ?line ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,1000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + ?line {error,{"cannot read the index file",_}} = rb:start(), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line restart_sasl(), + ?line {ok,_} = rb:start(), + + ?line ok = rb:stop(), + ok. + + +%% start_opts(suite) -> []; +%% start_opts(Config) -> +%% PrivDir = ?config(priv_dir,Config), +%% RbDir = filename:join(PrivDir,rb_opts), +%% ok = file:make_dir(RbDir), + + +install_mf_h(Config) -> + ?line RbDir = ?config(rb_dir,Config), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,5000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + Config. + +remove_mf_h(_Config) -> + ok. + + + +show() -> show(suite). +show(suite) -> []; +show(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + %% Show all reports + ?line All = check_report(fun() -> rb:show() end,OutFile), + + %% Show by number + ?line [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile), + ?line {1,First} = lists:keyfind(1,1,All), + + %% Show by type + ?line [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile), + ?line true = contains(CR,"rb_test_crash"), + ?line [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile), + ?line true = contains(EC,"rb_test_crash"), + ?line true = contains(EM,"rb_test_error_msg"), + ?line [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile), + ?line true = contains(ER,"rb_test_error"), + ?line [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile), + ?line true = contains(IR,"rb_test_info"), + ?line [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile), + ?line true = contains(IM,"rb_test_info_msg"), + ?line [_|_] = check_report(fun() -> rb:show(progress) end,OutFile), + ?line [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end, + OutFile), + ?line true = contains(SR,"child_terminated"), + ?line true = contains(SR,"{rb_SUITE,rb_test_crash}"), + + ok. + +list() -> list(suite). +list(suite) -> []; +list(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = capture(fun() -> rb:list() end), + ?line [{crash_report,[_]=CR}, + {error,[_,_]=EM}, + {error_report,[_]=ER}, + {info_msg,[_]=IM}, + {info_report,[_]=IR}, + {progress,[_|_]=P}, + {supervisor_report,[_]=SR}] = sort_list(All), + + ?line [{crash_report,CR}] = + sort_list(capture(fun() -> rb:list(crash_report) end)), + ?line [{error,EM}] = + sort_list(capture(fun() -> rb:list(error) end)), + ?line [{error_report,ER}] = + sort_list(capture(fun() -> rb:list(error_report) end)), + ?line [{info_msg,IM}] = + sort_list(capture(fun() -> rb:list(info_msg) end)), + ?line [{info_report,IR}] = + sort_list(capture(fun() -> rb:list(info_report) end)), + ?line [{progress,P}] = + sort_list(capture(fun() -> rb:list(progress) end)), + ?line [{supervisor_report,SR}] = + sort_list(capture(fun() -> rb:list(supervisor_report) end)), + + ok. + + +grep() -> grep(suite). +grep(suite) -> []; +grep(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line [{_,S}, + {_,CR}, + {_,EC}, + {_,IM}, + {_,IR}, + {_,EM}, + {_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile), + ?line true = contains(S, "rb_test_crash"), + ?line true = contains(CR, "rb_test_crash"), + ?line true = contains(EC, "rb_test_crash"), + ?line true = contains(IM, "rb_test_info_msg"), + ?line true = contains(IR, "rb_test_info"), + ?line true = contains(EM, "rb_test_error_msg"), + ?line true = contains(ER, "rb_test_error"), + ok. + + +filter_filter() -> filter_filter(suite). +filter_filter(suite) -> []; +filter_filter(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test}],OutFile), + ?line _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERRep} = lists:unzip(ER), + {_,AllButERRep} = lists:unzip(AllButER), + ?line AllButERRep = AllRep -- ERRep, + + ok. + +filter_date() -> filter_date(suite). +filter_date(suite) -> []; +filter_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = rb_filter([],{Before,from},OutFile), + ?line All = rb_filter([],{After,to},OutFile), + ?line [] = rb_filter([],{Before,to},OutFile), + ?line [] = rb_filter([],{After,from},OutFile), + ?line All = rb_filter([],{Before,After},OutFile), + + %%?t:format("~p~n",[All]), + ?line AllButLast = [{N-1,R} || {N,R} <- tl(All)], + ?line AllButLast = rb_filter([],{Before,Between1},OutFile), + + ?line Last = hd(All), + ?line [Last] = rb_filter([],{Between2,After},OutFile), + + ok. + +filter_filter_and_date() -> filter_filter_and_date(suite). +filter_filter_and_date(suite) -> []; +filter_filter_and_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line error_logger:error_report([{rb_SUITE,rb_test_filter}]), + ?line ok = start_rb(OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + ?line Last = hd(All), + + ?line [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile), + ?line [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile), + ?line [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile), + ?line [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile), + ?line [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile), + ?line {_,Str} = Last, + ?line false = contains(Str,"rb_test_filter"), + + ok. + + +filter_re_no() -> filter_re_no(suite). +filter_re_no(suite) -> []; +filter_re_no(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERep} = lists:unzip(E), + {_,AllButERep} = lists:unzip(AllButE), + ?line AllButERep = AllRep -- ERep, + + ok. + + +rescan() -> rescan(suite). +rescan(suite) -> []; +rescan(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Start rb + ?line ok = start_rb(OutFile), + + %% Insert one more report and check that the list is longer. Note + %% that there might be two more reports, since the progress report + %% from starting rb_server might not be included before the rescan. + ?line AllBefore = capture(fun() -> rb:list() end), + ?line error_logger:error_report([{rb_SUITE,rb_test_rescan}]), + ?line ok = rb:rescan(), + ?line AllAfter = capture(fun() -> rb:list() end), + ?line Diff = length(AllAfter) - length(AllBefore), + ?line true = (Diff >= 1), + + ok. + + +start_stop_log() -> start_stop_log(suite). +start_stop_log(suite) -> []; +start_stop_log(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + ?line ok = file:write_file(OutFile,[]), + + %% Start rb and check that show is printed to standard_io + ?line ok = start_rb(), + ?line StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to log and not to standad_io + ?line ok = rb:start_log(OutFile), + ?line [] = capture(fun() -> rb:show(1) end), + ?line {ok,Bin} = file:read_file(OutFile), + ?line true = (Bin =/= <<>>), + + %% Stop log and check that show is printed to standard_io and not to log + ?line ok = rb:stop_log(), + ?line ok = file:write_file(OutFile,[]), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Test that standard_io is used if log file can not be opened + ?line ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +restart_sasl() -> + application:stop(sasl), + ok = application:start(sasl), + wait_for_sasl(). + +reset_sasl(Config) -> + application:unset_env(sasl,error_logger_mf_dir), + application:unset_env(sasl,error_logger_mf_maxbytes), + application:unset_env(sasl,error_logger_mf_maxfiles), + empty_error_logs(Config). + +empty_error_logs(Config) -> + application:stop(sasl), + catch delete_content(?config(rb_dir, Config)), + ok = application:start(sasl), + wait_for_sasl(). + +wait_for_sasl() -> + wait_for_sasl(50). +wait_for_sasl(0) -> + ?t:fail("sasl application did not start within 5 seconds"); +wait_for_sasl(N) -> + case lists:keymember(sasl,1,application:which_applications()) of + true -> + ok; + false -> + timer:sleep(100), + wait_for_sasl(N-1) + end. + +start_rb(OutFile) -> + do_start_rb([{start_log,OutFile}]). +start_rb() -> + do_start_rb([]). + +do_start_rb(Opts) -> + {ok,Pid} = rb:start(Opts), + + %% Wait for process to started, then wait a little bit more + sys:get_status(Pid), + timer:sleep(500), + + %% Make sure printouts (e.g. from rb:list(), come to the test log, + %% and that they can be captured. + group_leader(group_leader(),Pid), + ok. + + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + delete_content(Dir), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +delete_content(Dir) -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files). + +init_error_logs() -> + ?line error_logger:error_report([{rb_SUITE,rb_test_error}]), + ?line error_logger:error_msg("rb_test_error_msg"), + ?line error_logger:info_report([{rb_SUITE,rb_test_info}]), + ?line error_logger:info_msg("rb_test_info_msg"), + ?line _Pid = start(), + ?line Ref = erlang:monitor(process,?MODULE), + ?line gen_server:cast(?MODULE,crash), + ?line receive {'DOWN',Ref,process,_,{rb_SUITE,rb_test_crash}} -> ok + after 2000 -> + ?t:format("Got: ~p~n",[process_info(self(),messages)]), + ?t:fail("rb_SUITE server never died") + end, + ?line erlang:demonitor(Ref), + ?line wait_for_server(), + ok. + +wait_for_server() -> + case whereis(?MODULE) of + undefined -> + wait_for_server(); + Pid -> + timer:sleep(100), % allow the supervisor report to be written + Pid + end. + +capture(Fun) -> + ?t:capture_start(), + ok = Fun(), + timer:sleep(1000), + ?t:capture_stop(), + string:tokens(lists:append(?t:capture_get()),"\n"). + + +rb_filter(Filter,OutFile) -> + check_report(fun() -> rb:filter(Filter) end, OutFile). +rb_filter(Filter,Dates,OutFile) -> + check_report(fun() -> rb:filter(Filter,Dates) end, OutFile). + + +%% This function first empties the given report file, then executes +%% the fun and returns a list of {N,Report}, where Report is a report +%% read from the file and N is an integer. The newest report has the +%% lowest number. +%% If the fun was a call to rb:show() (i.e. with no arguments), then +%% the numbering (N) will be the same as rb's own numbering (as shown +%% by rb:list()). +check_report(Fun,File) -> + file:delete(File), + rb:rescan([{start_log,File}]), + ok = Fun(), + {ok,Bin} = file:read_file(File), + Reports = split_reports(binary_to_list(Bin),[],[]), + lists:zip(lists:seq(1,length(Reports)),Reports). + +-define(report_header_line,"\n===============================================================================\n"). +split_reports([],Report,Reports) -> + add_report(Report,Reports); +split_reports(Text,Report,Reports) -> + case Text of + ?report_header_line++Rest -> + {Heading,PrevReport} = lists:splitwith(fun($\n) -> false; + (_) -> true + end, + Report), + split_reports(Rest, + ?report_header_line++Heading, + add_report(PrevReport,Reports)); + [Ch|Rest] -> + split_reports(Rest,[Ch|Report],Reports) + end. + +add_report(Report,Reports) -> + case string:strip(Report,both,$\n) of + [] -> Reports; + Report1 -> [lists:reverse(Report1)|Reports] + end. + +%% Returns true if Substr is a substring of Str. +contains(Str,Substr) -> + 0 =/= string:str(Str,Substr). + +%% Sort the result of rb_list after report type +sort_list(List) -> + sort_list(List,dict:new()). +sort_list([H|T],D) -> + case re:run(H,"\s+[0-9]+\s+([a-z_]+)",[{capture,all_but_first,list}]) of + nomatch -> + sort_list(T,D); + {match,[TypeStr]} -> + sort_list(T,dict:append(list_to_atom(TypeStr),H,D)) + end; +sort_list([],D) -> + lists:sort(dict:to_list(D)). + + +%%%----------------------------------------------------------------- +%%% A dummy supervisor and gen_server used for creating crash- and +%%% supervisor reports +start() -> + {ok,Pid} = + supervisor:start_link({local, ?SUP}, ?MODULE, i_am_supervisor), + unlink(Pid), + Pid. +start_server() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, i_am_server, []). +init(i_am_server) -> + {ok, state}; +init(i_am_supervisor) -> + AChild = {?SUP,{?MODULE,start_server,[]}, + permanent,2000,worker,[?MODULE]}, + {ok,{{one_for_all,1,1}, [AChild]}}. +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. +handle_cast(crash, State) -> + exit({rb_SUITE,rb_test_crash}), + {noreply, State}. +handle_info(_Info, State) -> + {noreply, State}. +terminate(_Reason, _State) -> + ok. + diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl new file mode 100644 index 0000000000..efa775f344 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -0,0 +1,1651 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(release_handler_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +% Default timetrap timeout (set in init_per_testcase). +%-define(default_timeout, ?t:minutes(40)). +-define(default_timeout, ?t:minutes(10)). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + case os:type() of + {unix, _} -> unix_cases(); + {win32, _} -> win32_cases() + end. + +unix_cases() -> + RunErl = filename:join([code:root_dir(),"bin","run_erl"]), + RunErlCases = case filelib:is_file(RunErl) of + true -> [{group, release}]; + false -> [no_run_erl] + end, + [target_system] ++ RunErlCases ++ cases(). + +win32_cases() -> + cases(). + +%% Cases that can be run on all platforms +cases() -> + [otp_2740, otp_2760, otp_5761, instructions, eval_appup]. + +groups() -> + [{release,[], + [ + {group,release_single}, + {group,release_gg} + ]}, + {release_single,[], + [ + upgrade, + client1, + client2 + ]}, + {release_gg,[], + [ + upgrade_gg + ]}]. + +%% {group,release} +%% Top group for all cases using run_erl +init_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + P1gInstall = filename:join(priv_dir(Config),p1g_install), + ok = do_create_p1g(Config,P1gInstall), + ok = create_p1h(Config), + ?t:timetrap_cancel(Dog); + +%% {group,release_single} +%% Subgroup of {group,release}, contains all cases that are not +%% related to global_group +init_per_group(release_single, Config) -> + Dog = ?t:timetrap(?default_timeout), + + %% Create some more releases to upgrade to + ok = create_p1i(Config), + ok = create_p2a(Config), + + ?t:timetrap_cancel(Dog); + +%% {group,release_gg} +%% Subgroup of {group,release}. global_group tests. +init_per_group(release_gg, Config0) -> + Config = [{sname_prefix,release_gg}|Config0], + + PrivDir = priv_dir(Config), + Dog = ?t:timetrap(?default_timeout), + + reg_print_proc(), %% starts a printer process on this node + + Snames = [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + gg_node_snames(Config), + + %% kill all possible nodes which are to be used + ok = stop_nodes([node_name(Sname) || Sname <- Snames]), + + %% For gg1, gg3, gg4 and gg5: create a target system running + %% P1G, and with P1H unpacked. + %% For gg2 and gg6: create a target system running P1H. + %% Use gg2 for unpacking and permanenting P1H. + ok = copy_installed(Config,p1g_install,[Gg2Sname]), + InstallNode = unpack_p1h(Config,Gg2Sname), + ok = copy_installed(Config,Gg2Sname,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname]), + ok = permanent_p1h(InstallNode), + ok = stop_nodes([InstallNode]), + ok = copy_installed(Config,Gg2Sname,[Gg6Sname]), + + %% Replace the sys.config files + %% The reason for not creating the releases with these configs in + %% the first place (create_p1g, create_p1h) is that then the + %% InstallNode (gg2) will be very slow started since it will try + %% to synch with the other nodes in the global group. + %% Also, the rpc call for installing the P1H release (in + %% permanent_p1h/1) would return {rpc,nodedown} due to change of + %% global groups. + lists:foreach( + fun(Sname) -> + ReleasesDir = filename:join([PrivDir,Sname,"releases"]), + write_term_file(filename:join([ReleasesDir,"P1G","sys.config"]), + gg_config([Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname])), + write_term_file(filename:join([ReleasesDir,"P1H","sys.config"]), + gg_config([Gg1Sname,Gg2Sname,Gg4Sname, + Gg5Sname,Gg6Sname])) + end, + Snames), + + ?t:timetrap_cancel(Dog), + [{snames,Snames}|Config]. + + +end_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + stop_print_proc(), + delete_release(Config), + ?t:timetrap_cancel(Dog), + Config; +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Case, Config0) -> + Dog = test_server:timetrap(?default_timeout), + Config = [{sname_prefix,Case},{watchdog, Dog}|Config0], + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ?t:format("~n======= init_per_testcase done =======~n",[]), + Config. + +end_per_testcase(Case, Config) -> + ?t:format("~n======= start end_per_testcase =======~n",[]), + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + + %% DEBUG + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + %% save logs from master and client nodes + PrivDir = priv_dir(Config), + SaveDir = filename:join(PrivDir,save), + FailDir = filename:join(SaveDir,lists:concat(["failed-",Case])), + ok = filelib:ensure_dir(filename:join(FailDir,"*")), + + LogDirs = filelib:wildcard(filename:join([PrivDir,"*",log])), + + lists:foreach( + fun(LogDir) -> + ["log",Sname|_] = lists:reverse(filename:split(LogDir)), + copy_tree(Config,LogDir,Sname,FailDir) + end, + LogDirs), + + case filelib:is_file("sasl_erl_crash.dump") of + true -> + copy_file("sasl_erl_crash.dump",FailDir); + _ -> + ok + end + + end, + %% End DEBUG + + %% Remove any remaining sasl_erl_crash.dump + %% These can occur when a new master@<host> is started, before + %% the old usage of the name is unregistered, causing the node to + %% terminate. (This has no effect on the test case, as the node is + %% immediately restarted by heart and the test cases wait until + %% the node is actually up and running -- see wait_nodes_up/2) + file:delete("sasl_erl_crash.dump"), + + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +gg_node_snames(Config) -> + [tc_sname(Config,X) || X <- [gg1,gg2,gg3,gg4,gg5,gg6]]. + + +%%%----------------------------------------------------------------- +%%% TEST CASES + + +%% Executed instead of release group when no run_erl program exists +no_run_erl(Config) when is_list(Config) -> + {comment, "No run_erl program"}. + + + +%% Test upgrade and downgrade of erts +upgrade(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + ?t:format("upgrade ~p~n",[reg_print_proc]), + PrivDir = priv_dir(Conf), + Sname = tc_sname(Conf), % nodename for use in this testcase + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Sname]), + + %% start the test node + [TestNode] = start_nodes(Conf,[Sname],"upgrade start"), + + %% unpack and install P1H + ok = rpc_inst(TestNode, install_1, [PrivDir]), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_1"), + + %% reinstall P1H and make it permanent + ok = rpc_inst(TestNode, install_2, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_2",[a]), + + %% check that P1H is permanent, unpack and install P1I, unpack and install P2A + TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_3, [PrivDir]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_3a, []), + wait_nodes_up([{TestNode,TestNodeInit1}],"install_3",[a]), + + %% check that P2A is used, reboot from P1I + ok = rpc_inst(TestNode, install_4, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_4",[a]), + + %% check that P1I, reinstall P2A + TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_5, []), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_5a, []), + wait_nodes_up([{TestNode,TestNodeInit2}],"install_5",[a]), + + %% check that P2A is used, make P2A permanent + ok = rpc_inst(TestNode, install_6, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_6",[a]), + + %% check that P2A is permanent, install old P1H + TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_7, []), + wait_nodes_up([{TestNode,TestNodeInit3}],"install_7",[a]), + + %% check that P1H is permanent, remove P1I and P2A + ok = rpc_inst(TestNode, install_8, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_8",[a]), + + %% check that P1H is permanent, reboot old P1G + TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_9, []), + wait_nodes_up([{TestNode,TestNodeInit4}],"install_9"), + + %% check that P1G is permanent, remove P1H + ok = rpc_inst(TestNode, install_10, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_10"), + + %% check that P1G is permanent + ok = rpc_inst(TestNode, install_11, []), + + ok. + +upgrade(cleanup,Config) -> + TestNode = tc_full_node_name(Config), + ok = stop_nodes([TestNode]). + +reboot_and_wait(Node,Tag) -> + reboot_and_wait(Node,Tag,[]). + +reboot_and_wait(Node,Tag,Apps) -> + InitPid = rpc:call(Node,erlang,whereis,[init]), + ok = rpc:call(Node,init,reboot,[]), + wait_nodes_up([{Node,InitPid}],Tag,Apps). + + +%% Test upgrade and downgrade of erts, diskless +client1(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + MasterDir = filename:join(PrivDir,Master), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli1"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client1"), + + ok = rpc_inst(TestNode, client1_1, [PrivDir,MasterDir,Client]), + + ok. + +client1(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test diskless release handling when illegal master node +client2(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli2"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client2"), + + ok = rpc_inst(TestNode, client2, [PrivDir,Client]), + + ok. + +client2(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test instructions _not_ tested by the installer module. +instructions(Conf) when is_list(Conf) -> + DataDir = ?config(data_dir, Conf), + + Dir = filename:join(DataDir, "c"), + true = code:add_path(Dir), + check_bstate("no", []), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("first", []), + FirstBB = whereis(bb), + + case whereis(cc) of + Pid when is_pid(Pid) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Stop and start cc process + S1 = [point_of_no_return, + {stop, [aa]}, + {apply, {?MODULE, no_cc, []}}, + {start, [aa]}], + {ok, _} = release_handler_1:eval_script(S1, [], []), + + case whereis(cc) of + Pid2 when is_pid(Pid2) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Make bb run old version of b. + S2 = [point_of_no_return, + {remove, {b, soft_purge, soft_purge}}], + {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2, [], []), + check_bstate("first", [FirstBB]), + + false = code:is_loaded(b), + {error,{old_processes,b}} = release_handler_1:eval_script(S2,[],[]), + check_bstate("first", [FirstBB]), + + %% Let supervisor restart bb with new code + S3 = [point_of_no_return, + {purge, [b]}], + {ok, []} = release_handler_1:eval_script(S3, [], []), + ok = wait_for(bb), + check_bstate("second", []), + SecondBB = whereis(bb), + + if + SecondBB =:= FirstBB -> ?t:fail("bb not killed"); + true -> ok + end, + + %% Restart bb yet another time + ok = application:stop(c), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("third", []), + ThirdBB = whereis(bb), + + case ThirdBB of + _ when is_pid(ThirdBB) -> ok; + undefined -> ?t:fail("bb not started") + end, + + %% Make bb run old version of b. + %%c:l(b), + check_bstate("third", []), + false = code:purge(b), + check_bstate("third", []), + {module,b} = code:load_file(b), + check_bstate("third", [ThirdBB]), + + %% Let supervisor restart bb yet another time + S4 = [point_of_no_return, + {remove, {b, brutal_purge, soft_purge}}], + {ok, HopefullyEmpty} = release_handler_1:eval_script(S4, [], []), + ok = wait_for(bb), + FourthBB = whereis(bb), + + case HopefullyEmpty of + [{b, soft_purge}] -> + %% The process managed to start between purge and delete + check_bstate("fourth", [FourthBB]); + [] -> + %% The process started after delete + check_bstate("fourth", []) + end, + + application:stop(c), + check_bstate("no", []), + ok. + +instructions(cleanup,Conf) -> + application:stop(c), + really_del_code([aa,b,c_sup]), + code:del_path(filename:join(?config(data_dir,Conf), "c")), + ok. + +really_del_code(Mods) -> + lists:foreach(fun(Mod) -> + code:purge(Mod), % remove old code + code:delete(Mod),% make current code old + code:purge(Mod) % remove old code + end, + Mods). + +check_bstate(Slogan,ExpectedProcs) -> + BB = whereis(bb), + ActualProcs = lists:sort([P || P <- processes(), + erlang:check_process_code(P, b)]), + ExpectedProcs2 = lists:sort(ExpectedProcs), + ?t:format("check_bstate:~n~p~n~p~n", + [{"bb process", Slogan, BB}, + {"Processes running old b code", ActualProcs}]), + if + Slogan =:= "no", BB =/= undefined -> + ?t:fail("instructions failed; process bb is running"); + Slogan =/= "no", BB =:= undefined -> + ?t:fail("instructions failed; process bb is not running"); + ExpectedProcs2 =:= [], ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; old b processes are running"); + ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; wrong number of old b processes are running"); + true -> + ok + end. + +wait_for(Name) -> + case whereis(Name) of + undefined -> + timer:sleep(100), + wait_for(Name); + Pid when is_pid(Pid) -> + ok + end. + +no_cc() -> + case whereis(cc) of + Pid when is_pid(Pid) -> ?t:fail("cc not stopped"); + _ -> ok + end. + + + +%%%----------------------------------------------------------------- +%%% Testing of reported bugs and other tickets. +%%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Ticket: OTP-2740 +%% Slogan: vsn not numeric doesn't work so good in release_handling +%%----------------------------------------------------------------- +%% Test vsn. +otp_2740(Conf) -> + DataDir = ?config(data_dir, Conf), + Dir = filename:join(DataDir, "otp_2740"), + true = code:add_path(Dir), + + {module, vsn_numeric} = c:l(vsn_numeric), + {module, vsn_tuple} = c:l(vsn_tuple), + {module, vsn_list} = c:l(vsn_list), + {module, vsn_atom} = c:l(vsn_atom), + {module, vsn_string} = c:l(vsn_string), + + 231894 = release_handler_1:get_current_vsn(vsn_numeric), + {tuple,["of",terms]} = release_handler_1:get_current_vsn(vsn_tuple), + [list,"of",{some,terms}] = release_handler_1:get_current_vsn(vsn_list), + atom = release_handler_1:get_current_vsn(vsn_atom), + "a string" = release_handler_1:get_current_vsn(vsn_string), + + true = code:del_path(Dir), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2760 +%% Slogan: when an application is removed from a node it is not unloaded +%%----------------------------------------------------------------- +%% Test that when an application is removed from a node it is also unloaded. +otp_2760(Conf) -> + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_2760"), + DataDir = ?config(data_dir,Conf), + LibDir = filename:join([DataDir,app1_app2,lib1]), + + Rel1 = create_and_install_fake_first_release(Dir,[{app1,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir,"after",[],{Rel1,Rel1,[LibDir]}), + Rel2Dir = filename:dirname(Rel2), + + %% Start a node with Rel1.boot and check that the app1 module is loaded + {ok, Node} = t_start_node(otp_2760, Rel1, []), + {file, _} = rpc:call(Node, code, is_loaded, [app1]), + + %% Execute the relup script and check that app1 is unloaded + {ok, [{"after", [{_Rel1Vsn, _Descr, Script}], _}]} = + file:consult(filename:join(Rel2Dir, "relup")), + {ok, []} = rpc:call(Node, release_handler_1, eval_script, + [Script, [], []]), + false = rpc:call(Node, code, is_loaded, [app1]), + + true = stop_node(Node), + ok. + +%% Test upgrade using other filesystem than the defined in OTP and +%% option {update_paths, true} +otp_5761(Conf) when is_list(Conf) -> + + %% In the following test case, the release upgrade is somewhat + %% simplified (since it is not this procedure in itself we want to + %% test, but that application code directories are set correctly.) + %% Existing Erlang release is used as base, instead of creating + %% a new one. + + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_5761"), + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + LibDir1 = filename:join(RelDir, "lib1"), + LibDir2 = filename:join(RelDir, "lib2"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{app1,"1.0",LibDir1}, + {app2,"1.0",LibDir1}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{app1,"2.0",LibDir2}, + {app2,"1.0",LibDir2}], + {Rel1,Rel1,[LibDir1]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_5761, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Bind some variable names that will be used in patternmatching below + App11Dir = filename:join([LibDir1, "app1-1.0"]), + App12Dir = filename:join([LibDir2, "app1-2.0"]), + App2aDir = filename:join([LibDir1, "app2-1.0"]), + App2bDir = filename:join([LibDir2, "app2-1.0"]), + + %% Make sure correct code paths are used + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Unpack rel2 (make sure it does not work if an AppDir is bad) + LibDir3 = filename:join(RelDir, "lib3"), + {error, {no_such_directory, _}} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]), + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "relup")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "sys.config")]), + + %% Install RelVsn2 without {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn1]), + + %% Install RelVsn2 with {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn2, [{update_paths, true}]]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2bDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn1, [{update_paths, true}]]), + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Stop the slave node + true = stop_node(Node), + ok. + +%% Test upgrade and downgrade of applications +eval_appup(Conf) when is_list(Conf) -> + + %% OTP-6162 + %% Create an ETS table which is updated by app1 if there is any + %% change made to the application configuration parameter 'var' + %% (see config_change/3 in myrel/lib1|2/app1-1|2.0/src/app1.erl) + ets:new(otp_6162, [set, public, named_table]), + + %% Set some paths + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + App11Dir = filename:join([RelDir, "lib1", "app1-1.0"]), + App12Dir = filename:join([RelDir, "lib2", "app1-2.0"]), + EbinDir = filename:join(App11Dir, "ebin"), + + %% Start app1-1.0 + code:add_patha(EbinDir), + ok = application:start(app1), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% Upgrade to app1-2.0 + {ok, []} = release_handler:upgrade_app(app1, App12Dir), + App12Dir = code:lib_dir(app1), + error = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val2' + %% (see myrel/lib2/app1-2.0/ebin/app1.app) + [{var,val2}] = ets:lookup(otp_6162, var), + + %% Downgrade to app1-1.0 + {ok, []} = release_handler:downgrade_app(app1,"1.0",App11Dir), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val1' + %% (see myrel/lib1/app1-1.0/ebin/app1.app) + [{var,val1}] = ets:lookup(otp_6162, var), + + ok = application:stop(app1), + ok = application:unload(app1), + + true = code:del_path(EbinDir), + ok. + + +%% Test the example/target_system.erl module +target_system(Conf) when is_list(Conf) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + TargetCreateDir = filename:join([PrivDir,"target_system","create"]), + TargetInstallDir = filename:join([PrivDir,"target_system","install"]), + + ok = filelib:ensure_dir(filename:join(TargetCreateDir,"xx")), + ok = filelib:ensure_dir(filename:join(TargetInstallDir,"xx")), + + + %% Create the .rel file + ErtsVsn = erlang:system_info(version), + RelName = filename:join(TargetCreateDir,"ts-1.0"), + RelFile = RelName++".rel", + RelVsn = "R1A", + create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,[{a, "1.0"}]), + + %% Build the target_system module + ExamplesEbin = filename:join([code:lib_dir(sasl),examples,ebin]), + TSPath = + case filelib:is_file(filename:join(ExamplesEbin,"target_system.beam")) of + true -> + ExamplesEbin; + false -> + {ok,_} = + compile:file(filename:join(DataDir,"target_system.erl"), + [{outdir,TargetCreateDir}]), + TargetCreateDir + end, + code:add_path(TSPath), + + %% Create the release + target_system:create(RelName,[{path,[filename:join([DataDir, + lib, + "a-1.0", + ebin])]}]), + + %% Install the release + target_system:install(RelName,TargetInstallDir), + + code:del_path(TSPath), + + %% Check that all files exist in installation + true = filelib:is_dir(filename:join(TargetInstallDir,"erts-"++ErtsVsn)), + LibDir = filename:join(TargetInstallDir,lib), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + true = filelib:is_dir(filename:join(LibDir,"kernel-"++KernelVsn)), + true = filelib:is_dir(filename:join(LibDir,"stdlib-"++StdlibVsn)), + true = filelib:is_dir(filename:join(LibDir,"sasl-"++SaslVsn)), + true = filelib:is_dir(filename:join(LibDir,"a-1.0")), + RelDir = filename:join(TargetInstallDir,releases), + true = filelib:is_regular(filename:join(RelDir,"RELEASES")), + true = filelib:is_regular(filename:join(RelDir,"start_erl.data")), + true = filelib:is_regular(filename:join(RelDir, + filename:basename(RelFile))), + true = filelib:is_dir(filename:join(RelDir,RelVsn)), + true = filelib:is_regular(filename:join([RelDir,RelVsn,"start.boot"])), + BinDir = filename:join(TargetInstallDir,bin), + true = filelib:is_regular(filename:join(BinDir,"start.boot")), + true = filelib:is_regular(filename:join(BinDir,erl)), + true = filelib:is_regular(filename:join(BinDir,start_erl)), + true = filelib:is_regular(filename:join(BinDir,start)), + true = filelib:is_regular(filename:join(BinDir,epmd)), + true = filelib:is_regular(filename:join(BinDir,run_erl)), + true = filelib:is_regular(filename:join(BinDir,to_erl)), + + %% Check content of files + {ok,SED} = file:read_file(filename:join(RelDir,"start_erl.data")), + [ErtsVsn,RelVsn] = string:tokens(binary_to_list(SED),"\s\n"), + ok. + + + +%%%================================================================= +%%% Testing global groups. +%%%================================================================= + +%% This test case involves P1G and P1H with the sys.config as +%% specified in gg_config/1. The test case checks that the global +%% group information is correct before and after the upgrade and also +%% after terminating one of the nodes. The flow is as follows: +%% 1. Start all four nodes of global group gg1 with P1G +%% 2. Terminate one of the nodes, and upgrade the others to P1H. P1H +%% config adds to more nodes to the global group. +%% 3. Start the two remaining nodes with P1H +upgrade_gg(Conf) -> + [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + ?config(snames,Conf), + + %% start gg1, gg3, gg4, gg5 and check that global group info is ok + Nodes1 = [Gg1,Gg3,Gg4,Gg5] = + start_nodes(Conf,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname],"upgrade_gg"), + + %% Give some time to synch nodes, then check global group info. + timer:sleep(1000), + [check_gg_info(Node,Nodes1,[],Nodes1--[Node]) || Node <- Nodes1], + + %% register a process on each of the nodes + ok = rpc:call(Gg1, installer, reg_proc, [reg1]), + ok = rpc:call(Gg3, installer, reg_proc, [reg3]), + ok = rpc:call(Gg4, installer, reg_proc, [reg4]), + ok = rpc:call(Gg5, installer, reg_proc, [reg5]), + are_names_reg_gg(Gg1, [reg1, reg3, reg4, reg5]), + + %% Stop gg3, then upgrade gg1, gg4 and gg5 to P1H + ok = stop_nodes([Gg3]), + + ok = install_release_changed_gg(Gg1,"P1H"), + ok = install_release_changed_gg(Gg4,"P1H"), + ok = install_release_changed_gg(Gg5,"P1H"), + + %% Check global group info + Gg2 = node_name(Gg2Sname), + Gg6 = node_name(Gg6Sname), + Nodes2 = [Gg1,Gg4,Gg5], + [check_gg_info(Node,Nodes2,[Gg2,Gg6],Nodes2--[Node]) || Node <- Nodes2], + + %% start gg2 and gg6 + [Gg2,Gg6] = start_nodes(Conf,[Gg2Sname,Gg6Sname],"upgrade_gg start gg2/gg6"), + + %% reg proc on each of the nodes + ok = rpc:call(Gg2, installer, reg_proc, [reg2]), + ok = rpc:call(Gg6, installer, reg_proc, [reg6]), + are_names_reg_gg(Gg1, [reg1, reg2, reg4, reg5, reg6]), + + %% Check global group info + Nodes3 = [Gg1,Gg2,Gg4,Gg5,Gg6], + [check_gg_info(Node,Nodes3,[],Nodes3--[Node]) || Node <- Nodes3], + + ok. + +upgrade_gg(cleanup,Config) -> + Snames = ?config(snames,Config), + NodeNames = [node_name(Sname) || Sname <- Snames], + ok = stop_nodes(NodeNames). + + + + +%%%================================================================= +%%% Misceleaneous functions +%%%================================================================= +stop_nodes(Nodes) -> + ?t:format("Stopping nodes: ~p~n",[Nodes]), + Running = + lists:foldl(fun(Node,Acc) -> + Now = now(), + stop_cover(Node), + case rpc:call(Node,installer,stop,[Now]) of + {badrpc,nodedown} -> + Acc; + Other -> + ?t:format("Stop ~p(~p): ~p~n", + [Node,Now,Other]), + [Node|Acc] + end + end, [], Nodes), + wait_nodes_down(Running). + + +wait_nodes_down(Nodes) -> + ?t:format( "wait_nodes_down ~p:",[Nodes]), + wait_nodes_down(Nodes, 30). + +wait_nodes_down(Nodes, 0) -> + test_server:fail({error, {"could not kill nodes", Nodes}}); +wait_nodes_down(Nodes, N) -> + Fun = fun(Node, A) -> + case net_adm:ping(Node) of + pong -> + ?t:format( " net_adm:ping(~p) = pong", [Node]), + [Node|A]; + pang -> + ?t:format( " net_adm:ping(~p) = pang", [Node]), + A + end + end, + Pang = lists:foldl(Fun, [], Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_down(Pang, N-1) + end. + + + +wait_nodes_up(Nodes, Tag) -> + wait_nodes_up(Nodes, Tag, []). + +wait_nodes_up(Nodes0, Tag, Apps) -> + ?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]), + Nodes = fix_nodes(Nodes0), + wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30). + +fix_nodes([{Node,InitPid}|Nodes]) -> + [{Node,InitPid} | fix_nodes(Nodes)]; +fix_nodes([Node|Nodes]) -> + [{Node,fake_init_pid} | fix_nodes(Nodes)]; +fix_nodes([]) -> + []. + +wait_nodes_up(Nodes, Tag, Apps, 0) -> + test_server:fail({error, {"nodes not started", Nodes, Tag, Apps}}); +wait_nodes_up(Nodes, Tag, Apps, N) -> + Fun = + fun(NodeInfo={Node,OldInitPid}, A) -> + case rpc:call(Node, application, which_applications, []) of + {badrpc, nodedown} -> + ?t:format( " ~p = {badarg, nodedown}",[Node]), + [NodeInfo | A]; + List when is_list(List)-> + ?t:format( " ~p = [~p]",[Node, List]), + case lists:all(fun(App) -> + lists:keymember(App,1,List) + end, Apps) of + true -> + case rpc:call(Node,erlang,whereis,[init]) of + OldInitPid -> + [NodeInfo | A]; + _ -> + start_cover(Node), + A + end; + false -> + [NodeInfo | A] + end + end + end, + Pang = lists:foldl(Fun,[],Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_up(Pang, Tag, Apps, N-1) + end. + + + + +are_names_reg_gg(Node, Names) -> + ?t:format( "are_names_reg_gg ~p~n",[Names]), + are_names_reg_gg(Node, Names, 30). + +are_names_reg_gg(Node, Names, N) -> + case lists:sort(rpc:call(Node, global, registered_names, [])) of + Names -> + ok; + Regs when N > 0 -> + timer:sleep(1000), + ?t:format( "are_names_reg_gg Regs ~p~n",[Regs]), + are_names_reg_gg(Node, Names, N-1); + Regs -> + ?t:fail({error, {"Names not registered", + {{"should :", Names}, + {"was :", Regs}}}}) + end. + + + +t_start_node(Name, Boot, SysConfig) -> + Args = + case Boot of + [] -> []; + _ -> " -boot " ++ Boot + end ++ + case SysConfig of + [] -> []; + _ -> " -config " ++ SysConfig + end, + test_server:start_node(Name, slave, [{args, Args}]). + +stop_node(Node) -> + ?t:stop_node(Node). + + +copy_client(Conf,Master,Sname,StartScript) -> + io:format("copy_client(Conf)"), + + DataDir = ?config(data_dir, Conf), + MasterDir = filename:join(priv_dir(Conf),Master), + + {ok,Host} = inet:gethostname(), + {ok,IpTuple} = inet:getaddr(Host,inet), + IpAddr = inet_parse:ntoa(IpTuple), + + CliNode = node_name(Sname), + + Cli = filename:join([MasterDir, "clients", "type1", CliNode]), + ok = filelib:ensure_dir(filename:join([Cli,"bin","."])), + ok = filelib:ensure_dir(filename:join([Cli,"releases","."])), + ok = filelib:ensure_dir(filename:join([Cli,"log","."])), + + P1GOrig = filename:join([MasterDir, "releases", "P1G"]), + ok = copy_tree(Conf,P1GOrig,filename:join(Cli,"releases")), + + ok = subst_file(filename:join([DataDir, "clients", StartScript]), + filename:join([Cli,"bin","start"]), + [{"ROOT",MasterDir}, + {"MASTER",atom_to_list(Master)}, + {"IPADDR",IpAddr}], + [{chmod,8#0755}]), + + StartErlData = filename:join([MasterDir, "releases", "start_erl.data"]), + CliRelDir = filename:join([Cli, "releases"]), + copy_file(StartErlData, CliRelDir), + + RR = filename:join([MasterDir, "releases", "RELEASES"]), + copy_file(RR, CliRelDir), + + ok. + + +delete_release(Conf) -> + PrivDir = priv_dir(Conf), + + {ok, OrigWd} = file:get_cwd(), + + ok = file:set_cwd(PrivDir), + ?t:format("======== current dir ~p~n",[PrivDir]), + {ok, Dirs} = file:list_dir(PrivDir), + ?t:format("======== deleting ~p~n",[Dirs]), + + ok = delete_release_os(Dirs), + ?t:format("======== remaining ~p~n",[file:list_dir(PrivDir)]), + ok = file:set_cwd(OrigWd), + ok. + + +delete_release_os(Dirs) -> + case os:type() of + {unix, _} -> + delete_release_unix(Dirs); + {win32, _} -> + delete_release_win32(Dirs); + Os -> + test_server:fail({error, {not_yet_implemented_os, Os}}) + end. + + +delete_release_unix([]) -> + ok; +delete_release_unix(["save"|Dirs]) -> + delete_release_unix(Dirs); +delete_release_unix([Dir|Dirs]) -> + Rm = string:concat("rm -rf ", Dir), + ?t:format("============== COMMAND ~p~n",[Rm]), + case file:list_dir(Dir) of + {error, enotdir} -> + ok; + X -> + ?t:format("------- Dir ~p~n ~p~n",[Dir, X]) + end, + case os:cmd(Rm) of + [] -> + ?t:format("------- Result of COMMAND ~p~n",[ok]); + Y -> + ?t:format("!!!!!!! delete ERROR Dir ~p Error ~p~n",[Dir, Y]), + ?t:format("------- ls -al ~p~n",[os:cmd("ls -al " ++ Dir)]) + end, + + delete_release_unix(Dirs). + +delete_release_win32([]) -> + ok; +delete_release_win32(["save"|Dirs]) -> + delete_release_win32(Dirs); +delete_release_win32([Dir|Dirs]) -> + Rm = string:concat("rmdir /s ", Dir), + [] = os:cmd(Rm), + delete_release_win32(Dirs). + + +node_name(Sname) when is_atom(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). +copy_file(Src, Dest, Opts) -> + case file:copy(Src,Dest) of + {ok,_} -> + preserve(Src,Dest,Opts), + chmod(Dest,Opts), + ok; + {error,eisdir} -> + NewDest = filename:join(Dest, filename:basename(Src)), + case file:copy(Src,NewDest) of + {ok,_} -> + preserve(Src,NewDest,Opts), + chmod(NewDest,Opts); + {error,Reason} -> + copy_error(Src,Dest,Reason) + end; + {error,Reason} -> + copy_error(Src,Dest,Reason) + end. + +preserve(Src,Dest,Opts) -> + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + ok = file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +chmod(Dest,Opts) -> + case lists:keyfind(chmod,1,Opts) of + {chmod,Mode} -> + ok = file:change_mode(Dest, Mode); + false -> + ok + end. + + + +copy_error(Src, Dest, Reason) -> + io:format("Copy ~s to ~s failed: ~s\n", + [Src,Dest,file:format_error(Reason)]), + ?t:fail(file_copy_failed). + +copy_tree(Conf, Src, DestDir) -> + case catch copy_tree(Conf, Src, filename:basename(Src), DestDir) of + ok -> + ok; + {'EXIT', {{badmatch,Error},_Stack}} -> + %% Most probably, an erl_tar call has failed. + %% Known to happen on some platforms (symbolic_link_too_long) + Error; + {'EXIT', Reason} -> + {error, Reason} + end. + +copy_tree(Conf, Src, NewName, DestDir) -> + PrivDir = priv_dir(Conf), + TempTarName = filename:join(PrivDir, "temp_tar_file.tar"), + %% Not compressing tar file here since that would increase test + %% suite time by almost 100%, and the tar file is deleted + %% imediately anyway. + {ok,Tar} = erl_tar:open(TempTarName, [write]), + ok = erl_tar:add(Tar, Src, NewName, []), + ok = erl_tar:close(Tar), + ok = erl_tar:extract(TempTarName, [{cwd,DestDir}]), + ok = file:delete(TempTarName), + ok. + +%% subst_file(Src, Dest, Vars) +%% Src = Dest = string(), filename and path +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Src, using the list +%% of variables in Vars. Result is written to Dest. +%% +subst_file(Src, Dest, Vars) -> + subst_file(Src, Dest, Vars, []). +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + ok = file:write_file(Dest, NConts), + preserve(Src,Dest,Opts), + chmod(Dest,Opts). + +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +priv_dir(Conf) -> + filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash + +latest_version(Dir) -> + List = filelib:wildcard(Dir ++ "*"), + lists:last(lists:sort(List)). + +%% A printer process which receives messages from other nodes and +%% prints in the log +reg_print_proc() -> + catch unregister(rh_print), + Pid = spawn_link(?MODULE, rh_print, []), + register(rh_print, Pid), + ok. + +rh_print() -> + receive + {print, {Module,Line}, [H|T]} -> + ?t:format("=== ~p:~p - ~p",[Module,Line,H]), + lists:foreach(fun(Term) -> ?t:format(" ~p",[Term]) end, T), + ?t:format("",[]), + rh_print(); + kill -> + exit(normal) + end. + +stop_print_proc() -> + case whereis(rh_print) of %%removes the printer process + undefined -> + ok; + Pid when is_pid(Pid) -> + rh_print ! kill + end. + +%% Create the first target release, vsn P1G. This release is used for +%% all test cases in {group,release} +create_p1g(Conf,Sname) -> + do_create_p1g(Conf,filename:join(priv_dir(Conf),Sname)). + +do_create_p1g(Conf,TargetDir) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + ErtsVsn = "4.4", + ErtsDir = "erts-"++ErtsVsn, + + %% Create dirs + BinDir = filename:join(TargetDir,bin), + ReleasesDir = filename:join(TargetDir,releases), + LogDir = filename:join(TargetDir,log), + ok = filelib:ensure_dir(filename:join(BinDir,"*")), + ok = filelib:ensure_dir(filename:join(ReleasesDir,"*")), + ok = filelib:ensure_dir(filename:join(LogDir,"*")), + + %% Copy stuff + ErtsLatest = latest_version(filename:join(code:root_dir(),"erts")), + ok = copy_tree(Conf, ErtsLatest, ErtsDir, TargetDir), + ErtsBinDir = filename:join([TargetDir,ErtsDir,bin]), + copy_file(filename:join([ErtsBinDir, "epmd"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), BinDir, [preserve]), + + copy_file(filename:join(DataDir, "../installer.beam"), + filename:join([DataDir,lib,"installer-1.0",ebin])), + + %% Create .rel, .script and .boot files + RelName = "rel0", + RelVsn = "P1G", + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + TarFile = create_basic_release(RelFile, RelVsn, {ErtsVsn,false}, + LibPath, [], [], [], []), + + %% Extract tar file in target directory (i.e. same directory as erts etc.) + ok = erl_tar:extract(TarFile, [{cwd, TargetDir}, compressed]), + + %% Create start_erl.data + StartErlDataFile = filename:join([ReleasesDir, "start_erl.data"]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + ok = file:write_file(StartErlDataFile, StartErlData), + + %% Create RELEASES + ok = release_handler:create_RELEASES(TargetDir,ReleasesDir,RelFile,[]), + + %% Create start_erl + ok = subst_file(filename:join([ErtsBinDir,"start_erl.src"]), + filename:join([BinDir,"start_erl"]), + [{"EMU","beam"}], + [{chmod,8#0755}]), + + %% Create start script + %% Using a customized start script from DataDir where some options + %% (heart and nodename) are added compared to the start.src in the + %% erlang distribution. + ok = subst_file(filename:join(DataDir, "start"), + filename:join([BinDir, "start"]), + [{"ROOT",TargetDir}], + [preserve]), + ok. + +%% Create version P1H - which is P1G + a-1.0 +%% Must have run create_p1g first!! +create_p1h(Conf) -> + create_upgrade_release(Conf,"rel1","P1H",{"4.4",false},[{a,"1.0"}], + [{a,[{key2,val2}]}],{"rel0",[new_appl]}). + +%% Create version P1I - which is P1H, but with application a upgraded to a-1.1 +%% Must have run create_p1h first!! +create_p1i(Conf) -> + create_upgrade_release(Conf,"rel2","P1I",{"4.4",false},[{a,"1.1"}], + [{a,[{key2,newval2}]}], + {"rel1",[{extra,gott}]}). + +%% Create version P2A - which is P1I, but with erts-<latest> +%% Must have run create_p1i first!! +create_p2a(Conf) -> + ErtsVsn = erlang:system_info(version), + create_upgrade_release(Conf,"rel3","P2A",{ErtsVsn,code:root_dir()}, + [{a,"1.1"}],[{a,[{key2,newval2}]}], + {"rel2",[new_emu]}). + +%% Create a release tar package which can be installed on top of P1G +create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}], + + create_basic_release(RelFile, RelVsn, Erts, LibPath, + Apps, Config, UpFrom, []), + ok. + +%% Create .rel, .script, .boot, sys.config and tar +create_basic_release(RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) -> + RelDir = filename:dirname(RelFile), + RelFileName = filename:rootname(RelFile), + + %% Create .rel file + create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps), + + %% Generate .script and .boot + ok = systools:make_script(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir}]), + + %% Generate relup + ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,[LibPath]}, + {outdir,RelDir}]), + + %% Create sys.config + ok = write_term_file(filename:join(RelDir,"sys.config"),Config), + + + %% Create tar file (i.e. collect all lib/app-*/* and system files) + ok = systools:make_tar(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir} | + case ErtsDir of + false -> []; + _ -> [{erts,ErtsDir}] + end]), + + RelFileName ++ ".tar.gz". + +%% Create a .rel file +create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) -> + create_rel_file(RelFile,"SASL-test",RelVsn,ErtsVsn, + [{installer,"1.0"}|ExtraApps]). + +create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,ExtraApps) -> + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + application:load(tools), + {ok,ToolsVsn} = application:get_key(tools,vsn), + application:load(runtime_tools), + {ok,RuntimeToolsVsn} = application:get_key(runtime_tools,vsn), + + RelFileContent = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, + {stdlib, StdlibVsn}, + {sasl, SaslVsn}, + {runtime_tools, RuntimeToolsVsn}, + {tools, ToolsVsn} | + ExtraApps]}, + ok = write_term_file(RelFile,RelFileContent). + +%% Insert a term in a file, which can be read with file:consult/1. +write_term_file(File,Term) -> + ok = file:write_file(File,io_lib:format("~p.~n",[Term])). + + +%% Check that global group info is correct +check_gg_info(Node,OtherAlive,OtherDead,Synced) -> + GGI = rpc:call(Node, global_group, info, []), + GI = rpc:call(Node, global, info,[]), + try do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) + catch _:E -> + ?t:format("~ncheck_gg_info failed for ~p: ~p~nwhen GGI was: ~p~n" + "and GI was: ~p~n", + [Node,E,GGI,GI]), + ?t:fail("check_gg_info failed") + end. + +do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) -> + {_,gg1} = lists:keyfind(own_group_name,1,GGI), + {_,synced} = lists:keyfind(state,1,GGI), + {_,AllNodes} = lists:keyfind(own_group_nodes,1,GGI), + true = lists:sort(AllNodes) =:= lists:sort(OtherAlive++OtherDead), + {_,[]} = lists:keyfind(sync_error,1,GGI), + {_,[{gg2,[_,_]}]} = lists:keyfind(other_groups,1,GGI), + + %% There is a known bug in global_group (OTP-9177) which causes + %% the following to fail every now and then: + %% {_,SyncedNodes} = lists:keyfind(synced_nodes,1,GGI), + %% true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + %% {_,NoContact} = lists:keyfind(no_contact,1,GGI), + %% true = lists:sort(NoContact) =:= lists:sort(OtherDead), + + %% Therefore we use global:info instead for this part + {state,_,_,SyncedNodes,_,_,_,_,_,_,_} = GI, + true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + + %% .. and we only check that all OtherDead are listed as + %% no_contact (due to th bug there might be more nodes in this + %% list) + {_,NoContact} = lists:keyfind(no_contact,1,GGI), + true = + lists:sort(OtherDead) =:= + lists:sort([NC || NC <- NoContact,lists:member(NC,OtherDead)]), + + ok. + +%% Return the configuration (to be inserted in sys.config) for global group tests +gg_config(Snames) -> + Nodes = [node_name(Sname) || Sname <- Snames], + [{kernel, [{sync_nodes_optional, Nodes}, + {sync_nodes_timeout, 10000}, + {global_groups, + [{gg1, Nodes}, + {gg2, [node_name(Sname) || Sname <- [ggq,ggw]]}]}]}, + {a, [{key2, val2}]}]. + +%% Start a node with short name SnameStr, and unpack P1H +unpack_p1h(Conf,Sname) -> + PrivDir = priv_dir(Conf), + [Node] = start_nodes(Conf,[Sname],"create_p1h"), + ok = rpc_inst(Node, unpack_p1h, [PrivDir]), + Node. + +%% On the given node, install P1H and make it permanent +%% This function is to be called after unpack_p1h/2, with the same node. +permanent_p1h(Node) -> + ok = rpc_inst(Node, permanent_p1h, []). + +%% For each node in ToNodes, create a target installation which is +%% indentical to the target installation for FromNode. +copy_installed(Conf,FromNode,ToNodes) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + lists:foreach( + fun(Node) -> + ok = copy_tree(Conf,filename:join(PrivDir,FromNode),Node,PrivDir), + NodeDir = filename:join(PrivDir,Node), + ok = subst_file(filename:join(DataDir, "start"), + filename:join([NodeDir, "bin", "start"]), + [{"ROOT",NodeDir}]), + LogDir = filename:join(NodeDir,log), + {ok,Logs} = file:list_dir(LogDir), + lists:foreach(fun(Log) -> + file:delete(filename:join(LogDir,Log)) + end, + Logs) + end, + ToNodes). + +start_nodes(Conf,Snames,Tag) -> + PrivDir = priv_dir(Conf), + Nodes = + lists:map( + fun(Sname) -> + NodeDir = filename:join(PrivDir,Sname), + Node = node_name(Sname), + + Script = filename:join([NodeDir,"bin","start"]), + Cmd = "env NODENAME="++atom_to_list(Sname) ++ " " ++ Script, + %% {ok,StartFile} = file:read_file(Cmd), + %% io:format("~s:\n~s~n~n",[Start,binary_to_list(StartFile)]), + Res = os:cmd(Cmd), + io:format("Start ~p: ~p~n=>\t~p~n", [Sname,Cmd,Res]), + Node + end, + Snames), + wait_nodes_up(Nodes,Tag), + Nodes. + +tc_sname(Config) -> + tc_sname(Config,""). +tc_sname(Config,Fix) when is_atom(Fix) -> + tc_sname(Config,atom_to_list(Fix)); +tc_sname(Config,Fix) when is_list(Fix) -> + list_to_atom( + atom_to_list(?MODULE) + ++ "-" ++ atom_to_list(?config(sname_prefix, Config)) ++ + case Fix of + "" -> ""; + _ -> "-" ++ Fix + end). + +tc_full_node_name(Config) -> + tc_full_node_name(Config,""). +tc_full_node_name(Config,Fix) -> + node_name(tc_sname(Config,Fix)). + + +%% When installing a release for which the sys.config includes added +%% or changed global group(s), this node (test_sever@host) will be +%% disconnected from the test node (Node) by global_group.erl. This +%% will cause the rpc:call to terminate with {badrpc,nodedown} even if +%% the installation succeeds. This function installs the release, +%% accepts the faulty return value and then checks if the release was +%% successfully installed. +install_release_changed_gg(Node,RelVsn) -> + stop_cover(Node), + {badrpc,nodedown} = rpc:call(Node,release_handler,install_release,[RelVsn]), + timer:sleep(100), + wait_installed(Node,RelVsn,4). + +wait_installed(Node,RelVsn,0) -> + ?t:fail("install_release_changed_gg failed for " ++ RelVsn ++ + " on " ++ atom_to_list(Node)); +wait_installed(Node,RelVsn,N) -> + Rels = rpc:call(Node,release_handler,which_releases,[]), + case lists:keyfind(RelVsn, 2, Rels) of + {"SASL-test", RelVsn, _Libs, current} -> + start_cover(Node), + ok; + _ -> + timer:sleep(500), + wait_installed(Node,RelVsn,N-1) + end. + +%% Start/stop cover measurements on the given node +start_cover(Node) -> + cover_fun(Node,start). +stop_cover(Node) -> + cover_fun(Node,stop). + +cover_fun(Node,Func) -> + case ?t:is_cover() of + true -> + cover:Func(Node); + false -> + ok + end. + +%%%----------------------------------------------------------------- +%%% Create a fake release .... + +%% This function will create and install a release build on the +%% current running OTP release. It includes kernel, stdlib and sasl, +%% and possibly other applications if they are listed in AppDirs = +%% [{App,Vsn,LibDir}] +create_and_install_fake_first_release(Dir,AppDirs) -> + %% Create the first release + {RelName,RelVsn} = init:script_id(), + {Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + ReleasesDir = filename:join(Dir, "releases"), + RelDir = filename:dirname(Rel), + + %% And install it + RelVsnDir = filename:join(ReleasesDir, RelVsn), + ok = filelib:ensure_dir(filename:join(RelVsnDir,"*")), + + ok = copy_file(Rel++".rel",RelVsnDir), + ok = copy_file(Rel++".boot",filename:join(RelVsnDir, "start.boot")), + ok = copy_file(filename:join(RelDir,"sys.config"),RelVsnDir), + + ok = release_handler:create_RELEASES(code:root_dir(), + ReleasesDir, + Rel++".rel", + AppDirs), + + Rel. + +%% This function create a new release, including a relup file. It can +%% be upgraded to from the release created by +%% create_and_install_fake_first_release/2. Unpack first by calls to +%% release_handler:set_unpacked and release_handler:install_file. +create_fake_upgrade_release(Dir,RelVsn,AppDirs,{UpFrom,DownTo,ExtraLibs}) -> + %% Create a new release + {RelName,_} = init:script_id(), + {Rel,Paths} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + RelDir = filename:dirname(Rel), + + %% And a relup file so it can be upgraded to + RelupPath = Paths ++ [filename:join([Lib,"*","ebin"]) || Lib <- ExtraLibs], + ok = systools:make_relup(Rel,[UpFrom],[DownTo], + [{path,RelupPath}, + {outdir,RelDir}]), + + Rel. + + +create_fake_release(Dir,RelName,RelVsn,AppDirs) -> + %% Create .rel files + RelDir = filename:join(Dir,"rel_" ++ RelVsn), + Rel = filename:join([RelDir,"rel_" ++ RelVsn]), + ok = filelib:ensure_dir(Rel), + ErtsVsn = erlang:system_info(version), + + {Apps,Paths} = + lists:foldl(fun({App,Vsn,Lib},{As,Ps}) -> + {[{App,Vsn}|As], + lists:umerge([filename:join([Lib,"*",ebin])],Ps)} + end, + {[],[]}, + AppDirs), + + create_rel_file(Rel++".rel",RelName,RelVsn,ErtsVsn,Apps), + + %% Generate boot scripts + ok = systools:make_script(Rel,[local, + {path, Paths}, + {outdir,RelDir}]), + ok = copy_file(Rel++".boot", filename:join(RelDir,"start.boot")), + + %% Use an own 'releases' directory - we don't want to change the + %% contents of $OTP_ROOT/releases + %% Inform SASL about this via sys.config + ReleasesDir = filename:join(Dir, "releases"), + Config = [{sasl,[{releases_dir,ReleasesDir}]}], + ok = write_term_file(filename:join(RelDir,"sys.config"), Config), + + {Rel,Paths}. + + +rpc_inst(Node,Func,Args) -> + rpc:call(Node,installer,Func,[node()|Args]). diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src new file mode 100644 index 0000000000..85e25fdc2f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -0,0 +1,108 @@ +EFLAGS=+debug_info + +P2B= \ + P2B/a-2.0/ebin/a.beam \ + P2B/a-2.0/ebin/a_sup.beam + +LIB= \ + lib/a-1.1/ebin/a.beam \ + lib/a-1.1/ebin/a_sup.beam \ + lib/a-1.0/ebin/a.beam \ + lib/a-1.0/ebin/a_sup.beam \ + +APP= \ + app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@ + +OTP2740= \ + otp_2740/vsn_atom.@EMULATOR@ \ + otp_2740/vsn_list.@EMULATOR@ \ + otp_2740/vsn_numeric.@EMULATOR@ \ + otp_2740/vsn_tuple.@EMULATOR@ \ + otp_2740/vsn_string.@EMULATOR@ + +C= \ + c/aa.@EMULATOR@ \ + c/b.@EMULATOR@ \ + c/c_sup.@EMULATOR@ + + +all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) + +P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl +P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl + + +lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl +lib/a-1.0/ebin/a_sup.@EMULATOR@: lib/a-1.0/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a_sup.erl + + +lib/a-1.1/ebin/a.@EMULATOR@: lib/a-1.1/src/a.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a.erl +lib/a-1.1/ebin/a_sup.@EMULATOR@: lib/a-1.1/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a_sup.erl + + +app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_sup.erl +app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_server.erl +app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1.erl + + +app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_sup.erl +app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_server.erl +app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2.erl + + +app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_sup.erl +app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_server.erl +app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1.erl + + +app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_sup.erl +app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_server.erl +app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2.erl + + +otp_2740/vsn_atom.@EMULATOR@: otp_2740/vsn_atom.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_atom.erl +otp_2740/vsn_list.@EMULATOR@: otp_2740/vsn_list.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_list.erl +otp_2740/vsn_numeric.@EMULATOR@: otp_2740/vsn_numeric.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_numeric.erl +otp_2740/vsn_tuple.@EMULATOR@: otp_2740/vsn_tuple.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_tuple.erl +otp_2740/vsn_string.@EMULATOR@: otp_2740/vsn_string.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_string.erl + +c/aa.@EMULATOR@: c/aa.erl + erlc $(EFLAGS) -oc c/aa.erl +c/b.@EMULATOR@: c/b.erl + erlc $(EFLAGS) -oc c/b.erl +c/c_sup.@EMULATOR@: c/c_sup.erl + erlc $(EFLAGS) -oc c/c_sup.erl diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app new file mode 100644 index 0000000000..200cfcfe47 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "2.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl new file mode 100644 index 0000000000..cfe38b55ce --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl @@ -0,0 +1,47 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app new file mode 100644 index 0000000000..0489cb2595 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "1.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val1}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl new file mode 100644 index 0000000000..9b49e772cc --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl @@ -0,0 +1,32 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app new file mode 100644 index 0000000000..3c65adfbb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "2.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val2}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup new file mode 100644 index 0000000000..e5e0cbda0e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup @@ -0,0 +1,4 @@ +{"2.0", + [{"1.0", [{load_module, app1_server}]}], + [{"1.0", [{load_module, app1_server}]}] +}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl new file mode 100644 index 0000000000..660d095ebf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl @@ -0,0 +1,35 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(error, _From, State) -> + Reply = error, + {reply, Reply, State}; +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/aa.erl b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl new file mode 100644 index 0000000000..1c853c85b2 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl @@ -0,0 +1,41 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(aa). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/1, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, cc}, aa, [], []). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/b.erl b/lib/sasl/test/release_handler_SUITE_data/c/b.erl new file mode 100644 index 0000000000..d8426a515e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/b.erl @@ -0,0 +1,38 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(b). + + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/0]). + +start_link() -> {ok, proc_lib:spawn_link(b, init, [])}. + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init() -> + register(bb, self()), + loop(). + +loop() -> + receive + hej -> ok + end. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app new file mode 100644 index 0000000000..908a94cf2d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app @@ -0,0 +1,8 @@ +{application, c, + [{description, "C CXC 138 11"}, + {vsn, "1.0"}, + {modules, [b, {aa, 1}, {c_sup,1}]}, + {registered, [cc,bb,c_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {c_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl new file mode 100644 index 0000000000..069eb3b99b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl @@ -0,0 +1,40 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(c_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, c_sup}, c_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config1 = {c, + {aa, start_link, []}, + permanent, 2000, worker, [aa]}, + Config2 = {b, + {b, start_link, []}, + permanent, 2000, worker, [b]}, + {ok, {SupFlags, [Config1, Config2]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 new file mode 100755 index 0000000000..ee3d8c97cf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 @@ -0,0 +1,38 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` +IPADDR=%IPADDR% + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\" -loader inet -id $NODENAME -hosts $IPADDR" > $CLIENTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 new file mode 100755 index 0000000000..88912cf884 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 @@ -0,0 +1,37 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\',\\'master2@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\"" > /dev/null 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app new file mode 100644 index 0000000000..1c3053b2fa --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.1"}, + {modules, [{a, 2}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup new file mode 100644 index 0000000000..05db4cb541 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup @@ -0,0 +1,3 @@ +{"1.1", + [{"1.0",[{update,a,{advanced,extra_par}}]}], + []}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl new file mode 100644 index 0000000000..c082ad5339 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl @@ -0,0 +1,54 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0, b/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). +b() -> gen_server:call(aa, b). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, {state, bval}}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}; + +handle_call(b, _From, State) -> + {reply, {ok, element(2, State)}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(1, Extra, State) -> + {ok, {state, bval}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app new file mode 100644 index 0000000000..6f77317f6a --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app @@ -0,0 +1,6 @@ +{application, installer, + [{description, "Installer application"}, + {vsn, "1.0"}, + {modules, [{installer, 1}]}, + {registered, []}, + {applications, [kernel, stdlib, sasl]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl new file mode 120000 index 0000000000..c2f93b822d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl @@ -0,0 +1 @@ +../../../../installer.erl
\ No newline at end of file diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl new file mode 100644 index 0000000000..883688c231 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_atom). + +-vsn(atom). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl new file mode 100644 index 0000000000..34c38307ba --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_list). + +-vsn([list, "of", {some, terms}]). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl new file mode 100644 index 0000000000..6bf52753fd --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_numeric). + +-vsn(231894). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl new file mode 100644 index 0000000000..aa430a0bb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_string). + +-vsn("a string"). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl new file mode 100644 index 0000000000..3ff1018994 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_tuple). + +-vsn({tuple, ["of", terms]}). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/start b/lib/sasl/test/release_handler_SUITE_data/start new file mode 100755 index 0000000000..45e526c15f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/start @@ -0,0 +1,29 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%ROOT% + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +HEART_COMMAND=$ROOTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME" > $ROOTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/target_system.erl b/lib/sasl/test/release_handler_SUITE_data/target_system.erl new file mode 120000 index 0000000000..4d36c59632 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/target_system.erl @@ -0,0 +1 @@ +../../examples/src/target_system.erl
\ No newline at end of file diff --git a/lib/sasl/test/sasl.cover b/lib/sasl/test/sasl.cover new file mode 100644 index 0000000000..d19d3d0180 --- /dev/null +++ b/lib/sasl/test/sasl.cover @@ -0,0 +1,2 @@ +{incl_app,sasl,details}. + diff --git a/lib/sasl/test/sasl.spec b/lib/sasl/test/sasl.spec new file mode 100644 index 0000000000..f3de90c9aa --- /dev/null +++ b/lib/sasl/test/sasl.spec @@ -0,0 +1 @@ +{suites,"../sasl_test",all}. diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl new file mode 100644 index 0000000000..454095db6a --- /dev/null +++ b/lib/sasl/test/sasl_SUITE.erl @@ -0,0 +1,98 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(sasl_SUITE). +-include_lib("common_test/include/ct.hrl"). + + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). +-define(application, sasl). + +% Test server specific exports +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +% Test cases must be exported. +-export([app_test/1, + log_mf_h_env/1]). + +all() -> + [app_test, log_mf_h_env]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_Case, Config) -> + ?line Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +app_test(Config) when is_list(Config) -> + ?line ?t:app_test(sasl, allow), + ok. + +%% OTP-9185 - fail sasl start if some but not all log_mf_h env vars +%% are given. +log_mf_h_env(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), + ok = file:make_dir(LogDir), + application:stop(sasl), + SaslEnv = application:get_all_env(sasl), + lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,50000), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ok = application:unset_env(sasl,error_logger_mf_dir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,xx), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + ok = application:start(sasl). + + +%%----------------------------------------------------------------- +%% Internal +match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) -> + ok; +match_error(Expected,Actual) -> + ?t:fail({unexpected_return,Expected,Actual}). diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl new file mode 100644 index 0000000000..9190b111ef --- /dev/null +++ b/lib/sasl/test/systools_SUITE.erl @@ -0,0 +1,2112 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%% Test suite for the systools module. +%% +%% The systools module is a wrapper for a number of modules that +%% handle large parts of the release building functionality +%% (e.g. checking app files, building a tar file, building +%% release upgrad scripts. +%% + + +-module(systools_SUITE). + +%-define(debug, true). + +-include_lib("test_server/include/test_server.hrl"). +-define(format(S, A), ok). +-define(datadir, ?config(data_dir, Config)). +-define(privdir, ?config(priv_dir, Config)). +-define(copydir, ?config(copy_dir, Config)). + +-include_lib("kernel/include/file.hrl"). + +-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]). + +-export([ script_options/1, normal_script/1, no_mod_vsn_script/1, + wildcard_script/1, variable_script/1, + abnormal_script/1, src_tests_script/1, crazy_script/1, + warn_shadow_script/1, + included_script/1, included_override_script/1, + included_fail_script/1, included_bug_script/1, exref_script/1]). +-export([ tar_options/1, normal_tar/1, no_mod_vsn_tar/1, variable_tar/1, + src_tests_tar/1, shadow_tar/1, var_tar/1, + exref_tar/1, link_tar/1]). +-export([ normal_relup/1, abnormal_relup/1, no_appup_relup/1, + bad_appup_relup/1, app_start_type_relup/1, otp_3065/1]). +-export([ + otp_6226/1]). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-import(lists, [foldl/3]). + +-define(default_timeout, ?t:minutes(20)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group, script}, {group, tar}, {group, relup}, + {group, tickets}]. + +groups() -> + [{script, [], + [script_options, normal_script, no_mod_vsn_script, + wildcard_script, variable_script, abnormal_script, + src_tests_script, crazy_script, warn_shadow_script, + included_script, included_override_script, + included_fail_script, included_bug_script, exref_script, + otp_3065]}, + {tar, [], + [tar_options, normal_tar, no_mod_vsn_tar, variable_tar, + src_tests_tar, shadow_tar, var_tar, + exref_tar, link_tar]}, + {relup, [], + [normal_relup, abnormal_relup, no_appup_relup, + bad_appup_relup, app_start_type_relup]}, + {tickets, [], [otp_6226]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +init_per_suite(Config) when is_list(Config) -> + %% Make of copy of the data directory. + DataDir = ?datadir, + PrivDir = ?privdir, + ?line CopyDir = fname(PrivDir, "datacopy"), + ?line TarFile = fname(PrivDir, "datacopy.tgz"), + ?line {ok, Tar} = erl_tar:open(TarFile, [write, compressed]), + ?line ok = erl_tar:add(Tar, DataDir, CopyDir, [compressed]), + ?line ok = erl_tar:close(Tar), + ?line ok = erl_tar:extract(TarFile, [compressed]), + ?line ok = file:delete(TarFile), + + %% Compile source files in the copy directory. + ?line Sources = filelib:wildcard(fname([CopyDir,'*','*','*','*','*.erl'])), + ?line lists:foreach(fun compile_source/1, Sources), + + %% To use in end_per_testcase + Path = code:get_path(), + {ok,Cwd} = file:get_cwd(), + + [{copy_dir, CopyDir}, {cwd,Cwd}, {path,Path} | Config]. + +compile_source(File) -> + %% The compiler will no longer create a Beam file + %% with a module name that does not match the output + %% file, so we must compile to a binary and write + %% the output file ourselves. + U = filename:dirname(filename:dirname(File)), + Base = filename:rootname(filename:basename(File)), + OutFile = filename:join([U,"ebin",Base++".beam"]), + OutFileTemp = OutFile ++ "#", + {ok,_,Code} = compile:file(File, [binary]), + ok = file:write_file(OutFileTemp, Code), + file:rename(OutFileTemp, OutFile). + +end_per_suite(Conf) when is_list(Conf) -> + %% Nothing. + Conf. + +init_per_testcase(link_tar, Config) -> + case os:type() of + {unix, _} -> init_per_testcase(dummy, Config); + {win32, _} -> {skip, "Skip on windows"} + end; +init_per_testcase(_Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + case {?config(path,Config),?config(cwd,Config)} of + {undefined,undefined} -> + ok; + {Path,Cwd} -> + true = code:set_path(Path), + ok = file:set_cwd(Cwd) + end, + ok. + + + +%% Usage: +%% systools:make_script("RelName") +%% Make a boot file from RelName.rel. +%% Generates RelName.{script,boot} +%% systools:make_tar("RelName") +%% Make a release package from RelName.rel. +%% Generates RelName.tar,Z +%% systools:script2boot(File) +%% File.script -> File.boot +%% systools:make_relup("Target", ["UpFromRel"...], ["DownToRel"...], Opts) +%% Gather all appup scripts to the relup file +%% + + +%% make_script +%% +script_options(suite) -> []; +script_options(doc) -> + ["Check illegal script options."]; +script_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_script("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[locall]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},locall])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_script("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, {"TEST", "/home/lib"}}]}, _}} = + (catch systools:make_script("release", + [{variables, {"TEST", "/home/lib"}}])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_script("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_script("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_script("release", [{machine,"appl"}])), + ok. + + +%% make_script +%% +normal_script(suite) -> []; +normal_script(doc) -> + ["Check that make_script handles normal case."]; +normal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName,[{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +no_mod_vsn_script(suite) -> []; +no_mod_vsn_script(doc) -> + ["Check that make_script handles normal case.", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-3.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName, + [{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +wildcard_script(suite) -> []; +wildcard_script(doc) -> + ["Check that make_script handles wildcards in path."]; +wildcard_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line WildDir = fname([LibDir, '*', ebin]), + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_script(filename:basename(LatestName)), + + ?line ok = systools:make_script(LatestName, + [{path, [WildDir]}]), + + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +variable_script(suite) -> []; +variable_script(doc) -> + ["Add own installation dependent variable in script."]; +variable_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(LatestName, + [{path, P}, + {variables, [{"TEST", LibDir}]}]), + + %% Check variables + ?line ok = check_var_script_file([fname(['$TEST', 'db-2.1', ebin]), + fname(['$TEST', 'fe-3.1', ebin])], + P, + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +abnormal_script(suite) -> []; +abnormal_script(doc) -> + ["Abnormal cases."]; +abnormal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + + ?line ok = file:set_cwd(LatestDir), + ?line LibDir = fname([DataDir, d_bad_app_vsn, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Check wrong app vsn + ?line error = systools:make_script(LatestName, [{path, P}]), + ?line {error, + systools_make, + [{error_reading, {db, {no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = + systools:make_script(LatestName, [silent, {path, P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +src_tests_script(suite) -> []; +src_tests_script(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + N = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than its .erl file + ?line Erl = filename:join([P1,"..","src","db1.erl"]), + ?line {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + ?line Beam = filename:join(P1,"db1.beam"), + ?line ok=file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + %% Then make script - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, {path, N}, src_tests]), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = + systools:make_script(LatestName, [silent, {path, N}]), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, + {path, N}, + no_module_tests, + src_tests]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + +%% make_script +%% +warn_shadow_script(suite) -> []; +warn_shadow_script(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +warn_shadow_script(Config) when is_list(Config) -> + %% This test has been removed since the 'vsn' attribute is + %% not used any more, starting with R6. No warning + %% 'obj_out_of_date' seemed to be generated. + true. + + +%% make_script +%% +crazy_script(suite) -> []; +crazy_script(doc) -> + ["Do the crazy cases."]; +crazy_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest, Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Run with bad path + ?line error = systools:make_script(LatestName), + ?line {error, _, [{error_reading, _}, {error_reading, _}]} = + systools:make_script(LatestName, [silent]), + + %% Run with .rel file lacking kernel + ?line {LatestDir2, LatestName2} = create_script(latest_nokernel, Config), + ?line ok = file:set_cwd(LatestDir2), + + ?line error = systools:make_script(LatestName2), + ?line {error, _, {missing_mandatory_app,[kernel,stdlib]}} = + systools:make_script(LatestName2, [silent,{path,P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_script(suite) -> []; +included_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications."]; +included_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc1, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t5, t4, t6], + [t1, t3, t6]), + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_override_script(suite) -> []; +included_override_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications which are override by", + "the .rel file."]; +included_override_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc2, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t4, t6, t5], + [t1, t3, t6, t5]), + + ?line {_, LatestName1} = create_include_files(inc3, Config), + ?line ok = systools:make_script(LatestName1), + ?line ok = check_include_script(LatestName1, + [t3, t5, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line {_, LatestName2} = create_include_files(inc4, Config), + ?line ok = systools:make_script(LatestName2), + ?line ok = check_include_script(LatestName2, + [t3, t4, t6, t5, t1, t2], + [t3, t6, t5, t1, t2]), + + ?line {_, LatestName3} = create_include_files(inc5, Config), + ?line ok = systools:make_script(LatestName3), + ?line ok = check_include_script(LatestName3, + [t3, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_fail_script(suite) -> []; +included_fail_script(doc) -> + ["Check that make_script handles errors then generating", + "script with included applications."]; +included_fail_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc6, Config), + ?line ok = file:set_cwd(LatestDir), + ?line {error, _, {undefined_applications,[t2]}} = + systools:make_script(LatestName, [silent]), + + ?line {_, LatestName1} = create_include_files(inc7, Config), + ?line {error, _, {duplicate_include,[{{t5,t7,_,_},{t5,t6,_,_}}]}} = + systools:make_script(LatestName1, [silent]), + + ?line {_, LatestName3} = create_include_files(inc9, Config), + ?line {error, _, {circular_dependencies,[{t10,_},{t8,_}]}} = + systools:make_script(LatestName3, [silent]), + + ?line {_, LatestName4} = create_include_files(inc10, Config), + ?line {error, _, [{error_reading,{t9,{override_include,[t7]}}}]} = + systools:make_script(LatestName4, [silent]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_bug_script(suite) -> []; +included_bug_script(doc) -> + ["Check that make_script handles generation of script", + "with difficult dependency for included applications."]; +included_bug_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc11, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t13, t11, t12], + [t11, t12]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +otp_3065(suite) -> []; +otp_3065(doc) -> + ["Circular dependencies in systools:make_script()."]; +otp_3065(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(otp_3065, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [aa12, chAts, chTraffic], + [chTraffic]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +exref_script(suite) -> []; +exref_script(doc) -> + ["Check that make_script exref option works."]; +exref_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [{path,P}, silent]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_script(LatestName, [exref, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_script(LatestName, [{exref,[db]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_script(LatestName, [{exref,[fe]}, {path,P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_script(LatestName, [{exref,[db,stdlib]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + +check_exref_warnings(with_db1, W) -> + case get_exref(undef, W) of + {ok, [{db2,non_existing_func,0}, + {fe2,non_existing_func,0}, + {lists,non_existing_func,1}]} -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}); + _E -> + test_server:fail({bad_undef,_E}) + end; +check_exref_warnings(without_db1, W) -> + case get_exref(undef, W) of + false -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}) + end. + +get_exref(undef, W) -> filter(no_hipe(get_exref1(exref_undef, W))). + +filter(false) -> + false; +filter({ok, W}) -> + {ok, filter(W)}; +filter(L) -> + lists:filter(fun%({hipe_consttab,_,_}) -> false; + ({int,_,_}) -> false; + ({i,_,_}) -> false; + ({crypto,_,_}) -> false; + (_) -> true + end, + L). + +get_exref1(T, [{warning, {T, Value}}|_]) -> {ok, Value}; +get_exref1(T, [_|W]) -> get_exref1(T, W); +get_exref1(_, []) -> false. + +no_hipe(false) -> + false; +no_hipe({ok, Value}) -> + case erlang:system_info(hipe_architecture) of + undefined -> + Hipe = "hipe", + Fun = fun({M,_,_}) -> not lists:prefix(Hipe, atom_to_list(M)) end, + NewValue = lists:filter(Fun, Value), + {ok, NewValue}; + _Arch -> + {ok, Value} + end. + +%% tar_options +%% +tar_options(suite) -> []; +tar_options(doc) -> + ["Check illegal tar options."]; +tar_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_tar("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[{dirs,["dirs"]}]}, _}} = + (catch systools:make_tar("release", [{dirs, ["dirs"]}])), + ?line {'EXIT',{{badarg,[{erts, illegal}]}, _}} = + (catch systools:make_tar("release", [{erts, illegal}])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_tar("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_tar("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[{var_tar, illegal}]}, _}} = + (catch systools:make_tar("release", [{var_tar, illegal}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_tar("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_tar("release", [{machine,"appl"}])), + ok. + + +%% normal_tar +%% +normal_tar(suite) -> []; +normal_tar(doc) -> + ["Check normal case"]; +normal_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% no_mod_vsn_tar +%% +no_mod_vsn_tar(suite) -> []; +no_mod_vsn_tar(doc) -> + ["Check normal case", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-3.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-3.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% variable_tar +%% +variable_tar(suite) -> []; +variable_tar(doc) -> + ["Use variable and create separate tar (included in generated tar)."]; +variable_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line {ok, _, _} = systools:make_tar(LatestName, + [{path, P}, silent, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% link_tar +%% +link_tar(suite) -> []; +link_tar(doc) -> + ["Check that symlinks in applications are handled correctly"]; +link_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_links, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Make some links + ?line Db1Erl = fname(['db-2.1',src,'db1.erl']), + ?line NormalDb1Erl = fname([DataDir,d_normal,lib,Db1Erl]), + ?line LinkDb1Erl = fname([LibDir, Db1Erl]), + ?line ok = file:make_symlink(NormalDb1Erl, LinkDb1Erl), + ?line Db1Beam = fname(['db-2.1',ebin,'db1.beam']), + ?line NormalDb1Beam = fname([DataDir,d_normal,lib,Db1Beam]), + ?line LinkDb1Beam = fname([LibDir, Db1Beam]), + ?line ok = file:make_symlink(NormalDb1Beam, LinkDb1Beam), + ?line FeApp = fname(['fe-3.1',ebin,'fe.app']), + ?line NormalFeApp = fname([DataDir,d_normal,lib,FeApp]), + ?line LinkFeApp = fname([LibDir, FeApp]), + ?line ok = file:make_symlink(NormalFeApp, LinkFeApp), + + %% Create the tar and check that the linked files are included as + %% regular files + ?line ok = file:set_cwd(LatestDir), + + ?line {ok,_,[]} = systools:make_script(LatestName, [{path, P},silent]), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam])], + LatestName), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam]), + fname([lib,Db1Erl])], + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% src_tests_tar +%% +src_tests_tar(suite) -> []; +src_tests_tar(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + P = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than the .erl file + Erl = filename:join([P1,"..","src","db1.erl"]), + {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + Beam = filename:join(P1,"db1.beam"), + ok = file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + ?line ok = systools:make_script(LatestName, [{path, P}]), + + %% Then make tar - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + no_module_tests, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% shadow_tar +%% +shadow_tar(suite) -> []; +shadow_tar(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +shadow_tar(Config) when is_list(Config) -> + % This test has been commented out since the 'vsn' attribute is not used + % any more, starting with R6. No warning 'obj_out_of_date' seemed to be + % generated. + true; +shadow_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, 'd_bad_mod+warn', lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% var_tar +%% +var_tar(suite) -> []; +var_tar(doc) -> + ["Check that make_tar handles generation and placement of tar", + "files for variables outside the main tar file.", + "Test the {var_tar, include | ownfile | omit} option."]; +var_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, ownfile}, + {variables,[{"TEST", LibDir}]}]), + + ?line true = exists_tar_file("TEST"), %% Also removes the file ! + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, omit}, + {variables,[{"TEST", LibDir}]}]), + + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, include}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% exref_tar +%% +exref_tar(suite) -> []; +exref_tar(doc) -> + ["Check exref option."]; +exref_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_tar(LatestName, [exref, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_tar(LatestName, [{exref, [db]}, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_tar(LatestName, [{exref, [fe]}, {path, P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_tar(LatestName, [{exref, [db, stdlib]}, + {path, P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% The relup stuff. +%% +%% + + +%% make_relup +%% +normal_relup(suite) -> []; +normal_relup(doc) -> + ["Check normal case"]; +normal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + ?line {_LatestDir2,LatestName2} = create_script(latest2,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = [fname([DataDir, d_normal, lib])], + ?line P = [fname([LibDir, '*', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% OTP-2561: Check that the option 'restart_emulator' generates a + %% "restart_new_emulator" instruction. + ?line {ok, _ , _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P},restart_emulator,silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator(), + + %% This is the ultra normal case + ?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + + %% Check that warnings get through + ?line ok = systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, [{erts_vsn_changed, _}]} = + systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% This test fails if wrong version numbers are seen in the relup file +%% or if any application is missing. This was triggered by OTP-1360. +check_relup(UpVsnL, DnVsnL) -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + UpVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Up]), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + DnVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Dn]), + ok. + +check_restart_emulator() -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + restart_new_emulator = lists:last(Up), + restart_new_emulator = lists:last(Dn), + ok. + +%% make_relup +%% +no_appup_relup(suite) -> []; +no_appup_relup(doc) -> + ["Check that appup files may be missing, but only if we don't need them."]; +no_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest_small1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that appup might be missing + ?line ok = + systools:make_relup(LatestName, [LatestName], [], [{path, P1}]), + ?line {ok,_, _, []} = + systools:make_relup(LatestName, [LatestName], [], + [silent, {path, P1}]), + + %% Check that appup might NOT be missing when we need it + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, P1}]), + ?line {error,_,{file_problem, {_,{error,{open,_,_}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, P1}]), + + %% Check that appups missing vsn traps + ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line error = + systools:make_relup(LatestName0, [LatestName1], [], [{path, P2}]), + ?line {error,_,{no_relup, _, _, _}} = + systools:make_relup(LatestName0, [], [LatestName1], + [silent, {path, P2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +bad_appup_relup(suite) -> []; +bad_appup_relup(doc) -> + ["Check that badly written appup files are detected"]; +bad_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + + ?line DataDir = filename:absname(?copydir), + ?line N2 = [fname([DataDir, d_bad_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that bad appup is trapped + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, N2}]), + ?line {error,_,{file_problem, {_, {error, {parse,_, _}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, N2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +abnormal_relup(suite) -> []; +abnormal_relup(doc) -> + ["Check some abnormal cases"]; +abnormal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + + %% Check wrong app vsn + ?line DataDir = filename:absname(?copydir), + ?line P = [fname([DataDir, d_bad_app_vsn, lib, 'db-2.1', ebin]), + fname([DataDir, d_bad_app_vsn, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line R0 = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [silent, {path, P}]), + ?line {error,systools_make, + [{error_reading,{db,{no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = R0, + ?line ok = file:set_cwd(OldDir), + ok. + + +%% Check that application start type is used in relup +app_start_type_relup(suite) -> + []; +app_start_type_relup(doc) -> + ["Release upgrade file with various application start types"]; +app_start_type_relup(Config) when is_list(Config) -> + ?line PrivDir = ?config(priv_dir, Config), + ?line {Dir1,Name1} = create_script(latest_app_start_type1,Config), + ?line {Dir2,Name2} = create_script(latest_app_start_type2,Config), + ?line Release1 = filename:join(Dir1,Name1), + ?line Release2 = filename:join(Dir2,Name2), + + ?line {ok, Release2Relup, systools_relup, []} = systools:make_relup(Release2, [Release1], [Release1], [{outdir, PrivDir}, silent]), + ?line {"2", [{"1",[], UpInstructions}], [{"1",[], DownInstructions}]} = Release2Relup, + %% ?t:format("Up: ~p",[UpInstructions]), + %% ?t:format("Dn: ~p",[DownInstructions]), + ?line [{load_object_code, {mnesia, _, _}}, + {load_object_code, {sasl, _, _}}, + {load_object_code, {webtool, _, _}}, + {load_object_code, {snmp, _, _}}, + {load_object_code, {xmerl, _, _}}, + point_of_no_return + | UpInstructionsT] = UpInstructions, + ?line true = lists:member({apply,{application,start,[mnesia,permanent]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[sasl,transient]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[webtool,temporary]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,load,[snmp]}}, UpInstructionsT), + ?line false = lists:any(fun({apply,{application,_,[xmerl|_]}}) -> true; (_) -> false end, UpInstructionsT), + ?line [point_of_no_return | DownInstructionsT] = DownInstructions, + ?line true = lists:member({apply,{application,stop,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[xmerl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[xmerl]}}, DownInstructionsT), + ok. + + +otp_6226(suite) -> + []; +otp_6226(doc) -> + ["{outdir,Dir} option for systools:make_script()"]; +otp_6226(Config) when is_list(Config) -> + PrivDir = ?privdir, + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest0,Config), + ?line {_LatestDir, LatestName1} = create_script(latest1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'db-1.0', ebin]), + fname([LibDir, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + + %% Create an outdir1 directory + ?line ok = file:make_dir("outdir1"), + + %% ==== Now test systools:make_script ==== + %% a) badarg + ?line {'EXIT', {{badarg,[{outdir,outdir1}]}, _}} = + (catch systools:make_script(LatestName, [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Outdir1 = filename:join(PrivDir, "outdir1"), + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Script1 = filename:join(Outdir1, LatestName ++ ".script"), + ?line Boot1 = filename:join(Outdir1, LatestName ++ ".boot"), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% d) absolute but incorrect path + ?line Outdir2 = filename:join(PrivDir, "outdir2"), + ?line Script2 = filename:join(Outdir2, LatestName ++ ".script"), + ?line {error,_,{open,Script2,_}} = + systools:make_script(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{open,_,_}} = + systools:make_script(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_script(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(LatestName ++ ".script"), + ?line true = filelib:is_file(LatestName ++ ".boot"), + ?line ok = file:delete(LatestName ++ ".script"), + ?line ok = file:delete(LatestName ++ ".boot"), + ?line ok = file:set_cwd(LatestDir), + + %% ==== Now test systools:make_tar ===== + ?line {ok,_,[]} = systools:make_script(LatestName, [{path,P},silent]), + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_tar(LatestName,[{outdir,outdir1},{path,P},silent])), + + %% b) absolute path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Tar1 = filename:join(Outdir1,LatestName++".tar.gz"), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% d) absolute but incorrect path + ?line Tar2 = filename:join(Outdir2,LatestName++".tar.gz"), + ?line {error,_,{tar_error,{open,Tar2,{Tar2,enoent}}}} = + systools:make_tar(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{tar_error,{open,_,_}}} = + systools:make_tar(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_tar(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:set_cwd(LatestDir), + + %% ===== Now test systools:make_relup ===== + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Relup = filename:join(Outdir1, "relup"), + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir1}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% c) relative path + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% d) absolute but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + %% -- not necessary to test since relup by default is placed in + %% cwd, not in the same directory as the .rel file -- + + %% Change back to previous working directory + ?line ok = file:set_cwd(OldDir), + ok. + + +%%%%%% +%%%%%% Utilities +%%%%%% + +check_script_path(RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + case lists:keysearch(path, 1, ListOfThings) of + {value, {path, [$$,$R,$O,$O,$T | _]}} -> %"$ROOT..." + false; + _ -> ok + end. + +check_var_script_file(VarDirs, NoExistDirs, RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + AllPaths = lists:append(lists:map(fun({path, P}) -> P; + (_) -> [] + end, + ListOfThings)), + case lists:filter(fun(VarDir) -> lists:member(VarDir, AllPaths) end, + VarDirs) of + VarDirs -> + ok; + _ -> + test_server:fail("All variable dirs not in generated script") + end, + case lists:filter(fun(NoExistDir) -> lists:member(NoExistDir, AllPaths) end, + NoExistDirs) of + [] -> + ok; + _ -> + test_server:fail("Unexpected dirs in generated script") + end. + +check_include_script(RelName, ExpectedLoad, ExpectedStart) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + + %% Check that the applications are loaded in given order ! + ActualLoad = + [App || {apply,{application,load,[{application,App,_}]}} <- ListOfThings, + App=/=kernel, + App=/=stdlib], + + if ActualLoad =:= ExpectedLoad -> ok; + true -> test_server:fail({bad_load_order, ActualLoad, ExpectedLoad}) + end, + + %% Check that applications are started in given order ! + ActualStart = + [App || {apply,{application,start_boot,[App|_]}} <- ListOfThings, + App =/= kernel, + App =/= stdlib], + + if ActualStart =:= ExpectedStart -> ok; + true -> test_server:fail({bad_start_order, ActualStart,ExpectedStart}) + end, + + ok. + +read_script_file(RelName) -> + file:consult(RelName ++ ".script"). + +check_var_tar(Variable, RelName) -> + Expected = tar_name(Variable), + case check_tar(Expected,RelName) of + ok -> + ok; + {error, {erroneous_tar_file, _, missing, _}} -> + {error, {not_generated, Expected}} + end. + +exists_tar_file(Name) -> + File = tar_name(Name), + case filelib:is_regular(File) of + true -> + ok = file:delete(File), + true; + _ -> + false + end. + +%% Take a snap of the generated tar file and check if a certain +%% file is included. +%% This ensures at least that the tar file is generated. +check_tar(File, RelName) -> + TarContents = tar_contents(RelName), + case lists:member(File,TarContents) of + true -> ok; + _ -> {error, {erroneous_tar_file, tar_name(RelName), missing, File}} + end. + +%% Check that the given files exist in the tar file, and that they are +%% not symlinks +check_tar_regular(PrivDir, Files, RelName) -> + TmpDir = fname(PrivDir,tmp), + ok = file:make_dir(TmpDir), + ok = erl_tar:extract(tar_name(RelName), + [{files,Files},{cwd,TmpDir},compressed]), + R = lists:foldl(fun(File,Acc) -> + case file:read_link_info(fname(TmpDir,File)) of + {ok,#file_info{type=regular}} -> + Acc; + {ok,#file_info{type=Other}} -> + [{File,Other}|Acc]; + _ -> + [{File,missing}|Acc] + end + end, + [], + Files), + delete_tree(TmpDir), + case R of + [] -> + ok; + NotThere -> + {error,{erroneous_tar_file,tar_name(RelName),NotThere}} + end. + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +tar_contents(Name) -> + {ok, Cont} = erl_tar:table(Name ++ ".tar.gz", [compressed]), + Cont. + +tar_name(Name) -> + Name ++ ".tar.gz". + +create_script(latest,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_no_mod_vsn,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATESTNOMOD\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"3.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest0,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-2'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 1\", \"LATEST2\"}, \n" + " {erts, \"4.3\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small0,Config) -> %Differs in fe vsn + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small0'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"500.18.7\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_nokernel,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-nokernel'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST_NOKERNEL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type1), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "1"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type2), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line OtherApps = [{mnesia,permanent}, + {sasl,transient}, + {webtool,temporary}, + {snmp,load}, + {xmerl,none}], + ?line lists:foreach(fun({App,_}) -> application:load(App) end, + OtherApps), + ?line Loaded = application:loaded_applications(), + ?line OtherAppsRel = + lists:map(fun({App,StartType}) -> + {_,_,Ver} = lists:keyfind(App,1,Loaded), + {App,Ver,StartType} + end, + OtherApps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "2"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer} | OtherAppsRel]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}. + +create_include_files(inc1, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc1), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc2, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc2), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc3, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc3), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc4, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc4), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc5, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc5), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + %% exclude t5. + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc6, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc6), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does include non existing t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc7, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc7), + create_apps(PrivDir), + create_app(t7, PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t7 and t6 does include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t7, \"1.0\"}, {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc8, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc8), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9 and t10 includes t9 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc9, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc9), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9, t9 uses t10 and t10 includes t8 ==> circular !! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\", [t8]}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc10, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc10), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t9 tries to include not specified (in .app file) application ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\", [t7]}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc11, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc11), + create_apps2(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t11, \"1.0\"}, \n" + " {t12, \"1.0\"}, \n" + " {t13, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(otp_3065, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, otp_3065), + create_apps_3065(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" + " {chTraffic, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}. + +create_apps(Dir) -> + T1 = "{application, t1,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [kernel, stdlib]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't1.app'), list_to_binary(T1)), + + T2 = "{application, t2,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t1]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't2.app'), list_to_binary(T2)), + + T3 = "{application, t3,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t2]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't3.app'), list_to_binary(T3)), + + T4 = "{application, t4,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't4.app'), list_to_binary(T4)), + + T5 = "{application, t5,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't5.app'), list_to_binary(T5)), + + T6 = "{application, t6,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t4, t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't6.app'), list_to_binary(T6)). + +create_app(t7, Dir) -> + T7 = "{application, t7,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't7.app'), list_to_binary(T7)). + +create_circular_apps(Dir) -> + T8 = "{application, t8,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t9]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't8.app'), list_to_binary(T8)), + + T9 = "{application, t9,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t10]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't9.app'), list_to_binary(T9)), + + T10 = "{application, t10,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t8, t9]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't10.app'), list_to_binary(T10)). + +create_apps2(Dir) -> + T11 = "{application, t11,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t13]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't11.app'), list_to_binary(T11)), + + T12 = "{application, t12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t11]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't12.app'), list_to_binary(T12)), + + T13 = "{application, t13,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't13.app'), list_to_binary(T13)). + + + +create_apps_3065(Dir) -> + T11 = "{application, chTraffic,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [chAts]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chTraffic.app'), list_to_binary(T11)), + + T12 = "{application, chAts,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [aa12]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chAts.app'), list_to_binary(T12)), + + T13 = "{application, aa12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [chAts]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). + +fname(N) -> + filename:join(N). + +fname(Dir, Basename) -> + filename:join(Dir, Basename). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..d375768b99 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..dac4c00851 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1" [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..3e0ac3c3c9 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app new file mode 100644 index 0000000000..22530ee335 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app new file mode 100644 index 0000000000..7243a0a96a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..202d7f1234 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..7cddf6bb63 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl @@ -0,0 +1,13 @@ +-module(db1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + lists:non_existing_func("dummy_list"), + b(). + +b() -> + fe2:non_existing_func(), + db2:non_existing_func(), + fe1:a(). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app new file mode 100644 index 0000000000..97643561eb --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "3.1"}, + {modules, [db1, db2]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup new file mode 100644 index 0000000000..e636eee158 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app new file mode 100644 index 0000000000..c7ba1dfe91 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..8aca89b2c7 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,7 @@ +-module(fe1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + ok. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app new file mode 100644 index 0000000000..e33314be7a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app @@ -0,0 +1,6 @@ +{application, kernel, + [{description, "FAKE KERNEL"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup new file mode 100644 index 0000000000..c53f07591f --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for kernel +%% + +{ + "4.4.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app new file mode 100644 index 0000000000..288fcfe74e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app @@ -0,0 +1,6 @@ +{application, stdlib, + [{description, "FAKE STDLIB"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup new file mode 100644 index 0000000000..b521eb5d71 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for stdlib +%% + +{ + "1.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl new file mode 100644 index 0000000000..bb93f38fa7 --- /dev/null +++ b/lib/sasl/test/systools_rc_SUITE.erl @@ -0,0 +1,488 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(systools_rc_SUITE). + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("sasl/src/systools.hrl"). +-export([all/0,groups/0,init_per_group/2,end_per_group/2, + syntax_check/1, translate/1, translate_app/1]). + +%%----------------------------------------------------------------- +%% erl -compile systools_rc_SUITE @i ../src/ @i ../../test_server/include/ +%% c(systools_rc_SUITE, [{i, "../src"}, {i, "../../test_server/include"}]). +%%----------------------------------------------------------------- +all() -> + [syntax_check, translate, translate_app]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +syntax_check(suite) -> []; +syntax_check(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "0.1", + modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}], + regs = [], + mod = {sasl, []}}, + #application{name = snmp, + description = "SNMP", + vsn = "1.0", + modules = [snmp], + regs = [], + mod = {snmp, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}], + regs = [], + mod = {sasl, []}}], + S1 = [ + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, baz, 5000, soft, brutal_purge, brutal_purge, []}, + {add_module, new_mod}, + {remove_application, snmp} + ], + ?line {ok, _} = systools_rc:translate_scripts([S1], Apps, PreApps), + S2 = [ + {apply, {m, f, [a]}}, + {load_object_code, {tst, "1.0", [new_mod]}}, + point_of_no_return, + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {load, {new_mod, soft_purge, soft_purge}}, + {remove, {old_mod, soft_purge, soft_purge}}, + {purge, [m1, m2]}, + {suspend, [m1]}, + {code_change, [{m1, extra}]}, + {resume, [m1]}, + {stop, [m3,m4]}, + {start, [m3,m4]}, + {sync_nodes, id1, {m, f, [a]}}, + {sync_nodes, id2, [cp1, cp2]}, + {apply, {m,f,[a]}}, + restart_new_emulator + ], + ?line {ok, _} = systools_rc:translate_scripts([S2], Apps, []), + S3 = [{apply, {m, f, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3], Apps, []), + S3_1 = [{apply, {m, 3, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3_1], Apps, []), + S4 = [{apply, {m, f}}], + ?line {error, _, _} = systools_rc:translate_scripts([S4], Apps, []), + S5 = [{load_object_code, hej}], + ?line {error, _, _} = systools_rc:translate_scripts([S5], Apps, []), + S6 = [{load_object_code, {342, "1.0", [foo]}}], + ?line {error, _, _} = systools_rc:translate_scripts([S6], Apps, []), + S7 = [{load_object_code, {tets, "1.0", foo}}], + ?line {error, _, _} = systools_rc:translate_scripts([S7], Apps, []), + S8 = [{suspend, [m1]}, point_of_no_return], + ?line {error, _, _} = systools_rc:translate_scripts([S8], Apps, []), + S9 = [{update, ba, {advanced, extra}, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S9], Apps, []), + S10 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [baz]}], + ?line {error, _, _} = systools_rc:translate_scripts([S10], Apps, []), + S11 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S11], Apps, []), + S12 = [{update, bar, advanced, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S12], Apps, []), + S13 = [{update, bar, {advanced, extra}, rutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S13], Apps, []), + S14 = [{update, bar, {advanced, extra}, brutal_purge, rutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S14], Apps, []), + S15 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, ba}], + ?line {error, _, _} = systools_rc:translate_scripts([S15], Apps, []), + S16 = [{code_change, [module]}], + ?line {error, _, _} = systools_rc:translate_scripts([S16], Apps, []), + ?line ok. + +translate(suite) -> []; +translate(Config) when is_list(Config) -> + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}, + {x,1},{y,1},{z,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{update, foo, soft, soft_purge, soft_purge, []}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X1, + + %% Simple translation (2) + Up2 = [{update, foo, {advanced, extra}, soft_purge, soft_purge, []}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2, + + ?line {ok, X22} = systools_rc:translate_scripts(dn,[Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {code_change, down, [{foo, extra}]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X22, + + Up2a = [{update, foo, static, default, {advanced,extra}, + soft_purge, soft_purge, []}], + ?line {ok, X2a} = systools_rc:translate_scripts([Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2a, + + ?line {ok, X22a} = systools_rc:translate_scripts(dn,[Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, down, [{foo, extra}]}, + {resume,[foo]}] = X22a, + + %% Simple dependency (1) + Up3 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, []}], + ?line {ok, X31} = systools_rc:translate_scripts([Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X31, + ?line {ok, X32} = systools_rc:translate_scripts(dn,[Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X32, + + Up3a = [{update, foo, static, default, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, default, soft, soft_purge, soft_purge, []}], + ?line {ok, X3a1} = systools_rc:translate_scripts([Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo, bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a1, + ?line {ok, X3a2} = systools_rc:translate_scripts(dn,[Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a2, + + %% Simple dependency (2) + Up4 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}], + ?line {ok, X4} = systools_rc:translate_scripts(up,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4, + + ?line {ok, X42} = systools_rc:translate_scripts(dn,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42, + + Up4a = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4a} = systools_rc:translate_scripts(up,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4a, + + ?line {ok, X42a} = systools_rc:translate_scripts(dn,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,foo]}] = X42a, + + Up4b = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, dynamic, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4b} = systools_rc:translate_scripts(up,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4b, + + ?line {ok, X42b} = systools_rc:translate_scripts(dn,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42b, + + %% More complex dependency + Up5 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}, + {update, baz, {advanced, baz}, soft_purge, soft_purge, [bar]}], + ?line {ok, X5} = systools_rc:translate_scripts([Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5, + + ?line {ok, X52} = systools_rc:translate_scripts(dn,[Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {code_change,down,[{baz,baz},{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,baz,foo]}] = X52, + + Up5a = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, static, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, dynamic, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5a} = systools_rc:translate_scripts([Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz}, {bar,[]}]}, + {resume,[bar,baz,foo]}] = X5a, + + ?line {ok, X52a} = systools_rc:translate_scripts(dn,[Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{baz,baz}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,baz,foo]}] = X52a, + + Up5b = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, dynamic, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, static, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5b} = systools_rc:translate_scripts([Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5b, + + ?line {ok, X52b} = systools_rc:translate_scripts(dn,[Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{baz,baz}]}, + {resume,[bar,baz,foo]}] = X52b, + + %% Simple circular dependency (1) + Up6 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}], + ?line {ok, X61} = systools_rc:translate_scripts([Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X61, + ?line {ok, X62} = systools_rc:translate_scripts(dn,[Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X62, + + %% Simple circular dependency (2) + Up7 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X71} = systools_rc:translate_scripts([Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[baz, bar, foo]}] = X71, + ?line {ok, X72} = systools_rc:translate_scripts(dn,[Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {resume,[baz,bar,foo]}] = X72, + + %% Complex circular dependencies, check only order + %% + Up8 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X8} = systools_rc:translate_scripts([Up8], Apps, []), + ?line {value, {suspend, Order}} = lists:keysearch(suspend, 1, X8), + ?line Rest = case lists:reverse(Order) of + [bar, baz | R] -> R; + [baz, bar | R] -> R + end, + ?line case Rest of + [y, z, x, foo] -> ok; + [y, x, z, foo] -> ok; + [foo, y, z, x] -> ok; + [foo, y, x, z] -> ok; + [y, foo, z, x] -> ok; + [y, foo, x, z] -> ok + end, + + %% Check that order among other instructions isn't changed + Up9 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [1]}}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [2]}}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {apply, {m, f, [3]}}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {apply, {m, f, [4]}}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [5]}}, + {update, baz, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [6]}}], + ?line {ok, X9} = systools_rc:translate_scripts([Up9], Apps, []), + Other2 = [X || {apply, {m, f, [X]}} <- X9], + ?line [1,2,3,4,5,6] = lists:sort(Other2), + ?line ok. + + +translate_app(suite) -> []; +translate_app(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}, + #application{name = pelle, + description = "PELLE", + vsn = "1.0", + modules = [pelle, kalle], + regs = [], + mod = {pelle, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{add_module, foo}, + {add_module, bar}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}] = X1, + + %% Simple translation (2) + Up2 = [{add_application, test}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), +io:format("X2=~p~n", [X2]), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X2, + + %% Simple translation (3) + Up3 = [{remove_application, pelle}], + ?line {ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps), + ?line [point_of_no_return, + {apply,{application,stop,[pelle]}}, + {remove,{pelle,brutal_purge,brutal_purge}}, + {remove,{kalle,brutal_purge,brutal_purge}}, + {purge,[pelle,kalle]}, + {apply,{application,unload,[pelle]}}] = X3, + ?line ok. diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk index bd94cd6bac..61c91c9729 100644 --- a/lib/snmp/doc/src/files.mk +++ b/lib/snmp/doc/src/files.mk @@ -157,4 +157,5 @@ MIB_FILES = \ $(MIBSDIR)/SNMP-VIEW-BASED-ACM-MIB.mib \ $(MIBSDIR)/SNMP-USM-AES-MIB.mib \ $(MIBSDIR)/INET-ADDRESS-MIB.mib \ + $(MIBSDIR)/TRANSPORT-ADDRESS-MIB.mib \ $(MIBSDIR)/OTP-SNMPEA-MIB.mib diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2efeb8ae3f..6a20d8ee3a 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -33,6 +33,116 @@ </header> <section> + <title>SNMP Development Toolkit 4.20</title> + <p>Version 4.20 supports code replacement in runtime from/to + version 4.19 and 4.18.</p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + <list type="bulleted"> + <item> + <p>[agent] Added support for sending traps to IPv6 targets. </p> + <p>See the + <seealso marker="snmp_agent_config_files#target_addr">target address config file</seealso>, + the <seealso marker="snmpa_conf#target_addr_entry">target_addr_entry/11</seealso> function or + <seealso marker="snmp_target_mib#add_addr">add_addr/11</seealso> for more info. </p> + <p>Own Id: OTP-9088</p> + <p>Aux Id: Seq 11790</p> + </item> + + + <item> + <p>[agent] To be able to handle multiple engine-id(s) when + sending trap(s), the function + <seealso marker="snmp_community_mib#add_community"> + add_community/6</seealso> has been added. </p> + <p>Own Id: OTP-9119</p> + <p>Aux Id: Seq 11792</p> + </item> + + <item> + <p>[manager] The API for snmp requests has been augmented to + allow the caller to override some configuration. </p> + <p>This has been done by introducing a new set of API functions, see + <seealso marker="snmpm#sync_get2">sync_get2/3,4</seealso>, + <seealso marker="snmpm#async_get2">async_get2/3,4</seealso>, + <seealso marker="snmpm#sync_get_next2">sync_get_next2/3,4</seealso>, + <seealso marker="snmpm#async_get_next2">async_get_next2/3,4</seealso>, + <seealso marker="snmpm#sync_get_bulk2">sync_get_bulk2/5,6</seealso>, + <seealso marker="snmpm#async_get_bulk2">async_get_bulk2/5,6</seealso>, + <seealso marker="snmpm#sync_set2">sync_set2/3,4</seealso> and + <seealso marker="snmpm#async_set2">async_set2/3,4</seealso> + for more info. </p> + <p>Own Id: OTP-9162</p> + </item> + + <item> + <p>[manager] The old API functions (for get and set + requests: + snmpm:g/3,4,5,6,7, snmpm:ag/3,4,5,6,7, + snmpm:gn/3,4,5,6,7, snmpm:agn/3,4,5,6,7, + snmpm:s/3,4,5,6,7, snmpm:s/3,4,5,6,7, + snmpm:gb/5,6,7,8,9 and snmpm:agb/5,6,7,8,9) + are now officially deprecated. + They will be removed as of R16B. </p> + <p>Own Id: OTP-9174</p> + </item> + + <item> + <p>[agent] Pass extra info through the agent to the net-if + process when sending notifications. </p> + <p>See + <seealso marker="snmpa#send_notification2"> + snmpa:send_notification2/3</seealso> for more info. + See also the incomming net-if messages when sending a + <seealso marker="snmp_agent_netif#im_send_pdu">trap</seealso> + (send_pdu message) and + <seealso marker="snmp_agent_netif#im_send_pdu_req"> + notification</seealso> (send_pdu_req message). </p> + <p>Own Id: OTP-9183</p> + <p>Aux Id: Seq 11817</p> + </item> + + <item> + <p>Added type specs for functions that do not return. </p> + <p>Kostis Sagonas</p> + <p>Own Id: OTP-9208</p> + </item> + </list> + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>Fixed endode/decode of values of type <c>Counter32</c>. </p> + <p>This type (<c>Counter32</c>) is an unsigned integer 32, + but is actually encoded as an signed integer 32. + The encode/decode functions however, treated it as if it was + encodeded as an unsigned integer 32. </p> + <p>Own Id: OTP-9022</p> + </item> + + </list> + </section> + + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.20 --> + + + <section> <title>SNMP Development Toolkit 4.19</title> <p>Version 4.19 supports code replacement in runtime from/to version 4.18.</p> @@ -126,6 +236,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols). </section> <!-- 4.19 --> + <section> <title>SNMP Development Toolkit 4.18</title> <p>Version 4.18 supports code replacement in runtime from/to @@ -226,7 +337,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols). <p>The config utility (<seealso marker="snmp#config">snmp:config/0</seealso>) generated a default notify.conf - with a bad name for the starndard trap entry (was "stadard trap", + with a bad name for the standard trap entry (was "stadard trap", but should have been "standard trap"). This has been corrected. </p> <p>Kenji Rikitake</p> <p>Own Id: OTP-8433</p> @@ -430,7 +541,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols). <p>The config utility (<seealso marker="snmp#config">snmp:config/0</seealso>) generated a default notify.conf - with a bad name for the starndard trap entry (was "stadard trap", + with a bad name for the standard trap entry (was "stadard trap", but should have been "standard trap"). This has been corrected. </p> <p>Kenji Rikitake</p> <p>Own Id: OTP-8433</p> diff --git a/lib/snmp/doc/src/snmp_agent_config_files.xml b/lib/snmp/doc/src/snmp_agent_config_files.xml index b62269d506..bd5c537522 100644 --- a/lib/snmp/doc/src/snmp_agent_config_files.xml +++ b/lib/snmp/doc/src/snmp_agent_config_files.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -367,56 +367,50 @@ <marker id="target_addr"></marker> <title>Target Address Definitions</title> <p>The information about Target Address Definitions should be - stored in a file called - <c>target_addr.conf</c>. - </p> + stored in a file called <c>target_addr.conf</c>. </p> <p>The corresponding tables are <c>snmpTargetAddrTable</c> in the - SNMP-TARGET-MIB and <c>snmpTargetAddrExtTable</c> in the SNMP-COMMUNITY-MIB. - </p> - <p>Each entry is a term: - </p> - <p><c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId}.</c> or <br></br> -<c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c></p> + SNMP-TARGET-MIB and <c>snmpTargetAddrExtTable</c> in the + SNMP-COMMUNITY-MIB. </p> + <p>Each entry is a term: </p> + <p><c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId}.</c> <br></br> or <br></br> +<c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c> <br></br> or <br></br> +<c>{TargetName, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c></p> <list type="bulleted"> <item> <p><c>TargetName</c> is a unique non-empty string. </p> </item> <item> - <p><c>Ip</c> is a list of four integers. - </p> + <p><c>Domain</c> is one of the atoms: + <c>transportDomainUdpIpv4</c> | <c>transportDomainUdpIpv6</c>. </p> </item> <item> - <p><c>Udp</c> is an integer. - </p> + <p><c>Ip</c> is a list of four or eight integers. </p> </item> <item> - <p><c>Timeout</c> is an integer. - </p> + <p><c>Udp</c> is an integer. </p> </item> <item> - <p><c>RetryCount</c> is an integer. - </p> + <p><c>Timeout</c> is an integer. </p> </item> <item> - <p><c>TagList</c> is a string. - </p> + <p><c>RetryCount</c> is an integer. </p> </item> <item> - <p><c>ParamsName</c> is a string. - </p> + <p><c>TagList</c> is a string. </p> </item> <item> - <p><c>EngineId</c> is a string or the atom <c>discovery</c>. - </p> + <p><c>ParamsName</c> is a string. </p> </item> <item> - <p><c>TMask</c> is a string of size 0, or size 6 (default: []). - </p> + <p><c>EngineId</c> is a string or the atom <c>discovery</c>. </p> </item> <item> - <p><c>MaxMessageSize</c> is an integer (default: 2048). - </p> + <p><c>TMask</c> is a list of integer() of size 0, + size 6 or size 10 (default: []). </p> + </item> + <item> + <p><c>MaxMessageSize</c> is an integer (default: 2048). </p> </item> </list> <p>Note that if <c>EngineId</c> has the value <c>discovery</c>, @@ -429,14 +423,10 @@ <marker id="target_params"></marker> <title>Target Parameters Definitions</title> <p>The information about Target Parameters Definitions should be - stored in a file called - <c>target_params.conf</c>. - </p> + stored in a file called <c>target_params.conf</c>. </p> <p>The corresponding table is <c>snmpTargetParamsTable</c> in the - SNMP-TARGET-MIB. - </p> - <p>Each entry is a term: - </p> + SNMP-TARGET-MIB. </p> + <p>Each entry is a term: </p> <p><c>{ParamsName, MPModel, SecurityModel, SecurityName, SecurityLevel}.</c></p> <list type="bulleted"> <item> diff --git a/lib/snmp/doc/src/snmp_agent_netif.xml b/lib/snmp/doc/src/snmp_agent_netif.xml index 1f2dbe80db..d751740a82 100644 --- a/lib/snmp/doc/src/snmp_agent_netif.xml +++ b/lib/snmp/doc/src/snmp_agent_netif.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -65,16 +65,19 @@ </section> <section> + <marker id="messages"></marker> <title>Messages</title> <p>The section <em>Messages</em> describes mandatory messages, which Net if must send and be able to receive. </p> <section> + <marker id="outgoing_messages"></marker> <title>Outgoing Messages</title> <p>Net if must send the following message when it receives an - SNMP PDU from the network that is aimed for the MasterAgent: - </p> + SNMP PDU from the network that is aimed for the MasterAgent: + </p> + <marker id="om_snmp_pdu"></marker> <pre> MasterAgent ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra} </pre> @@ -106,9 +109,10 @@ MasterAgent ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra} the request.</item> </list> <p>The following message is used to report that a response to a - request has been received. The only request an agent can send - is an Inform-Request. - </p> + request has been received. The only request an agent can send + is an Inform-Request. + </p> + <marker id="om_snmp_response_received"></marker> <pre> Pid ! {snmp_response_received, Vsn, Pdu, From} </pre> @@ -131,119 +135,153 @@ Pid ! {snmp_response_received, Vsn, Pdu, From} </section> <section> + <marker id="incoming_messages"></marker> <title>Incoming Messages</title> <p>This section describes the incoming messages which a Net if - process must be able to receive. - </p> + process must be able to receive. + </p> <list type="bulleted"> <item> + <marker id="im_snmp_response"></marker> <p><c>{snmp_response, Vsn, Pdu, Type, ACMData, To, Extra}</c></p> <p>This message is sent to the Net if process from a master agent as a response to a previously received request. </p> <list type="bulleted"> - <item><c>Vsn</c> is either <c>'version-1'</c>, - <c>'version-2'</c>, or <c>'version-3'</c>. + <item> + <p><c>Vsn</c> is either <c>'version-1'</c>, + <c>'version-2'</c>, or <c>'version-3'</c>. </p> </item> - <item><c>Pdu</c> is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. + <item> + <p><c>Pdu</c> is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response. </p> </item> - <item><c>Type</c> is the <c>#pdu.type</c> of the original - request. + <item> + <p><c>Type</c> is the <c>#pdu.type</c> + of the original request. </p> </item> - <item><c>ACMData</c> is data used by the Access Control - Module in use. Normally this is just sent to - <c>snmpa_mpd:generate_response_message</c> (see Reference Manual). + <item> + <p><c>ACMData</c> is data used by the Access Control + Module in use. Normally this is just sent to + <c>snmpa_mpd:generate_response_message</c> + (see Reference Manual). </p> </item> - <item><c>To</c> is the destination address. If UDP over IP - is used, this should be a 2-tuple <c>{IP, UDPport}</c>, - where <c>IP</c> is a 4-tuple with the IP address, and - <c>UDPport</c> is an integer. + <item> + <p><c>To</c> is the destination address. If UDP over IP + is used, this should be a 2-tuple <c>{IP, UDPport}</c>, + where <c>IP</c> is a 4-tuple with the IP address, and + <c>UDPport</c> is an integer. </p> </item> - <item><c>Extra</c> is the term that the Net if process - sent to the agent when the request was sent to the agent. + <item> + <p><c>Extra</c> is the term that the Net if process + sent to the agent when the request was sent to the agent. </p> </item> </list> </item> <item> + <marker id="im_discarded_pdu"></marker> <p><c>{discarded_pdu, Vsn, ReqId, ACMData, Variable, Extra}</c></p> <p>This message is sent from a master agent if it for some - reason decided to discard the pdu. - </p> + reason decided to discard the pdu. </p> <list type="bulleted"> - <item><c>Vsn</c> is either <c>'version-1'</c>, - <c>'version-2'</c>, or <c>'version-3'</c>. + <item> + <p><c>Vsn</c> is either <c>'version-1'</c>, + <c>'version-2'</c>, or <c>'version-3'</c>. </p> </item> - <item><c>ReqId</c> is the request id of the original - request. + <item> + <p><c>ReqId</c> is the request id of the original request. </p> </item> - <item><c>ACMData</c> is data used by the Access Control - Module in use. Normally this is just sent to - <c>snmpa_mpd:generate_response_message</c> (see Reference Manual). + <item> + <p><c>ACMData</c> is data used by the Access Control + Module in use. Normally this is just sent to + <c>snmpa_mpd:generate_response_message</c> + (see Reference Manual). </p> </item> - <item><c>Variable</c> is the name of an snmp counter that - represents the error, e.g. <c>snmpInBadCommunityUses</c>. + <item> + <p><c>Variable</c> is the name of an snmp counter that + represents the error, e.g. <c>snmpInBadCommunityUses</c>. </p> </item> - <item><c>Extra</c> is the term that the Net if process - sent to the agent when the request was sent to the agent. + <item> + <p><c>Extra</c> is the term that the Net if process + sent to the agent when the request was sent to the agent. </p> </item> </list> </item> <item> - <p><c>{send_pdu, Vsn, Pdu, MsgData, To}</c></p> + <marker id="im_send_pdu"></marker> + <p><c>{send_pdu, Vsn, Pdu, MsgData, To, Extra}</c></p> <p>This message is sent from a master agent when a trap is - to be sent. - </p> + to be sent. </p> <list type="bulleted"> - <item><c>Vsn</c> is either <c>'version-1'</c>, - <c>'version-2'</c>, or <c>'version-3'</c>. + <item> + <p><c>Vsn</c> is either <c>'version-1'</c>, + <c>'version-2'</c>, or <c>'version-3'</c>.</p> </item> - <item><c>Pdu</c> is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. + <item> + <p><c>Pdu</c> is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response. </p> + </item> + <item> + <p><c>MsgData</c> is the message specific data used in + the SNMP message. This value is normally sent to + <c>snmpa_mpd:generate_message/4</c>. In SNMPv1 and + SNMPv2c, this message data is the community string. In + SNMPv3, it is the context information. </p> </item> - <item><c>MsgData</c> is the message specific data used in - the SNMP message. This value is normally sent to - <c>snmpa_mpd:generate_message/4</c>. In SNMPv1 and - SNMPv2c, this message data is the community string. In - SNMPv3, it is the context information. + <item> + <p><c>To</c> is a list of the destination addresses and + their corresponding security parameters. This value is + normally sent to <c>snmpa_mpd:generate_message/4</c>. </p> </item> - <item><c>To</c> is a list of the destination addresses and - their corresponding security parameters. This value is - normally sent to <c>snmpa_mpd:generate_message/4</c>. + <item> + <p><c>Extra</c> is any term that the notification sender + wishes to pass to the Net if process when sending a notification + (see + <seealso marker="snmpa#send_notification2">send notification + </seealso> for more info). </p> </item> </list> </item> <item> - <p><c>{send_pdu_req, Vsn, Pdu, MsgData, To, Pid}</c></p> - <p>This <marker id="message"></marker> - message is sent from a master - agent when a request is - to be sent. The only request an agent can send is - Inform-Request. The net if process needs to remember the - request id and the Pid, and when a response is received for - the request id, send it to Pid, using a - <c>snmp_response_received</c> message. - </p> - <list type="bulleted"> - <item><c>Vsn</c> is either <c>'version-1'</c>, - <c>'version-2'</c>, or <c>'version-3'</c>. - </item> - <item><c>Pdu</c> is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. - </item> - <item><c>MsgData</c> is the message specific data used in - the SNMP message. This value is normally sent to - <c>snmpa_mpd:generate_message/4</c>. In SNMPv1 and - SNMPv2c, this message data is the community string. In - SNMPv3, it is the context information. + <marker id="im_send_pdu_req"></marker> + <p><c>{send_pdu_req, Vsn, Pdu, MsgData, To, Pid, Extra}</c></p> + <p>This message is sent from a master agent when a request is to + be sent. The only request an agent can send is Inform-Request. + The net if process needs to remember the request id and the Pid, + and when a response is received for the request id, send it to Pid, + using a <c>snmp_response_received</c> message. </p> + <list type="bulleted"> + <item> + <p><c>Vsn</c> is either <c>'version-1'</c>, + <c>'version-2'</c>, or <c>'version-3'</c>.</p> + </item> + <item> + <p><c>Pdu</c> is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response. </p> + </item> + <item> + <p><c>MsgData</c> is the message specific data used in + the SNMP message. This value is normally sent to + <c>snmpa_mpd:generate_message/4</c>. In SNMPv1 and + SNMPv2c, this message data is the community string. In + SNMPv3, it is the context information. </p> + </item> + <item> + <p><c>To</c> is a list of the destination addresses and + their corresponding security parameters. This value is + normally sent to <c>snmpa_mpd:generate_message/4</c>. </p> + </item> + <item> + <p><c>Pid</c> is a process identifier. </p> + </item> + <item> + <p><c>Extra</c> is any term that the notification sender + wishes to pass to the Net if process when sending a notification + (see + <seealso marker="snmpa#send_notification2">send notification + </seealso> for more info). </p> </item> - <item><c>To</c> is a list of the destination addresses and - their corresponding security parameters. This value is - normally sent to <c>snmpa_mpd:generate_message/4</c>. - </item> - <item><c>Pid</c> is a process identifier. - </item> - </list> + </list> </item> </list> </section> diff --git a/lib/snmp/doc/src/snmp_community_mib.xml b/lib/snmp/doc/src/snmp_community_mib.xml index 7c7386af19..5e7bca3e27 100644 --- a/lib/snmp/doc/src/snmp_community_mib.xml +++ b/lib/snmp/doc/src/snmp_community_mib.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,11 +35,13 @@ <modulesummary>Instrumentation Functions for SNMP-COMMUNITY-MIB</modulesummary> <description> <p>The module <c>snmp_community_mib</c> implements the instrumentation - functions for the - SNMP-COMMUNITY-MIB, and functions for configuring the database. - </p> + functions for the SNMP-COMMUNITY-MIB, and functions for configuring the + database. </p> <p>The configuration files are described in the SNMP User's Manual.</p> + + <marker id="configure"></marker> </description> + <funcs> <func> <name>configure(ConfDir) -> void()</name> @@ -68,8 +70,11 @@ </p> <p>The configuration file read is: <c>community.conf</c>. </p> + + <marker id="reconfigure"></marker> </desc> </func> + <func> <name>reconfigure(ConfDir) -> void()</name> <fsummary>Configure the SNMP-COMMUNITY-MIB</fsummary> @@ -96,28 +101,35 @@ where the configuration files are found. </p> <p>The configuration file read is: <c>community.conf</c>.</p> + <marker id="add_community"></marker> </desc> </func> + <func> <name>add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Ret</name> + <name>add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> Ret</name> <fsummary>Added one community</fsummary> <type> <v>Idx = string()</v> <v>CommName = string()</v> <v>SecName = string()</v> + <v>EngineId = string()</v> <v>CtxName = string()</v> <v>TransportTag = string()</v> <v>Ret = {ok, Key} | {error, Reason}</v> <v>Key = term()</v> - <v>Reason = term()</v> + <v>Reason = term()</v> </type> <desc> <p>Adds a community to the agent config. - Equivalent to one line in the <c>community.conf</c> file.</p> + Equivalent to one line in the <c>community.conf</c> file.</p> + <p>With the <c>EngineId</c> argument it is possible to + override the configured engine-id (SNMP-FRAMEWORK-MIB).</p> <marker id="delete_community"></marker> </desc> </func> + <func> <name>delete_community(Key) -> Ret</name> <fsummary>Delete one community</fsummary> diff --git a/lib/snmp/doc/src/snmp_target_mib.xml b/lib/snmp/doc/src/snmp_target_mib.xml index 4a36be19a3..d5151d41de 100644 --- a/lib/snmp/doc/src/snmp_target_mib.xml +++ b/lib/snmp/doc/src/snmp_target_mib.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1998</year><year>2009</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,9 +39,21 @@ and functions for configuring the database. </p> <p>The configuration files are described in the SNMP User's Manual.</p> - <marker id="configure"></marker> + <marker id="types"></marker> </description> + <section> + <title>DATA TYPES</title> + <code type="none"><![CDATA[ +transportDomain() = transportDomainUdpIpv4 | transportDomainUdpIpv6 +transportAddressIPv4() = [integer()], length 4 +transportAddressIPv6() = [integer()], length 8 +transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6) + ]]></code> + + <marker id="configure"></marker> + </section> + <funcs> <func> <name>configure(ConfDir) -> void()</name> @@ -118,17 +130,19 @@ <func> <name>add_addr(Name, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name> + <name>add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name> <fsummary>Add one target address definition</fsummary> <type> <v>Name = string()</v> - <v>Ip = [integer()], length 4</v> + <v>Domain = transportDomain()</v> + <v>Ip = transportAddressIPv4() | transportAddressIPv6() (depends on the value of Domain)</v> <v>Port = integer()</v> <v>Timeout = integer()</v> <v>Retry = integer()</v> <v>TagList = string()</v> <v>ParamsName = string()</v> <v>EngineId = string()</v> - <v>TMask = string(), length 0 or 6</v> + <v>TMask = transportAddressMask() (depends on Domain)</v> <v>MMS = integer()</v> <v>Ret = {ok, Key} | {error, Reason}</v> <v>Key = term()</v> diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index 1d680e80f5..27d89ea4e3 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -872,10 +872,138 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). then that sub-agent will be unregistered from all trees in <c>Agent</c>. </p> - <marker id="send_notification"></marker> + <marker id="send_notification2"></marker> </desc> </func> + + <func> + <name>send_notification2(Agent, Notification, SendOpts) -> void()</name> + <fsummary>Send notification</fsummary> + <type> + <v>Agent = pid() | atom()</v> + <v>Notification = atom()</v> + <v>SendOpts = [send_option()]</v> + <v>send_option() = {receiver, receiver()} | {name, notify_name()} | {context, context_name()} | {varbinds, varbinds()} | {local_engine_id, string()} | {extra, extra_info()}</v> + <v>receiver() = no_receiver | {tag(), tag_receiver()} | notification_delivery_info()</v> + <v>tag() = term(()</v> + <v>tag_receiver() = pid() | registered_name() | {Mod, Func, Args}</v> + <v>registered_name() = atom()</v> + <v>Mod = atom()</v> + <v>Func = atom()</v> + <v>Args = list()</v> + <v>notify_name() = string()</v> + <v>context_name() = string()</v> + <v>varbinds() = [varbind()]</v> + <v>varbind() = {variable(), value()} | {column(), row_index(), value()} | {oid(), value()}</v> + <v>variable() = atom()</v> + <v>value() = term()</v> + <v>column() = atom()</v> + <v>row_index() = [int()]</v> + <v>extra_info() = term()</v> + </type> + <desc> + <p>Send the notification <c>Notification</c> to the management + targets defined for notify-name (<c>name</c>) in the + <c>snmpNotifyTable</c> in SNMP-NOTIFICATION-MIB from the + specified <c>context</c>. </p> + + <p>If no <c>name</c> is specified (or if it is <c>""</c>), the + notification is sent to all management targets. </p> + + <p>If no <c>context</c> is specified, the default context, <c>""</c>, + is used. </p> + + <p>The send option <c>receiver</c> specifies where information + about delivery of Inform-Requests should be sent. The agent + sends Inform-Requests and waits for acknowledgments from the + management targets. + The <c>receiver</c> can have three values: </p> + + <list type="bulleted"> + <item> + <p><c>no_receiver</c> - No information is delivered. </p> + </item> + + <item> + <p><c>notification_delivery_info()</c> - The information is + delivered via a function call according to this data. See the + <seealso marker="#data_types">DATA TYPES</seealso> section + above for details. </p> + </item> + + <item> + <p><c>{tag(), tag_receiver()}</c> - The information is delivered + either via messages or via a function call according to the value + of <c>tag_receiver()</c>. </p> + <p>Delivery is done differently depending on the value + of <c>tag_receiver()</c>: </p> + + <list> + <item> + <p><c>pid() | registered_name()</c> - The info will be delivered in + the following messages: </p> + <list> + <item> + <p><c>{snmp_targets, tag(), Addresses}</c></p> + <p>This informs the user which target addresses the + notification was sent to. </p> + </item> + <item> + <p><c>{snmp_notification, tag(), {got_response, Address}}</c></p> + <p>This informs the user that this target address + acknowledged the notification. </p> + </item> + <item> + <p><c>{snmp_notification, tag(), {no_response, Address}}</c></p> + <p>This informs the user that this target address + did not acknowledge the notification. </p> + </item> + </list> + <p>The notification is sent as an Inform-Request to each + target address in <c>Addresses</c> and if there are no + targets for which an Inform-Request is sent, <c>Addresses</c> + is the empty list <c>[]</c>. </p> + <p>The <c>tag_receiver()</c> will first be sent the + <c>snmp_targets</c> message, and then for each address in + <c>Addresses</c> list, one of the two <c>snmp_notification</c> + messages. </p> + </item> + + <item> + <p><c>{Mod, Func, Args}</c> - The info will be delivered via + the function call: </p> + <p><c>Mod:Func([Msg | Args])</c></p> + <p>where <c>Msg</c> has the same content and purpose as the + messages descrived above.</p> + </item> + + </list> + </item> + </list> + + <note> + <p>The <c>extra</c> info is not normally interpreted by the agent, + instead it is passed through to the + <seealso marker="snmp_agent_netif">net-if</seealso> process. It is + up to the implementor of that process to make use of this data. </p> + <p>The version of net-if provided by this application makes no use + of this data, with one exception: + Any tuple containing the atom + <c>snmpa_default_notification_extra_info</c> + may be used by the agent and is therefor <em>reserved</em>. </p> + <p>See the net-if incomming messages for sending a + <seealso marker="snmp_agent_netif#im_send_pdu"> + trap</seealso> and + <seealso marker="snmp_agent_netif#im_send_pdu_req"> + notification</seealso> for more info. </p> + </note> + + <marker id="send_notification"></marker> + </desc> + </func> + + <func> <name>send_notification(Agent, Notification, Receiver)</name> <name>send_notification(Agent, Notification, Receiver, Varbinds)</name> @@ -907,18 +1035,19 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </type> <desc> <p>Sends the notification <c>Notification</c> to the - management targets defined for <c>NotifyName</c> in the - <c>snmpNotifyTable</c> in SNMP-NOTIFICATION-MIB from the - specified context. If no <c>NotifyName</c> is specified (or - if it is <c>""</c>), the notification is sent to all - management targets (<c>Addresses</c> below). If no <c>ContextName</c> - is specified, the default <c>""</c> context is used. - </p> + management targets defined for <c>NotifyName</c> in the + <c>snmpNotifyTable</c> in SNMP-NOTIFICATION-MIB from the + specified context. </p> + <p>If no <c>NotifyName</c> is specified (or if it is <c>""</c>), + the notification is sent to all management targets + (<c>Addresses</c> below). </p> + <p>If no <c>ContextName</c> is specified, the default <c>""</c> + context is used. </p> <p>The parameter <c>Receiver</c> specifies where information - about delivery of Inform-Requests should be sent. The agent - sends Inform-Requests and waits for acknowledgments from the - managers. <c>Receiver</c> can have three values: </p> + about delivery of Inform-Requests should be sent. The agent + sends Inform-Requests and waits for acknowledgments from the + managers. <c>Receiver</c> can have three values: </p> <list type="bulleted"> <item> @@ -926,17 +1055,18 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). </item> <item> - <p><c>{Tag, Recv}</c> - The information is delivered either via messages - or via a function call according to the value of <c>Recv</c>. </p> - </item> - - <item> <p><c>notification_delivery_info()</c> - The information is delivered via a function call according to this data. See the <seealso marker="#data_types">DATA TYPES</seealso> section above for details. </p> </item> + <item> + <p><c>{Tag, Recv}</c> - The information is delivered either via + messages or via a function call according to the value of + <c>Recv</c>. </p> + </item> + </list> @@ -1064,86 +1194,20 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). (see SNMP-FRAMEWORK-MIB). </p> </note> -<!-- - <marker id="send_trap"></marker> ---> - <marker id="discovery"></marker> + <p><c>ExtraInfo</c> is not normally used in any way by the agent. + It is intended to be passed along to the net-if process, which is + a component that a user can implement themself. The users own net-if + may then make use of ExtraInfo. The net-if provided with this + application does not process ExtraInfo. </p> + <p>There is one exception. <em>Any</em> tuple containing the atom + <c>snmpa_default_notification_extra_info</c> will, in this context, + be considered belonging to this application, and may be processed + by the agent. </p> + + <marker id="discovery"></marker> </desc> </func> -<!-- - <func> - <name>send_trap(Agent,Trap,Community)</name> - <name>send_trap(Agent,Trap,Community,Varbinds) -> void()</name> - <fsummary>Send a trap</fsummary> - <type> - <v>Agent = pid() | atom()</v> - <v>Trap = atom()</v> - <v>Community = string()</v> - <v>Varbinds = [Varbind]</v> - <v>Varbind = {Variable, Value} | {Column, RowIndex, Value} | {OID, Value}</v> - <v>Variable = atom()</v> - <v>Column = atom()</v> - <v>OID = oid()</v> - <v>Value = term()</v> - <v>RowIndex = [int()]</v> - </type> - <desc> - <p>Note! This function is only kept for backwards - compatibility reasons. Use <c>send_notification</c> instead. - </p> - <p>Sends the trap <c>Trap</c> to the managers defined for - <c>Community</c> in the <c>intTrapDestTable</c> in - OTP-SNMPEA-MIB. The optional argument <c>Varbinds</c> defines - values for the objects in the trap. If no value is given for - an object, the <c>Agent</c> performs a get-operation to - retrieve the value. - </p> - <p><c>Varbinds</c> is a list of <c>Varbind</c>, where each - <c>Varbind</c> is one of: - </p> - <list type="bulleted"> - <item><c>{Variable, Value}</c>, where <c>Variable</c> is the - symbolic name of a scalar variable referred to in the trap - specification. - </item> - <item><c>{Column, RowIndex, Value}</c>, where <c>Column</c> - is the symbolic name of a column variable. - <c>RowIndex</c> is a list of indices for the specified - element. If this is the case, the OBJECT IDENTIFIER sent - in the trap is the <c>RowIndex</c> appended to the OBJECT - IDENTIFIER for the table column. This is the OBJECT - IDENTIFIER which specifies the element. - </item> - <item><c>{OID, Value}</c>, where <c>OID</c> is the OBJECT - IDENTIFIER for an instance of an object, scalar variable, - or column variable. - </item> - </list> - <p>For example, to specify that <c>sysLocation</c> should have the - value <c>"upstairs"</c> in the trap, we could use one of: - </p> - <list type="bulleted"> - <item><c>{sysLocation, "upstairs"}</c> or</item> - <item><c>{[1,3,6,1,2,1,1,6,0], "upstairs"}</c> or</item> - <item><c>{?sysLocation_instance, "upstairs"}</c> (provided - that the generated <c>.hrl</c> file is included)</item> - </list> - <p>If a variable in the trap is a table element, the - <c>RowIndex</c> for the element must be given in the - <c>Varbinds</c> list. In this case, the OBJECT IDENTIFIER sent - in the trap is the OBJECT IDENTIFIER that identifies this - element. This OBJECT IDENTIFIER could be used in a get - operation later. - </p> - <p>This function is asynchronous, and does not return any - information. If an error occurs, <c>snmp_error:user_err/2</c> - is called and the trap is discarded. </p> - - <marker id="discovery"></marker> - </desc> - </func> ---> <func> <name>discovery(TargetName, Notification) -> {ok, ManagerEngineID} | {error, Reason}</name> diff --git a/lib/snmp/doc/src/snmpa_conf.xml b/lib/snmp/doc/src/snmpa_conf.xml index d873574c6e..a533c179ee 100644 --- a/lib/snmp/doc/src/snmpa_conf.xml +++ b/lib/snmp/doc/src/snmpa_conf.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2006</year><year>2010</year> + <year>2006</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,8 +38,21 @@ used for manipulating (write/append/read) the config files of the SNMP agent. </p> - <marker id="agent_entry"></marker> + <marker id="types"></marker> </description> + + <section> + <title>DATA TYPES</title> + <code type="none"><![CDATA[ +transportDomain() = transportDomainUdpIpv4 | transportDomainUdpIpv6 +transportAddressIPv4() = [integer()], length 4 +transportAddressIPv6() = [integer()], length 8 +transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6) + ]]></code> + + <marker id="agent_entry"></marker> + </section> + <funcs> <func> <name>agent_entry(Tag, Val) -> agent_entry()</name> @@ -381,17 +394,19 @@ <name>target_addr_entry(Name, Ip, TagList, ParamsName, EngineId, TMask) -> target_addr_entry()</name> <name>target_addr_entry(Name, Ip, Udp, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> <name>target_addr_entry(Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> + <name>target_addr_entry(Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name> <fsummary>Create an target_addr entry</fsummary> <type> <v>Name = string()</v> - <v>Ip = string()</v> + <v>Domain = transportDomain()</v> + <v>Ip = transportAddressIPv4() | transportAddressIPv6() (depends on Domain)</v> <v>Udp = integer()</v> <v>Timeout = integer()</v> <v>RetryCount = integer()</v> <v>TagList = string()</v> <v>ParamsName = string()</v> <v>EngineId = string()</v> - <v>TMask = string()</v> + <v>TMask = transportAddressMask() (depends on Domain)</v> <v>MaxMessageSize = integer()</v> <v>target_addr_entry() = term()</v> </type> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index 1ee391d9ba..72849b9c9e 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -63,6 +63,10 @@ value_type() = o ('OBJECT IDENTIFIER') | c64 ('Counter64') | tt ('TimeTicks') value() = term() +community() = string() +sec_model() = any | v1 | v2c | usm +sec_name() = string() +sec_level() = noAuthNoPriv | authNoPriv | authPriv ]]></code> <marker id="monitor"></marker> @@ -482,11 +486,65 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <p>Get a list of all registered usm users with engine-id <c>EngineID</c>.</p> - <marker id="sync_get"></marker> + <marker id="sync_get2"></marker> </desc> </func> <func> + <name>sync_get2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name>sync_get2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <fsummary>Synchronous <c>get-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>Oids = [oid()]</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>SnmpReply = snmp_reply()</v> + <v>Remaining = integer()</v> + <v>Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term()</v> + <v>ReqId = term()</v> + <v>ActualReason = term()</v> + <v>SecInfo = [sec_info()]</v> + <v>sec_info() = {sec_tag(), ExpectedValue, ReceivedValue}</v> + <v>sec_tag() = atom()</v> + <v>ExpectedValue = ReceivedValue = term()</v> + <v>SnmpInfo = term()</v> + </type> + <desc> + <p>Synchronous <c>get-request</c>. </p> + + <p><c>Remaining</c> is the remaining time of the given (or default) + timeout time.</p> + + <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + <em>ActualReason</em> is the actual reason in this case. </p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <p>For <c>SnmpInfo</c>, see the user callback function + <seealso marker="snmpm_user#handle_report">handle_report</seealso>.</p> + + <marker id="sync_get"></marker> + </desc> + </func> + + <func> <name>sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <name>sync_get(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> <name>sync_get(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> @@ -513,19 +571,63 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Synchronous <c>get-request</c>. </p> <p><c>Remaining</c> is the remaining time of the given or - default timeout time.</p> + default timeout time.</p> <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. <em>R</em> - is the actual reason in this case. </p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. <em>R</em> + is the actual reason in this case. </p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> <p>For <c>SnmpInfo</c>, see the user callback function <seealso marker="snmpm_user#handle_report">handle_report</seealso>.</p> + <marker id="async_get2"></marker> + </desc> + </func> + + <func> + <name>async_get2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name>async_get2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <fsummary>Asynchronous <c>get-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>Oids = [oid()]</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>ReqId = term()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>Asynchronous <c>get-request</c>.</p> + + <p>The reply, if it arrives, will be delivered to the user + through a call to the snmpm_user callback function + <c>handle_pdu</c>.</p> + + <p>The send option <c>timeout</c> specifies for how long the request is + valid (after which the manager is free to delete it).</p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + <marker id="async_get"></marker> </desc> </func> @@ -550,17 +652,73 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Asynchronous <c>get-request</c>.</p> <p>The reply, if it arrives, will be delivered to the user - through a call to the snmpm_user callback function - <c>handle_pdu</c>.</p> - <p>The <c>Expire</c> time indicates for how long the request is - valid (after which the manager is free to delete it).</p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> - - <marker id="sync_get_next"></marker> + through a call to the snmpm_user callback function + <c>handle_pdu</c>.</p> + <p>The <c>Expire</c> time indicates for how long the request is + valid (after which the manager is free to delete it).</p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="sync_get_next2"></marker> + </desc> + </func> + + <func> + <name>sync_get_next2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name>sync_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <fsummary>Synchronous <c>get-next-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>Oids = [oid()]</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>SnmpReply = snmp_reply()</v> + <v>Remaining = integer()</v> + <v>Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term()</v> + <v>ReqId = term()</v> + <v>ActualReason = term()</v> + <v>SecInfo = [sec_info()]</v> + <v>sec_info() = {sec_tag(), ExpectedValue, ReceivedValue}</v> + <v>sec_tag() = atom()</v> + <v>ExpectedValue = ReceivedValue = term()</v> + <v>SnmpInfo = term()</v> + </type> + <desc> + <p>Synchronous <c>get-next-request</c>. </p> + + <p><c>Remaining</c> is the remaining time of the given (or default) + timeout time.</p> + + <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + <em>ActualReason</em> is the actual reason in this case. </p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <p>For <c>SnmpInfo</c>, see the user callback function + <seealso marker="snmpm_user#handle_report">handle_report</seealso>.</p> + + <marker id="sync_get_next"></marker> </desc> </func> @@ -587,16 +745,57 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <p>Synchronous <c>get-next-request</c>. </p> <p><c>Remaining</c> time of the given or default timeout time.</p> <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. <em>R</em> - is the actual reason in this case. </p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> - - <marker id="async_get_next"></marker> + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. <em>R</em> + is the actual reason in this case. </p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="async_get_next2"></marker> + </desc> + </func> + + <func> + <name>async_get_next2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name>async_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <fsummary>Asynchronous <c>get-next-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>Oids = [oid()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>ReqId = integer()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>Asynchronous <c>get-next-request</c>. </p> + + <p>The reply will be delivered to the user through a call + to the snmpm_user callback function <c>handle_pdu</c>.</p> + + <p>The send option <c>timeout</c> specifies for how long the request is + valid (after which the manager is free to delete it).</p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <marker id="async_get_next"></marker> </desc> </func> @@ -620,11 +819,75 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Asynchronous <c>get-next-request</c>. </p> <p>The reply will be delivered to the user through a call - to the snmpm_user callback function <c>handle_pdu</c>.</p> + to the snmpm_user callback function <c>handle_pdu</c>.</p> <p>The <c>Expire</c> time indicates for how long the request is - valid (after which the manager is free to delete it).</p> + valid (after which the manager is free to delete it).</p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="sync_set2"></marker> + </desc> + </func> - <marker id="sync_set"></marker> + <func> + <name>sync_set2(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name>sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <fsummary>Synchronous <c>set-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>VarsAndVals = vars_and_vals()</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>SnmpReply = snmp_reply()</v> + <v>Remaining = integer()</v> + <v>Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term()</v> + <v>ReqId = term()</v> + <v>ActualReason = term()</v> + <v>SecInfo = [sec_info()]</v> + <v>sec_info() = {sec_tag(), ExpectedValue, ReceivedValue}</v> + <v>sec_tag() = atom()</v> + <v>ExpectedValue = ReceivedValue = term()</v> + <v>SnmpInfo = term()</v> + </type> + <desc> + <p>Synchronous <c>set-request</c>. </p> + + <p><c>Remaining</c> is the remaining time of the given (or default) + timeout time.</p> + + <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + <em>ActualReason</em> is the actual reason in this case. </p> + + <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the + manager makes an educated guess based on the loaded mibs. </p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <p>For <c>SnmpInfo</c>, see the user callback function + <seealso marker="snmpm_user#handle_report">handle_report</seealso>.</p> + + <marker id="sync_set"></marker> </desc> </func> @@ -651,18 +914,64 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <p>Synchronous <c>set-request</c>. </p> <p><c>Remaining</c> time of the given or default timeout time.</p> <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. <em>R</em> - is the actual reason in this case. </p> - <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the - manager makes an educated guess based on the loaded mibs. </p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> - - <marker id="async_set"></marker> + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. <em>R</em> + is the actual reason in this case. </p> + <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the + manager makes an educated guess based on the loaded mibs. </p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="async_set2"></marker> + </desc> + </func> + + <func> + <name>async_set2(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason}</name> + <name>async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <fsummary>Asynchronous <c>set-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>VarsAndVals = vars_and_vals()</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>ReqId = term()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>Asynchronous <c>set-request</c>. </p> + + <p>The reply will be delivered to the user through a call + to the snmpm_user callback function <c>handle_pdu</c>.</p> + + <p>The send option <c>timeout</c> specifies for how long the request is + valid (after which the manager is free to delete it).</p> + + <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the + manager makes an educated guess based on the loaded mibs. </p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <marker id="async_set"></marker> </desc> </func> @@ -685,18 +994,76 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Asynchronous <c>set-request</c>. </p> <p>The reply will be delivered to the user through a call - to the snmpm_user callback function <c>handle_pdu</c>.</p> + to the snmpm_user callback function <c>handle_pdu</c>.</p> <p>The <c>Expire</c> time indicates for how long the request is - valid (after which the manager is free to delete it).</p> - <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the - manager makes an educated guess based on the loaded mibs. </p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> - - <marker id="sync_get_bulk"></marker> + valid (after which the manager is free to delete it).</p> + <p>When <em>var_and_val()</em> is <em>{oid(), value()}</em>, the + manager makes an educated guess based on the loaded mibs. </p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="sync_get_bulk2"></marker> + </desc> + </func> + + <func> + <name>sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <name>sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason}</name> + <fsummary>Synchronous <c>get-bulk-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>NonRep = integer()</v> + <v>MaxRep = integer()</v> + <v>Oids = [oid()]</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>SnmpReply = snmp_reply()</v> + <v>Remaining = integer()</v> + <v>Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term()</v> + <v>ReqId = term()</v> + <v>ActualReason = term()</v> + <v>SecInfo = [sec_info()]</v> + <v>sec_info() = {sec_tag(), ExpectedValue, ReceivedValue}</v> + <v>sec_tag() = atom()</v> + <v>ExpectedValue = ReceivedValue = term()</v> + <v>SnmpInfo = term()</v> + </type> + <desc> + <p>Synchronous <c>get-bulk-request</c> (See RFC1905).</p> + + <p><c>Remaining</c> is the remaining time of the given (or default) + timeout time.</p> + + <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + <em>ActualReason</em> is the actual reason in this case. </p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <p>For <c>SnmpInfo</c>, see the user callback function + <seealso marker="snmpm_user#handle_report">handle_report</seealso>.</p> + + <marker id="sync_get_bulk"></marker> </desc> </func> @@ -723,17 +1090,60 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Synchronous <c>get-bulk-request</c> (See RFC1905).</p> <p><c>Remaining</c> time of the given or default timeout time.</p> - <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. <em>R</em> - is the actual reason in this case. </p> - <p><c>ExtraInfo</c> is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.</p> - - <marker id="async_get_bulk"></marker> + <p>When <em>Reason</em> is <em>{send_failed, ...}</em> it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. <em>R</em> + is the actual reason in this case. </p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> + + <marker id="async_get_bulk2"></marker> + </desc> + </func> + + <func> + <name>async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason}</name> + <name>async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason}</name> + <fsummary>Asynchronous <c>get-bulk-request</c></fsummary> + <type> + <v>UserId = term()</v> + <v>TargetName = target_name()</v> + <v>NonRep = integer()</v> + <v>MaxRep = integer()</v> + <v>Oids = [oid()]</v> + <v>SendOpts = send_opts()</v> + <v>send_opts() = [send_opt()]</v> + <v>send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()}</v> + <v>ReqId = integer()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>Asynchronous <c>get-bulk-request</c> (See RFC1905).</p> + + <p>The reply will be delivered to the user through a call + to the snmpm_user callback function <c>handle_pdu</c>.</p> + + <p>The send option <c>timeout</c> specifies for how long the request is + valid (after which the manager is free to delete it).</p> + + <p>The send option <c>extra</c> specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.</p> + + <p>Some of the send options (<c>community</c>, <c>sec_model</c>, + <c>sec_name</c>, <c>sec_level</c> and <c>max_message_size</c>) + are <c>override options</c>. That is, + for <em>this</em> request, they override any configuration done + when the agent was registered. </p> + + <marker id="async_get_bulk"></marker> </desc> </func> @@ -759,9 +1169,16 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 <desc> <p>Asynchronous <c>get-bulk-request</c> (See RFC1905).</p> <p>The reply will be delivered to the user through a call - to the snmpm_user callback function <c>handle_pdu</c>.</p> + to the snmpm_user callback function <c>handle_pdu</c>.</p> <p>The <c>Expire</c> time indicates for how long the request is - valid (after which the manager is free to delete it).</p> + valid (after which the manager is free to delete it).</p> + <p><c>ExtraInfo</c> is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + <em>Any</em> tuple with <c>snmpm_extra_info_tag</c> as its first + element is reserved for internal use. </p> <marker id="cancel_async_request"></marker> </desc> diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in index 7aefb0ea34..3af74eca75 100644 --- a/lib/snmp/mibs/Makefile.in +++ b/lib/snmp/mibs/Makefile.in @@ -61,7 +61,8 @@ MIBS_A = \ SNMP-USER-BASED-SM-MIB \ SNMP-VIEW-BASED-ACM-MIB \ SNMP-USM-AES-MIB \ - INET-ADDRESS-MIB + INET-ADDRESS-MIB \ + TRANSPORT-ADDRESS-MIB MIBS_B = OTP-SNMPEA-MIB diff --git a/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib b/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib new file mode 100644 index 0000000000..7d450fbc2a --- /dev/null +++ b/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib @@ -0,0 +1,417 @@ +TRANSPORT-ADDRESS-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-IDENTITY, mib-2 FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC; + +transportAddressMIB MODULE-IDENTITY + LAST-UPDATED "200211010000Z" + ORGANIZATION + "IETF Operations and Management Area" + CONTACT-INFO + "Juergen Schoenwaelder (Editor) + TU Braunschweig + Bueltenweg 74/75 + 38106 Braunschweig, Germany + Phone: +49 531 391-3289 + EMail: [email protected] + + Send comments to <[email protected]>." + DESCRIPTION + "This MIB module provides commonly used transport + address definitions. + + Copyright (C) The Internet Society (2002). This version of + this MIB module is part of RFC 3419; see the RFC itself for + full legal notices." + + -- Revision log + + REVISION "200211010000Z" + DESCRIPTION + "Initial version, published as RFC 3419." + ::= { mib-2 100 } + + +transportDomains OBJECT IDENTIFIER ::= { transportAddressMIB 1 } + +transportDomainUdpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses." + ::= { transportDomains 1 } + +transportDomainUdpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses." + ::= { transportDomains 2 } + +transportDomainUdpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index." + ::= { transportDomains 3 } + +transportDomainUdpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index." + ::= { transportDomains 4 } + +transportDomainTcpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses." + ::= { transportDomains 5 } + +transportDomainTcpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses." + ::= { transportDomains 6 } + +transportDomainTcpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index." + ::= { transportDomains 7 } + +transportDomainTcpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index." + ::= { transportDomains 8 } + +transportDomainSctpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses. This transport domain usually + represents the primary address on multihomed SCTP + endpoints." + ::= { transportDomains 9 } + +transportDomainSctpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses. This transport domain usually + represents the primary address on multihomed SCTP + endpoints." + ::= { transportDomains 10 } + +transportDomainSctpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index. This transport + domain usually represents the primary address on + multihomed SCTP endpoints." + ::= { transportDomains 11 } + +transportDomainSctpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index. This transport + domain usually represents the primary address on + multihomed SCTP endpoints." + ::= { transportDomains 12 } + +transportDomainLocal OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The Posix Local IPC transport domain. The corresponding + transport address is of type TransportAddressLocal. + + The Posix Local IPC transport domain incorporates the + well-known UNIX domain sockets." + ::= { transportDomains 13 } + +transportDomainUdpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 14 } + +transportDomainTcpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 15 } + +transportDomainSctpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 16 } + +TransportDomain ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents a transport domain. + + Some possible values, such as transportDomainUdpIpv4, are + defined in this module. Other possible values can be + defined in other MIB modules." + SYNTAX OBJECT IDENTIFIER + +-- +-- The enumerated values of the textual convention below should +-- be identical to the last sub-identifier of the OID registered +-- for the same domain. +-- + +TransportAddressType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents a transport domain. This is the + enumerated version of the transport domain registrations + in this MIB module. The enumerated values have the + following meaning: + + unknown(0) unknown transport address type + udpIpv4(1) transportDomainUdpIpv4 + udpIpv6(2) transportDomainUdpIpv6 + udpIpv4z(3) transportDomainUdpIpv4z + udpIpv6z(4) transportDomainUdpIpv6z + tcpIpv4(5) transportDomainTcpIpv4 + tcpIpv6(6) transportDomainTcpIpv6 + tcpIpv4z(7) transportDomainTcpIpv4z + tcpIpv6z(8) transportDomainTcpIpv6z + sctpIpv4(9) transportDomainSctpIpv4 + sctpIpv6(10) transportDomainSctpIpv6 + sctpIpv4z(11) transportDomainSctpIpv4z + sctpIpv6z(12) transportDomainSctpIpv6z + local(13) transportDomainLocal + udpDns(14) transportDomainUdpDns + tcpDns(15) transportDomainTcpDns + sctpDns(16) transportDomainSctpDns + + This textual convention can be used to represent transport + domains in situations where a syntax of TransportDomain is + unwieldy (for example, when used as an index). + + The usage of this textual convention implies that additional + transport domains can only be supported by updating this MIB + module. This extensibility restriction does not apply for the + TransportDomain textual convention which allows MIB authors + to define additional transport domains independently in + other MIB modules." + SYNTAX INTEGER { + unknown(0), + udpIpv4(1), + udpIpv6(2), + udpIpv4z(3), + udpIpv6z(4), + tcpIpv4(5), + tcpIpv6(6), + tcpIpv4z(7), + tcpIpv6z(8), + sctpIpv4(9), + sctpIpv6(10), + sctpIpv4z(11), + sctpIpv6z(12), + local(13), + udpDns(14), + tcpDns(15), + sctpDns(16) + } + +TransportAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a generic transport address. + + A TransportAddress value is always interpreted within the + context of a TransportAddressType or TransportDomain value. + Every usage of the TransportAddress textual convention MUST + specify the TransportAddressType or TransportDomain object + which provides the context. Furthermore, MIB authors SHOULD + define a separate TransportAddressType or TransportDomain + object for each TransportAddress object. It is suggested that + the TransportAddressType or TransportDomain is logically + registered before the object(s) which use the + TransportAddress textual convention if they appear in the + same logical row. + + The value of a TransportAddress object must always be + consistent with the value of the associated + TransportAddressType or TransportDomain object. Attempts + to set a TransportAddress object to a value which is + inconsistent with the associated TransportAddressType or + TransportDomain must fail with an inconsistentValue error. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + SYNTAX OCTET STRING (SIZE (0..255)) + +TransportAddressIPv4 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv4 + address and a port number (as used for example by UDP, + TCP and SCTP): + + octets contents encoding + 1-4 IPv4 address network-byte order + 5-6 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (6)) + +TransportAddressIPv6 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "0a[2x:2x:2x:2x:2x:2x:2x:2x]0a:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv6 + address and a port number (as used for example by UDP, + TCP and SCTP): + + octets contents encoding + 1-16 IPv6 address network-byte order + 17-18 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (18)) + +TransportAddressIPv4z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d%4d:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv4 + address, a zone index and a port number (as used for + example by UDP, TCP and SCTP): + + octets contents encoding + 1-4 IPv4 address network-byte order + 5-8 zone index network-byte order + 9-10 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (10)) + +TransportAddressIPv6z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "0a[2x:2x:2x:2x:2x:2x:2x:2x%4d]0a:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv6 + address, a zone index and a port number (as used for + example by UDP, TCP and SCTP): + + octets contents encoding + 1-16 IPv6 address network-byte order + 17-20 zone index network-byte order + 21-22 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (22)) + +TransportAddressLocal ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1a" + STATUS current + DESCRIPTION + "Represents a POSIX Local IPC transport address: + + octets contents encoding + all POSIX Local IPC address string + + The Posix Local IPC transport domain subsumes UNIX domain + sockets. + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + REFERENCE + "Protocol Independent Interfaces (IEEE POSIX 1003.1g)" + SYNTAX OCTET STRING (SIZE (1..255)) + +TransportAddressDns ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1a" + STATUS current + DESCRIPTION + "Represents a DNS domain name followed by a colon ':' + (ASCII character 0x3A) and a port number in ASCII. + The name SHOULD be fully qualified whenever possible. + + Values of this textual convention are not directly useable as + transport-layer addressing information, and require runtime + resolution. As such, applications that write them must be + prepared for handling errors if such values are not + supported, or cannot be resolved (if resolution occurs at the + time of the management operation). + + The DESCRIPTION clause of TransportAddress objects that may + have TransportAddressDns values must fully describe how (and + when) such names are to be resolved to IP addresses and vice + versa. + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + SYNTAX OCTET STRING (SIZE (1..255)) + +END diff --git a/lib/snmp/priv/conf/agent/target_addr.conf b/lib/snmp/priv/conf/agent/target_addr.conf index 33a5d0d4c4..f48a6645a3 100644 --- a/lib/snmp/priv/conf/agent/target_addr.conf +++ b/lib/snmp/priv/conf/agent/target_addr.conf @@ -3,9 +3,13 @@ %% The data is inserted into the snmpTargetAddrTable defined %% in SNMP-TARGET-MIB, and in the snmpTargeAddrExtTabke defined %% in SNMP-COMMUNITY-MIB. -%% Each row is a 9-tuple: -%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, -%% TMask, MaxMessageSize}. +%% Each row is a 10 or 11-tuple (Domain is optional): +%% {Name, +%% Domain, Ip, Port, +%% Timeout, RetryCount, TagList, ParamsName, EngineId, +%% TMask, MaxMessageSize}. +%% The value of Domain decide the format of the Ip and TMask values. +%% If not present, classic Ipv4 is assumed. %% The EngineId value is only used if Inform-Requests are sent to this %% target. If Informs are not sent, this value is ignored, and can be %% e.g. an empty string. However, if Informs are sent, it is essential diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl index 5644a43345..3debe0a30e 100644 --- a/lib/snmp/src/agent/snmp_community_mib.erl +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -25,7 +25,7 @@ snmpTargetAddrExtTable/3, community2vacm/2, vacm2community/2, get_target_addr_ext_mms/2]). --export([add_community/5, delete_community/1]). +-export([add_community/5, add_community/6, delete_community/1]). -export([check_community/1]). -include("SNMP-COMMUNITY-MIB.hrl"). @@ -128,12 +128,16 @@ read_community_config_files(Dir) -> Comms. check_community({Index, CommunityName, SecName, CtxName, TransportTag}) -> + EngineID = get_engine_id(), + check_community({Index, CommunityName, SecName, + EngineID, CtxName, TransportTag}); +check_community({Index, CommunityName, SecName, + EngineID, CtxName, TransportTag}) -> snmp_conf:check_string(Index,{gt,0}), snmp_conf:check_string(CommunityName), snmp_conf:check_string(SecName), snmp_conf:check_string(CtxName), snmp_conf:check_string(TransportTag), - EngineID = get_engine_id(), Comm = {Index, CommunityName, SecName, EngineID, CtxName, TransportTag, ?'StorageType_nonVolatile', ?'RowStatus_active'}, {ok, Comm}; @@ -173,6 +177,13 @@ table_del_row(Tab, Key) -> %% FIXME: does not work with mnesia add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Community = {Idx, CommName, SecName, CtxName, TransportTag}, + do_add_community(Community). + +add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> + Community = {Idx, CommName, SecName, EngineId, CtxName, TransportTag}, + do_add_community(Community). + +do_add_community(Community) -> case (catch check_community(Community)) of {ok, Row} -> Key = element(1, Row), diff --git a/lib/snmp/src/agent/snmp_notification_mib.erl b/lib/snmp/src/agent/snmp_notification_mib.erl index 1cd69b430f..3da5766b44 100644 --- a/lib/snmp/src/agent/snmp_notification_mib.erl +++ b/lib/snmp/src/agent/snmp_notification_mib.erl @@ -273,9 +273,12 @@ find_targets(Key, TargAddrs, Db, Res) -> get_targets([{TagList, Addr, TargetName, Params, Timeout, Retry}|T], Tag, Type, Name) -> case snmp_misc:is_tag_member(Tag, TagList) of - true -> [{Name, {Addr, TargetName, Params, type(Type, Timeout, Retry)}}| - get_targets(T, Tag, Type, Name)]; + true -> + ?vtrace("tag ~w *is* member", [Tag]), + [{Name, {Addr, TargetName, Params, type(Type, Timeout, Retry)}}| + get_targets(T, Tag, Type, Name)]; false -> + ?vtrace("tag ~w is *not* member", [Tag]), get_targets(T, Tag, Type, Name) end; get_targets([], _Tag, _Type, _Name) -> diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index 270a5fd5b6..b2f2417b02 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -26,16 +26,18 @@ snmpTargetParamsTable/1, snmpTargetParamsTable/3, get_target_addrs/0, get_target_engine_id/1, set_target_engine_id/2, is_valid_tag/3, get/3, table_next/2]). --export([add_addr/10, delete_addr/1, +-export([add_addr/10, add_addr/11, delete_addr/1, add_params/5, delete_params/1]). -export([check_target_addr/1, check_target_params/1]). +-export([default_domain/0]). --include("snmp_types.hrl"). --include("snmp_tables.hrl"). --include("SNMP-TARGET-MIB.hrl"). --include("SNMPv2-TC.hrl"). --include("SNMPv2-TM.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/include/snmp_tables.hrl"). +-include_lib("snmp/include/SNMP-TARGET-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TC.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). -define(VMODULE,"TARGET-MIB"). -include("snmp_verbosity.hrl"). @@ -49,6 +51,12 @@ %%----------------------------------------------------------------- + +default_domain() -> + snmpUDPDomain. + + +%%----------------------------------------------------------------- %% Func: configure/1 %% Args: Dir is the directory where the configuration files are found. %% Purpose: If the tables doesn't exist, this function reads @@ -139,39 +147,51 @@ read_target_config_files(Dir) -> %% {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId, %% TMask, MMS} %%----------------------------------------------------------------- -check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, - Params, EngineId, TMask, MMS}) -> + +check_target_addr({Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}) -> ?vtrace("check target address with:" - "~n Name: ~s" - "~n Ip: ~p" - "~n Udp: ~p" - "~n Timeout: ~p" - "~n RetryCount: ~p" - "~n TagList: ~p" - "~n Params: ~p" - "~n EngineId: ~p" - "~n TMask: ~p" - "~n MMS: ~p", - [Name,Ip,Udp,Timeout,RetryCount, - TagList,Params,EngineId,TMask,MMS]), + "~n Name: ~s" + "~n Domain: ~p" + "~n Ip: ~p" + "~n Udp: ~p" + "~n Timeout: ~p" + "~n RetryCount: ~p" + "~n TagList: ~p" + "~n Params: ~p" + "~n EngineId: ~p" + "~n TMask: ~p" + "~n MMS: ~p", + [Name, + Domain, Ip, Udp, + Timeout, RetryCount, + TagList, Params, EngineId, TMask, MMS]), snmp_conf:check_string(Name,{gt,0}), - snmp_conf:check_ip(Ip), + snmp_conf:check_domain(Domain), + snmp_conf:check_ip(Domain, Ip), snmp_conf:check_integer(Udp, {gt, 0}), snmp_conf:check_integer(Timeout, {gte, 0}), snmp_conf:check_integer(RetryCount, {gte,0}), snmp_conf:check_string(TagList), snmp_conf:check_string(Params), check_engine_id(EngineId), - TAddr = Ip ++ [Udp div 256, Udp rem 256], - check_mask(TMask, TAddr), + TAddress = snmp_conf:mk_taddress(Domain, Ip, Udp), + TDomain = snmp_conf:mk_tdomain(Domain), + check_tmask(TDomain, TMask, TAddress), snmp_conf:check_packet_size(MMS), ?vtrace("check target address done",[]), - - Addr = {Name, ?snmpUDPDomain, TAddr, Timeout, + Addr = {Name, TDomain, TAddress, Timeout, RetryCount, TagList, Params, ?'StorageType_nonVolatile', ?'RowStatus_active', EngineId, TMask, MMS}, % Values for Augmenting table in SNMP-COMMUNITY-MIB {ok, Addr}; +check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}) -> + Domain = default_domain(), + check_target_addr({Name, + Domain, Ip, Udp, + Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}); check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId}) -> check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, @@ -194,12 +214,13 @@ check_engine_id(discovery) -> check_engine_id(EngineId) -> snmp_conf:check_string(EngineId). -check_mask([], _TAddr) -> + +check_tmask(_TDomain, [], _TAddress) -> ok; -check_mask(TMask, TAddr) when length(TMask) == length(TAddr) -> - snmp_conf:check_taddress(TMask); -check_mask(TMask, _TAddr) -> - throw({error, {invalid_mask, TMask}}). +check_tmask(TDomain, TMask, TAddress) when length(TMask) =:= length(TAddress) -> + snmp_conf:check_taddress(TDomain, TMask); +check_tmask(_TDomain, TMask, _TAddr) -> + throw({error, {invalid_tmask, TMask}}). %%----------------------------------------------------------------- @@ -261,7 +282,13 @@ table_del_row(Tab, Key) -> add_addr(Name, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> - Addr = {Name, Ip, Port, Timeout, Retry, TagList, + Domain = default_domain(), + add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, + Params, EngineId, TMask, MMS). + +add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, + Params, EngineId, TMask, MMS) -> + Addr = {Name, Domain, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS}, case (catch check_target_addr(Addr)) of {ok, Row} -> @@ -341,8 +368,11 @@ maybe_create_var(Var) -> init_var(Var) -> ets:insert(snmp_agent_table, {Var, 0}). vars() -> - [snmpUnavailableContexts, - snmpUnknownContexts]. + [ + snmpUnavailableContexts, + snmpUnknownContexts + ]. + %%----------------------------------------------------------------- %% API functions @@ -562,6 +592,8 @@ snmpTargetAddrTable(print) -> Prefix, element(?snmpTargetAddrTDomain, Row), case element(?snmpTargetAddrTDomain, Row) of ?snmpUDPDomain -> udp; + ?transportDomainUdpIpv4 -> udpIpv4; + ?transportDomainUdpIpv6 -> udpIpv6; _ -> undefined end, Prefix, element(?snmpTargetAddrTAddress, Row), @@ -610,14 +642,14 @@ snmpTargetAddrTable(get, RowIndex, Cols) -> snmpTargetAddrTable(get_next, RowIndex, Cols) -> next(snmpTargetAddrTable, RowIndex, Cols); snmpTargetAddrTable(set, RowIndex, Cols0) -> - %% BMK BMK BMK - case (catch verify_targetAddrTable_cols(Cols0, [])) of + %% BMK BMK + case (catch verify_targetAddrTable_cols(Cols0)) of {ok, Cols} -> snmp_notification_mib:invalidate_cache(), %% Add columns for augmenting table snmpTargetAddrExtTable and for - %% target engine ID. Target engine ID is set to "". The function + %% target engine ID. Target engine ID is set to "". The function %% get_target_engine_id will return "" unless a value is set using - %% set_target_engine_id. If it is "" Informs can't be sent to the + %% set_target_engine_id. If it is "" Informs can't be sent to the %% target. NCols = Cols ++ [{?snmpTargetAddrEngineId, ""}, {?snmpTargetAddrTMask, []}, @@ -628,12 +660,12 @@ snmpTargetAddrTable(set, RowIndex, Cols0) -> Error end; snmpTargetAddrTable(is_set_ok, RowIndex, Cols0) -> - case (catch verify_targetAddrTable_cols(Cols0, [])) of + case (catch verify_targetAddrTable_cols(Cols0)) of {ok, Cols} -> %% Add columns for augmenting table snmpTargetAddrExtTable and for - %% target engine ID. Target engine ID is set to "". The function + %% target engine ID. Target engine ID is set to "". The function %% get_target_engine_id will return "" unless a value is set using - %% set_target_engine_id. If it is "" Informs can't be sent to the + %% set_target_engine_id. If it is "" Informs can't be sent to the %% target. NCols = Cols ++ [{?snmpTargetAddrEngineId, ""}, {?snmpTargetAddrTMask, []}, @@ -647,55 +679,83 @@ snmpTargetAddrTable(Op, Arg1, Arg2) -> Db = db(snmpTargetAddrTable), snmp_generic:table_func(Op, Arg1, Arg2, Db). +verify_targetAddrTable_cols(Cols) -> + ValidCols0 = verify_targetAddrTable_cols(Cols, []), + %% Make a last pass to verify TDomain and TAddress. + ValidCols0. + verify_targetAddrTable_cols([], Cols) -> {ok, lists:reverse(Cols)}; -verify_targetAddrTable_cols([{Col, Val0}|Cols], Acc) -> - Val = verify_targetAddrTable_col(Col, Val0), - verify_targetAddrTable_cols(Cols, [{Col, Val}|Acc]). +verify_targetAddrTable_cols([{Col, Val0}|Cols], ValidCols) -> + Val = verify_targetAddrTable_col(Col, Val0, ValidCols), + verify_targetAddrTable_cols(Cols, [{Col, Val}|ValidCols]). -verify_targetAddrTable_col(?snmpTargetAddrName, Name) -> +verify_targetAddrTable_col(?snmpTargetAddrName, Name, _) -> case (catch snmp_conf:check_string(Name)) of ok -> Name; _ -> wrongValue(?snmpTargetAddrName) end; -verify_targetAddrTable_col(?snmpTargetAddrTAddress, TAddr) -> - case (catch snmp_conf:check_taddress(TAddr)) of +verify_targetAddrTable_col(?snmpTargetAddrTDomain, TDomain, _) -> + case (catch snmp_conf:check_tdomain(TDomain)) of ok -> - TAddr; + TDomain; _ -> - wrongValue(?snmpTargetAddrTAddress) + wrongValue(?snmpTargetAddrTDomain) + end; +%% In order to (properly) validate the TAddress, +%% the TDomain must already have been validated +%% (the format of TAddress depends on TDomain). +verify_targetAddrTable_col(?snmpTargetAddrTAddress, TAddress, ValidCols) -> + case lists:keysearch(?snmpTargetAddrTDomain, 1, ValidCols) of + {value, {?snmpTargetAddrTDomain, TDomain}} -> + case (catch snmp_conf:check_taddress(TDomain, TAddress)) of + ok -> + TAddress; + _ -> + wrongValue(?snmpTargetAddrTAddress) + end; + false -> + %% The user did not provide us with a TDomain, which + %% must mean that he/she intends to use the old domain. + TDomain = snmp_conf:mk_tdomain(default_domain()), + case (catch snmp_conf:check_taddress(TDomain, TAddress)) of + ok -> + TAddress; + _ -> + wrongValue(?snmpTargetAddrTAddress) + end end; -verify_targetAddrTable_col(?snmpTargetAddrTimeout, Timeout) -> +verify_targetAddrTable_col(?snmpTargetAddrTimeout, Timeout, _) -> case (catch snmp_conf:check_integer(Timeout)) of ok when Timeout >= 0 -> Timeout; _ -> wrongValue(?snmpTargetAddrTimeout) end; -verify_targetAddrTable_col(?snmpTargetAddrRetryCount, Retry) -> +verify_targetAddrTable_col(?snmpTargetAddrRetryCount, Retry, _) -> case (catch snmp_conf:check_integer(Retry)) of ok when Retry >= 0 -> Retry; _ -> wrongValue(?snmpTargetAddrRetryCount) end; -verify_targetAddrTable_col(?snmpTargetAddrTagList, TagList) -> +verify_targetAddrTable_col(?snmpTargetAddrTagList, TagList, _) -> case (catch snmp_conf:check_string(TagList)) of ok -> TagList; _ -> wrongValue(?snmpTargetAddrTagList) end; -verify_targetAddrTable_col(?snmpTargetAddrParams, Params) -> +verify_targetAddrTable_col(?snmpTargetAddrParams, Params, _) -> case (catch snmp_conf:check_string(Params)) of ok -> Params; _ -> wrongValue(?snmpTargetAddrParams) end; -verify_targetAddrTable_col(_, Val) -> +verify_targetAddrTable_col(_, Val, _) -> Val. diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 22fbd33add..b2e4f253ab 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -60,6 +60,7 @@ register_subagent/3, unregister_subagent/2, + send_notification2/3, send_notification/3, send_notification/4, send_notification/5, send_notification/6, send_notification/7, send_trap/3, send_trap/4, @@ -108,8 +109,9 @@ -export([print_mib_info/0, print_mib_tables/0, print_mib_variables/0]). -include("snmpa_atl.hrl"). +-include("snmpa_internal.hrl"). --define(EXTRA_INFO, undefined). +-define(DISCO_EXTRA_INFO, undefined). %%----------------------------------------------------------------- @@ -596,22 +598,56 @@ set_request_limit(Agent, NewLimit) -> %% - +send_notification2(Agent, Notification, SendOpts) -> + snmpa_agent:send_notification(Agent, Notification, SendOpts). + send_notification(Agent, Notification, Recv) -> - send_notification(Agent, Notification, Recv, "", "", []). + SendOpts = + [ + {receiver, Recv}, + {varbinds, []}, + {name, ""}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, Varbinds) -> - send_notification(Agent, Notification, Recv, "", "", Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, ""}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, Varbinds) -> - send_notification(Agent, Notification, Recv, NotifyName, "", Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, ContextName, Varbinds) when (is_list(NotifyName) andalso is_list(ContextName) andalso is_list(Varbinds)) -> - snmpa_agent:send_trap(Agent, Notification, NotifyName, - ContextName, Recv, Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ContextName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, ContextName, Varbinds, LocalEngineID) @@ -619,8 +655,16 @@ send_notification(Agent, Notification, Recv, is_list(ContextName) andalso is_list(Varbinds) andalso is_list(LocalEngineID)) -> - snmpa_agent:send_trap(Agent, Notification, NotifyName, - ContextName, Recv, Varbinds, LocalEngineID). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ContextName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, + {local_engine_id, LocalEngineID} + ], + send_notification2(Agent, Notification, SendOpts). %% Kept for backwards compatibility send_trap(Agent, Trap, Community) -> @@ -655,7 +699,7 @@ discovery(TargetName, Notification, Varbinds, DiscoHandler) discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler). discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler) -> - ExtraInfo = ?EXTRA_INFO, + ExtraInfo = ?DISCO_EXTRA_INFO, discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler, ExtraInfo). diff --git a/lib/snmp/src/agent/snmpa_acm.erl b/lib/snmp/src/agent/snmpa_acm.erl index 6ad4f0b442..30bd34a205 100644 --- a/lib/snmp/src/agent/snmpa_acm.erl +++ b/lib/snmp/src/agent/snmpa_acm.erl @@ -62,11 +62,13 @@ %% {error, Reason} | %% {discarded, Variable, Reason} %% Types: Pdu = #pdu -%% ACMData = acm_data() = {community, Community, Address} | -%% {v3, MsgID, SecModel, SecName, SecLevel, -%% ContextEngineID, ContextName, SecData} +%% ACMData = acm_data() = +%% {community, SecModel, Community, TDomain, TAddress} | +%% {v3, MsgID, SecModel, SecName, SecLevel, +%% ContextEngineID, ContextName, SecData} %% Community = string() -%% Address = ip() ++ udp() (list) +%% TDomain = ?transportDomainUdpIpv4 | ?transportDomainUdpIpv6 +%% TAddress = ip() ++ udp() (list) %% MsgID = integer() <not used> %% SecModel = ?SEC_* (see snmp_types.hrl) %% SecName = string() @@ -114,7 +116,10 @@ error2status(_) -> genErr. %% discarded: no error response is sent %% authentication_failure: no error response is sent, a trap is generated %%----------------------------------------------------------------- -init_ca(Pdu, {community, SecModel, Community, TAddr}) -> +init_ca(Pdu, {community, SecModel, Community, TAddress}) -> + TDomain = snmp_conf:mk_tdomain(snmp_target_mib:default_domain()), + init_ca(Pdu, {community, SecModel, Community, TDomain, TAddress}); +init_ca(Pdu, {community, SecModel, Community, TDomain, TAddress}) -> %% This is a v1 or v2c request. Use SNMP-COMMUNITY-MIB to %% map the community to vacm parameters. ?vtrace("check access for ~n" @@ -126,18 +131,18 @@ init_ca(Pdu, {community, SecModel, Community, TAddr}) -> _ -> read end, ?vtrace("View type: ~p", [ViewType]), - CaCacheKey = {Community, SecModel, TAddr, ViewType}, + CaCacheKey = {Community, SecModel, TDomain, TAddress, ViewType}, case check_ca_cache(CaCacheKey) of false -> - case snmp_community_mib:community2vacm(Community, - {?snmpUDPDomain,TAddr}) of + case snmp_community_mib:community2vacm(Community, + {TDomain, TAddress}) of {SecName, _ContextEngineId, ContextName} -> %% Maybe we should check that the contextEngineID %% matches the local engineID? %% It better, since we don't impl. proxy. ?vtrace("get mib view" "~n Security name: ~p" - "~n Context name: ~p",[SecName,ContextName]), + "~n Context name: ~p",[SecName, ContextName]), case snmpa_vacm:get_mib_view(ViewType, SecModel, SecName, ?'SnmpSecurityLevel_noAuthNoPriv', ContextName) of @@ -153,7 +158,7 @@ init_ca(Pdu, {community, SecModel, Community, TAddr}) -> end; undefined -> {authentication_failure, snmpInBadCommunityNames, - {bad_community_name, TAddr, Community}} + {bad_community_name, TDomain, TAddress, Community}} end; Res -> Res @@ -219,6 +224,7 @@ upd_ca_cache(KeyVal) -> invalidate_ca_cache() -> erase(ca_cache). + %%----------------------------------------------------------------- %% Func: check(Res) -> {ok, MibView} | {discarded, Variable, Reason} %% Args: Res = {ok, AccessFunc} | diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index f70885b2ec..e4cfeddb6a 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -30,7 +30,7 @@ -export([subagent_set/2, load_mibs/2, unload_mibs/2, which_mibs/1, whereis_mib/2, info/1, register_subagent/3, unregister_subagent/2, - send_trap/6, send_trap/7, + send_notification/3, register_notification_filter/5, unregister_notification_filter/2, which_notification_filter/1, @@ -62,10 +62,16 @@ -export([increment_counter/3]). -export([restart_worker/1, restart_set_worker/1]). +%% For backward compatibillity +-export([send_trap/6, send_trap/7]). + %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, tr_var/2, tr_varbind/1, - handle_pdu/7, worker/2, worker_loop/1, do_send_trap/7]). + handle_pdu/7, worker/2, worker_loop/1, + do_send_trap/7, do_send_trap/8]). + +-include("snmpa_internal.hrl"). -ifndef(default_verbosity). -define(default_verbosity,silence). @@ -527,6 +533,11 @@ which_notification_filter(Agent) -> call(Agent, which_notification_filter). +send_notification(Agent, Notification, SendOpts) -> + Msg = {send_notif, Notification, SendOpts}, + maybe_call(Agent, Msg). + +%% <BACKWARD-COMPAT> send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> ?d("send_trap -> entry with" "~n self(): ~p" @@ -538,13 +549,33 @@ send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> "~n Varbinds: ~p", [self(), Agent, wis(Agent), Trap, NotifyName, CtxName, Recv, Varbinds]), - Msg = {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds}, - case (wis(Agent) =:= self()) of - false -> - call(Agent, Msg); - true -> - Agent ! Msg - end. + SendOpts = [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, CtxName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification(Agent, Trap, SendOpts). + +%% send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> +%% ?d("send_trap -> entry with" +%% "~n self(): ~p" +%% "~n Agent: ~p [~p]" +%% "~n Trap: ~p" +%% "~n NotifyName: ~p" +%% "~n CtxName: ~p" +%% "~n Recv: ~p" +%% "~n Varbinds: ~p", +%% [self(), Agent, wis(Agent), +%% Trap, NotifyName, CtxName, Recv, Varbinds]), +%% Msg = {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds}, +%% case (wis(Agent) =:= self()) of +%% false -> +%% call(Agent, Msg); +%% true -> +%% Agent ! Msg +%% end. send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> ?d("send_trap -> entry with" @@ -558,14 +589,38 @@ send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> "~n LocalEngineID: ~p", [self(), Agent, wis(Agent), Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]), - Msg = - {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID}, - case (wis(Agent) =:= self()) of - false -> - call(Agent, Msg); - true -> - Agent ! Msg - end. + SendOpts = [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, CtxName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, + {local_engine_id, LocalEngineID} + ], + send_notification(Agent, Trap, SendOpts). + +%% send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> +%% ?d("send_trap -> entry with" +%% "~n self(): ~p" +%% "~n Agent: ~p [~p]" +%% "~n Trap: ~p" +%% "~n NotifyName: ~p" +%% "~n CtxName: ~p" +%% "~n Recv: ~p" +%% "~n Varbinds: ~p" +%% "~n LocalEngineID: ~p", +%% [self(), Agent, wis(Agent), +%% Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]), +%% Msg = +%% {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID}, +%% case (wis(Agent) =:= self()) of +%% false -> +%% call(Agent, Msg); +%% true -> +%% Agent ! Msg +%% end. + +%% </BACKWARD-COMPAT> %% -- Discovery functions -- @@ -655,7 +710,14 @@ wis(Atom) when is_atom(Atom) -> forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds) -> - Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds}. + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo). + +forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo) -> + Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo}. %%----------------------------------------------------------------- @@ -745,6 +807,22 @@ handle_info(worker_available, S) -> ?vdebug("worker available",[]), {noreply, S#state{worker_state = ready}}; +handle_info({send_notif, Notification, SendOpts}, S) -> + ?vlog("[handle_info] send trap request:" + "~n Notification: ~p" + "~n SendOpts: ~p", + [Notification, SendOpts]), + case (catch handle_send_trap(cast, S, Notification, SendOpts)) of + {ok, NewS} -> + {noreply, NewS}; + {'EXIT', R} -> + ?vinfo("Trap not sent:~n ~p", [R]), + {noreply, S}; + _ -> + {noreply, S} + end; + +%% <BACKWARD-COMPAT> handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) -> ?vlog("[handle_info] send trap request:" "~n Trap: ~p" @@ -753,9 +831,10 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) -> "~n Recv: ~p" "~n Varbinds: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, - case catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID) of + case (catch handle_send_trap(S, Trap, NotifyName, ContextName, + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -775,8 +854,9 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds, "~n Varbinds: ~p" "~n LocalEngineID: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), - case catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID) of + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + case (catch handle_send_trap(S, Trap, NotifyName, ContextName, + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -785,8 +865,31 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds, _ -> {noreply, S} end; +%% </BACKWARD-COMPAT> handle_info({forward_trap, TrapRecord, NotifyName, ContextName, + Recv, Varbinds, ExtraInfo}, S) -> + ?vlog("[handle_info] forward trap request:" + "~n TrapRecord: ~p" + "~n NotifyName: ~p" + "~n ContextName: ~p" + "~n Recv: ~p" + "~n Varbinds: ~p", + [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), + LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, + case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, + Recv, Varbinds, LocalEngineID, ExtraInfo)) of + {ok, NewS} -> + {noreply, NewS}; + {'EXIT', R} -> + ?vinfo("Trap not sent:~n ~p", [R]), + {noreply, S}; + _ -> + {noreply, S} + end; + +%% <BACKWARD-COMPAT> +handle_info({forward_trap, TrapRecord, NotifyName, ContextName, Recv, Varbinds}, S) -> ?vlog("[handle_info] forward trap request:" "~n TrapRecord: ~p" @@ -795,9 +898,10 @@ handle_info({forward_trap, TrapRecord, NotifyName, ContextName, "~n Recv: ~p" "~n Varbinds: ~p", [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -806,6 +910,7 @@ handle_info({forward_trap, TrapRecord, NotifyName, ContextName, _ -> {noreply, S} end; +%% </BACKWARD-COMPAT> handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) -> ?vlog("[handle_info] backup done:" @@ -908,6 +1013,23 @@ handle_call(restart_set_worker, _From, #state{set_worker = Pid} = S) -> end, {reply, ok, S}; +handle_call({send_notif, Notification, SendOpts}, _From, S) -> + ?vlog("[handle_info] send trap request:" + "~n Notification: ~p" + "~n SendOpts: ~p", + [Notification, SendOpts]), + case (catch handle_send_trap(call, S, Notification, SendOpts)) of + {ok, NewS} -> + {reply, ok, NewS}; + {'EXIT', Reason} -> + ?vinfo("Trap not sent:~n ~p", [Reason]), + {reply, {error, {send_failed, Reason}}, S}; + _ -> + ?vinfo("Trap not sent", []), + {reply, {error, send_failed}, S} + end; + +%% <BACKWARD-COMPAT> handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, _From, S) -> ?vlog("[handle_call] send trap request:" @@ -917,19 +1039,20 @@ handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, "~n Recv: ~p" "~n Varbinds: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = case S#state.type of master_agent -> ?DEFAULT_LOCAL_ENGINE_ID; _ -> %% subagent - - %% we don't need this, eventually the trap sent request - %% will reach the master-agent and then it will look up - %% the proper engine id. + %% we don't need this now, eventually the trap send + %% request will reach the master-agent and then it + %% will look up the proper engine id. ignore end, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {reply, ok, NewS}; {'EXIT', Reason} -> @@ -951,8 +1074,9 @@ handle_call({send_trap, Trap, NotifyName, "~n Varbinds: ~p" "~n LocalEngineID: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {reply, ok, NewS}; {'EXIT', Reason} -> @@ -962,6 +1086,7 @@ handle_call({send_trap, Trap, NotifyName, ?vinfo("Trap not sent", []), {reply, {error, send_failed}, S} end; +%% </BACKWARD-COMPAT> handle_call({discovery, TargetName, Notification, ContextName, Vbs, DiscoHandler, @@ -1470,7 +1595,10 @@ handle_backup_res([{Who, Crap}|Results], Acc) -> %% because we (for some reason) support the function %% snmpa:current_community(). %%----------------------------------------------------------------- -cheat({community, _SecModel, Community, _IpUdp}, Address, ContextName) -> +cheat({community, SecModel, Community, _TAddress}, Address, ContextName) -> + {Community, Address, ContextName}; +cheat({community, _SecModel, Community, _TDomain, _TAddress}, + Address, ContextName) -> {Community, Address, ContextName}; cheat(_, Address, ContextName) -> {"", Address, ContextName}. @@ -1523,19 +1651,24 @@ spawn_thread(Vsn, Pdu, PduMS, ACMData, Address, Extra) -> proc_lib:spawn_link(?MODULE, handle_pdu, Args). spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> Dict = get(), proc_lib:spawn_link(?MODULE, do_send_trap, [TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, Dict]). + Recv, Vbs, LocalEngineID, ExtraInfo, Dict]). do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, Dict) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, Dict). +do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, Dict) -> lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), put(sname,trap_sender_short_name(get(sname))), ?vlog("starting",[]), snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, get(net_if)). + LocalEngineID, ExtraInfo, get(net_if)). worker(Master, Dict) -> lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), @@ -1550,21 +1683,34 @@ worker_loop(Master) -> handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, Extra), Master ! worker_available; - %% We don't trap exits! + %% We don't trap EXITs! {TrapRec, NotifyName, ContextName, Recv, Vbs} -> ?vtrace("worker_loop -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, - ContextName, Recv, Vbs, get(net_if)), + ContextName, Recv, Vbs, + ?DEFAULT_NOTIF_EXTRA_INFO, + get(net_if)), Master ! worker_available; - %% We don't trap exits! + %% We don't trap EXITs! {send_trap, TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID} -> ?vtrace("worker_loop -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, - ContextName, Recv, Vbs, LocalEngineID, + ContextName, Recv, Vbs, + LocalEngineID, ?DEFAULT_NOTIF_EXTRA_INFO, + get(net_if)), + Master ! worker_available; + + {send_trap, + TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, ExtraInfo} -> + ?vtrace("worker_loop -> send trap:" + "~n ~p", [TrapRec]), + snmpa_trap:send_trap(TrapRec, NotifyName, + ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, get(net_if)), Master ! worker_available; @@ -1713,34 +1859,79 @@ handle_acm_error(Vsn, Reason, Pdu, ACMData, Address, Extra) -> ok end. +get_opt(Key, Default, SendOpts) -> + case lists:keysearch(Key, 1, SendOpts) of + {value, {Key, Value}} -> + Value; + false -> + Default + end. -handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> +handle_send_trap(call, #state{type = master_agent} = S, + Notification, SendOpts) -> + SendOpts2 = + case lists:keymember(local_engine_id, 1, SendOpts) of + true -> + SendOpts; + false -> + [{local_engine_id, ?DEFAULT_LOCAL_ENGINE_ID}|SendOpts] + end, + handle_send_trap(S, Notification, SendOpts2); +handle_send_trap(call, S, Notification, SendOpts) -> + SendOpts2 = + case lists:keymember(local_engine_id, 1, SendOpts) of + true -> + SendOpts; + false -> + %% subagent - + %% we don't need this now, eventually the trap send + %% request will reach the master-agent and then it + %% will look up the proper engine id. + [{local_engine_id, ignore}|SendOpts] + end, + handle_send_trap(S, Notification, SendOpts2); +handle_send_trap(_, S, Notification, SendOpts) -> + handle_send_trap(S, Notification, SendOpts). + +handle_send_trap(S, Notification, SendOpts) -> + NotifyName = get_opt(name, "", SendOpts), + ContextName = get_opt(context, "", SendOpts), + Recv = get_opt(receiver, no_receiver, SendOpts), + Varbinds = get_opt(varbinds, [], SendOpts), + ExtraInfo = get_opt(extra, ?DEFAULT_NOTIF_EXTRA_INFO, SendOpts), + LocalEngineID = + get_opt(local_engine_id, ?DEFAULT_LOCAL_ENGINE_ID, SendOpts), + handle_send_trap(S, Notification, NotifyName, ContextName, Recv, Varbinds, + LocalEngineID, ExtraInfo). + +handle_send_trap(#state{type = Type} = S, + Notification, NotifyName, ContextName, Recv, Varbinds, + LocalEngineID, ExtraInfo) -> ?vtrace("handle_send_trap -> entry with" - "~n S#state.type: ~p" + "~n Agent type: ~p" "~n TrapName: ~p" "~n NotifyName: ~p" "~n ContextName: ~p" "~n LocalEngineID: ~p", - [S#state.type, TrapName, NotifyName, ContextName, LocalEngineID]), - case snmpa_trap:construct_trap(TrapName, Varbinds) of + [Type, Notification, NotifyName, ContextName, LocalEngineID]), + case snmpa_trap:construct_trap(Notification, Varbinds) of {ok, TrapRecord, VarList} -> ?vtrace("handle_send_trap -> construction complete: " "~n TrapRecord: ~p" "~n VarList: ~p", [TrapRecord, VarList]), - case S#state.type of + case Type of subagent -> ?vtrace("handle_send_trap -> [sub] forward trap",[]), maybe_forward_trap(S, TrapRecord, NotifyName, - ContextName, Recv, VarList), + ContextName, Recv, VarList, ExtraInfo), {ok, S}; master_agent -> ?vtrace("handle_send_trap -> " "[master] handle send trap",[]), maybe_send_trap(S, TrapRecord, NotifyName, ContextName, Recv, VarList, - LocalEngineID) + LocalEngineID, ExtraInfo) end; error -> error @@ -1748,7 +1939,7 @@ handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds, maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, - TrapRec, NotifyName, ContextName, Recv, V) -> + TrapRec, NotifyName, ContextName, Recv, V, ExtraInfo) -> ?vtrace("maybe_forward_trap -> entry with" "~n NFs: ~p", [NFs]), case filter_notification(NFs, [], TrapRec) of @@ -1764,13 +1955,15 @@ maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, {send, [], TrapRec2} -> ?vtrace("maybe_forward_trap -> forward trap:" "~n ~p", [TrapRec2]), - forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V), + forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, + ExtraInfo), {ok, S}; {send, Removed, TrapRec2} -> ?vtrace("maybe_forward_trap -> forward trap:" "~n ~p", [TrapRec2]), - forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V), + forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, + ExtraInfo), NFs2 = del_notification_filter(Removed, NFs), {ok, S#state{nfilters = NFs2}} end. @@ -1778,7 +1971,7 @@ maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, maybe_send_trap(#state{nfilters = NFs} = S, TrapRec, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> ?vtrace("maybe_send_trap -> entry with" "~n NFs: ~p", [NFs]), case filter_notification(NFs, [], TrapRec) of @@ -1796,7 +1989,7 @@ maybe_send_trap(#state{nfilters = NFs} = S, "~n ~p", [TrapRec2]), do_handle_send_trap(S, TrapRec2, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID); + LocalEngineID, ExtraInfo); {send, Removed, TrapRec2} -> ?vtrace("maybe_send_trap -> send trap:" @@ -1804,36 +1997,37 @@ maybe_send_trap(#state{nfilters = NFs} = S, NFs2 = del_notification_filter(Removed, NFs), do_handle_send_trap(S#state{nfilters = NFs2}, TrapRec2, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) + LocalEngineID, ExtraInfo) end. do_handle_send_trap(S, TrapRec, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> Vbs = snmpa_trap:try_initialise_vars(get(mibserver), Varbinds), case S#state.type of subagent -> forward_trap(S#state.parent, TrapRec, NotifyName, ContextName, - Recv, Vbs), + Recv, Vbs, ExtraInfo), {ok, S}; master_agent when S#state.multi_threaded =:= false -> ?vtrace("do_handle_send_trap -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, get(net_if)), + Recv, Vbs, LocalEngineID, ExtraInfo, + get(net_if)), {ok, S}; master_agent when S#state.worker_state =:= busy -> %% Main worker busy => create new worker ?vtrace("do_handle_send_trap -> main worker busy: " "spawn a trap sender", []), spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID), + LocalEngineID, ExtraInfo), {ok, S}; master_agent -> %% Send to main worker ?vtrace("do_handle_send_trap -> send to main worker",[]), S#state.worker ! {send_trap, TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID}, + LocalEngineID, ExtraInfo}, {ok, S#state{worker_state = busy}} end. @@ -1932,7 +2126,7 @@ send_discovery(S, From, TargetName, Record, ContextName, InitVars, DiscoHandler, ExtraInfo) -> case snmpa_trap:send_discovery(TargetName, Record, ContextName, - InitVars, get(net_if)) of + InitVars, get(net_if), ExtraInfo) of {ok, Sender, SecLevel} -> Disco = #disco{from = From, rec = Record, @@ -2009,9 +2203,12 @@ handle_discovery_response(#state{disco = #disco{target = TargetName, #disco{rec = Record, ctx = ContextName, ivbs = InitVars} = Disco, - case snmpa_trap:send_discovery(TargetName, Record, + case snmpa_trap:send_discovery(TargetName, + Record, ContextName, - InitVars, get(net_if)) of + InitVars, + get(net_if), + ExtraInfo) of {ok, Sender, _SecLevel} -> ?vdebug("handle_discovery_response(1) -> " "stage 2 trap sent", []), @@ -3971,6 +4168,14 @@ user_err(F, A) -> %% --------------------------------------------------------------------- +maybe_call(Server, Req) -> + case (wis(Server) =:= self()) of + false -> + call(Server, Req); + true -> + Server ! Req + end. + call(Server, Req) -> gen_server:call(Server, Req, infinity). diff --git a/lib/snmp/src/agent/snmpa_authentication_service.erl b/lib/snmp/src/agent/snmpa_authentication_service.erl index 572fab7fbf..d406c58ee4 100644 --- a/lib/snmp/src/agent/snmpa_authentication_service.erl +++ b/lib/snmp/src/agent/snmpa_authentication_service.erl @@ -29,11 +29,12 @@ behaviour_info(_) -> %%----------------------------------------------------------------- %% init_check_access(Pdu, ACMData) %% Pdu = #pdu -%% ACMData = acm_data() = {community, Community, Address} | -%% {v3, MsgID, SecModel, SecName, SecLevel, -%% ContextEngineID, ContextName, SecData} +%% ACMData = acm_data() = {community, SecModel, Community, TDomain, TAddress} | +%% {v3, MsgID, SecModel, SecName, SecLevel, +%% ContextEngineID, ContextName, SecData} %% Community = string() -%% Address = ip() ++ udp() (list) +%% TDomain = ?transportDomainUdpIpv4 | ?transportDomainUdpIpv6 +%% TAddress = ip() ++ udp() (list) %% MsgID = integer() <not used> %% SecModel = ?SEC_* (see snmp_types.hrl) %% SecName = string() diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index b4fc716b3e..4e5aab5319 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -48,7 +48,7 @@ %% target_addr.conf target_addr_entry/5, target_addr_entry/6, - target_addr_entry/8, target_addr_entry/10, + target_addr_entry/8, target_addr_entry/10, target_addr_entry/11, write_target_addr_config/2, write_target_addr_config/3, append_target_addr_config/2, read_target_addr_config/1, @@ -447,7 +447,23 @@ target_addr_entry(Name, EngineId, TMask, MaxMessageSize) -> + target_addr_entry(Name, snmp_target_mib:default_domain(), Ip, Udp, + Timeout, RetryCount, TagList, ParamsName, + TMask, MaxMessageSize). + +target_addr_entry(Name, + Domain, + Ip, + Udp, + Timeout, + RetryCount, + TagList, + ParamsName, + EngineId, + TMask, + MaxMessageSize) -> {Name, + Domain, Ip, Udp, Timeout, @@ -465,9 +481,13 @@ write_target_addr_config(Dir, Conf) -> "%% The data is inserted into the snmpTargetAddrTable defined\n" "%% in SNMP-TARGET-MIB, and in the snmpTargetAddrExtTable defined\n" "%% in SNMP-COMMUNITY-MIB.\n" -"%% Each row is a 10-tuple:\n" -"%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId,\n" -"%% TMask, MaxMessageSize}.\n" +"%% Each row is a 10 or 11-tuple (Domain is optional):\n" +"%% {Name, \n" +"%% Domain, Ip, Port, \n" +"%% Timeout, RetryCount, TagList, ParamsName, EngineId,\n" +"%% TMask, MaxMessageSize}.\n" +"%% The value of Domain decide the format of the Ip and TMask values. \n" +"%% If not present, classic Ipv4 is assumed. \n" "%% The EngineId value is only used if Inform-Requests are sent to this\n" "%% target. If Informs are not sent, this value is ignored, and can be\n" "%% e.g. an empty string. However, if Informs are sent, it is essential\n" @@ -521,16 +541,31 @@ write_target_addr_conf(Fd, Hdr, Conf) -> write_target_addr_conf(Fd, Conf) -> Fun = fun(Entry) -> do_write_target_addr_conf(Fd, Entry) end, - lists:foreach(Fun, Conf). + lists:foreach(Fun, Conf), + ok. do_write_target_addr_conf(Fd, - {Name, Ip, Udp, + {Name, + Ip, Udp, + Timeout, RetryCount, TagList, + ParamsName, EngineId, + TMask, MaxMessageSize}) -> + Domain = snmp_target_mib:default_domain(), + do_write_target_addr_conf(Fd, + {Name, + Domain, Ip, Udp, + Timeout, RetryCount, TagList, + ParamsName, EngineId, + TMask, MaxMessageSize}); +do_write_target_addr_conf(Fd, + {Name, + Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}) -> io:format(Fd, - "{\"~s\", ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n", - [Name, Ip, Udp, Timeout, RetryCount, TagList, + "{\"~s\", ~w, ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n", + [Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize]); do_write_target_addr_conf(_Fd, Crap) -> error({bad_target_addr_config, Crap}). @@ -546,13 +581,13 @@ target_params_entry(Name, Vsn) -> target_params_entry(Name, Vsn, SecName, SecLevel). target_params_entry(Name, Vsn, SecName, SecLevel) -> - MPModel = if Vsn == v1 -> v1; - Vsn == v2 -> v2c; - Vsn == v3 -> v3 + MPModel = if Vsn =:= v1 -> v1; + Vsn =:= v2 -> v2c; + Vsn =:= v3 -> v3 end, - SecModel = if Vsn == v1 -> v1; - Vsn == v2 -> v2c; - Vsn == v3 -> usm + SecModel = if Vsn =:= v1 -> v1; + Vsn =:= v2 -> v2c; + Vsn =:= v3 -> usm end, target_params_entry(Name, MPModel, SecModel, SecName, SecLevel). diff --git a/lib/snmp/src/agent/snmpa_internal.hrl b/lib/snmp/src/agent/snmpa_internal.hrl index 9fa874f119..a91f30a4a6 100644 --- a/lib/snmp/src/agent/snmpa_internal.hrl +++ b/lib/snmp/src/agent/snmpa_internal.hrl @@ -23,6 +23,7 @@ -include_lib("snmp/src/app/snmp_internal.hrl"). -define(DEFAULT_LOCAL_ENGINE_ID, snmp_framework_mib:get_engine_id()). +-define(DEFAULT_NOTIF_EXTRA_INFO, {snmpa_default_notification_extra_info}). -define(snmpa_info(F, A), ?snmp_info("agent", F, A)). -define(snmpa_warning(F, A), ?snmp_warning("agent", F, A)). diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl index fd75b98f84..39a4246d26 100644 --- a/lib/snmp/src/agent/snmpa_mpd.erl +++ b/lib/snmp/src/agent/snmpa_mpd.erl @@ -115,8 +115,8 @@ reset() -> %% Func: process_packet(Packet, TDomain, TAddress, State, Log) -> %% {ok, SnmpVsn, Pdu, PduMS, ACMData} | {discarded, Reason} %% Types: Packet = binary() -%% TDomain = snmpUDPDomain | atom() -%% TAddress = {Ip, Udp} +%% TDomain = snmpUDPDomain | transportDomain() +%% TAddress = {Ip, Udp} (*but* depends on TDomain) %% State = #state %% Purpose: This is the main Message Dispatching function. (see %% section 4.2.1 in rfc2272) @@ -182,24 +182,30 @@ discarded_pdu(Variable) -> inc(Variable). %%----------------------------------------------------------------- %% Handles a Community based message (v1 or v2c). %%----------------------------------------------------------------- -v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain, +v1_v2c_proc(Vsn, NoteStore, Community, Domain, {Ip, Udp}, LocalEngineID, Data, HS, Log, Packet) -> - TAddress = tuple_to_list(Ip) ++ [Udp div 256, Udp rem 256], - AgentMS = get_engine_max_message_size(LocalEngineID), - MgrMS = snmp_community_mib:get_target_addr_ext_mms(?snmpUDPDomain, - TAddress), - PduMS = case MgrMS of - {ok, MMS} when MMS < AgentMS -> MMS - HS; - _ -> AgentMS - HS - end, + TDomain = snmp_conf:mk_tdomain(Domain), + TAddress = snmp_conf:mk_taddress(Domain, Ip, Udp), + AgentMS = get_engine_max_message_size(LocalEngineID), + MgrMS = snmp_community_mib:get_target_addr_ext_mms(TDomain, TAddress), + PduMS = case MgrMS of + {ok, MMS} when MMS < AgentMS -> MMS - HS; + _ -> AgentMS - HS + end, case (catch snmp_pdus:dec_pdu(Data)) of Pdu when is_record(Pdu, pdu) -> Log(Pdu#pdu.type, Packet), inc_snmp_in_vars(Pdu), #pdu{request_id = ReqId} = Pdu, - OkRes = {ok, Vsn, Pdu, PduMS, - {community, sec_model(Vsn), Community, TAddress}}, + + %% <TDomain> + %% We have added TDomain, what are the consequences? + ACMData = + {community, sec_model(Vsn), Community, TDomain, TAddress}, + OkRes = {ok, Vsn, Pdu, PduMS, ACMData}, + %% </TDomain> + %% Make sure that we don't process duplicate SET request %% twice. We don't know what could happen in that case. %% The mgr does, so he has to generate a new SET request. @@ -216,8 +222,6 @@ v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain, snmp_note_store:set_note(NoteStore, 100, Key, true), %% Uses ACMData that snmpa_acm knows of. - %% snmpUDPDomain is implicit, since that's the only - %% one we handle. OkRes; true -> {discarded, duplicate_pdu} @@ -275,12 +279,12 @@ v3_proc(NoteStore, Packet, LocalEngineID, V3Hdr, Data, Log) -> "~n msgSecurityParameters = ~w", [MsgID, MMS, MsgFlags, MsgSecurityModel, SecParams]), %% 7.2.4 - SecModule = get_security_module(MsgSecurityModel), + SecModule = get_security_module(MsgSecurityModel), %% 7.2.5 - SecLevel = check_sec_level(MsgFlags), + SecLevel = check_sec_level(MsgFlags), IsReportable = snmp_misc:is_reportable(MsgFlags), %% 7.2.6 - ?vtrace("v3_proc -> [7.2.6]" + ?vtrace("v3_proc -> [7.2.4-7.2.6]" "~n SecModule = ~p" "~n SecLevel = ~p" "~n IsReportable = ~p", @@ -531,7 +535,7 @@ check_sec_module_result(Res, V3Hdr, Data, LocalEngineID, IsReportable, Log) -> ?vdebug("security module result [7.2.6-b]:" "~n Reason: ~p", [Reason]), throw({discarded, {securityError, Reason}}); - {error, Reason, ErrorInfo} when IsReportable == true -> % case 7.2.6 a + {error, Reason, ErrorInfo} when IsReportable =:= true -> % case 7.2.6 a ?vdebug("security module result when reportable [7.2.6-a]:" "~n Reason: ~p" "~n ErrorInfo: ~p", [Reason, ErrorInfo]), @@ -574,7 +578,7 @@ generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) -> generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log, 1). generate_response_msg(Vsn, RePdu, Type, - {community, _SecModel, Community, _IpUdp}, + {community, _SecModel, Community, _TDomain, _TAddress}, LocalEngineID, Log, _) -> case catch snmp_pdus:enc_pdu(RePdu) of diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl index 97a7a63dee..bbc5568cde 100644 --- a/lib/snmp/src/agent/snmpa_net_if.erl +++ b/lib/snmp/src/agent/snmpa_net_if.erl @@ -314,6 +314,14 @@ loop(S) -> NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, undefined), loop(NewS); + %% We dont use the extra-info at this time, ... + {send_pdu, Vsn, Pdu, MsgData, To, _ExtraInfo} -> + ?vdebug("send pdu: " + "~n Pdu: ~p" + "~n To: ~p", [Pdu, To]), + NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, undefined), + loop(NewS); + %% Informs {send_pdu_req, Vsn, Pdu, MsgData, To, From} -> ?vdebug("send pdu request: " @@ -324,7 +332,18 @@ loop(S) -> NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, From), loop(NewS); + %% We dont use the extra-info at this time, ... + {send_pdu_req, Vsn, Pdu, MsgData, To, From, _ExtraInfo} -> + ?vdebug("send pdu request: " + "~n Pdu: ~p" + "~n To: ~p" + "~n From: ~p", + [Pdu, To, toname(From)]), + NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, From), + loop(NewS); + %% Discovery Inform + %% <BACKWARD-COMPAT> {send_discovery, Pdu, MsgData, To, From} -> ?vdebug("received send discovery request: " "~n Pdu: ~p" @@ -333,6 +352,18 @@ loop(S) -> [Pdu, To, toname(From)]), NewS = handle_send_discovery(S, Pdu, MsgData, To, From), loop(NewS); + %% </BACKWARD-COMPAT> + + %% Discovery Inform + {send_discovery, Pdu, MsgData, To, From, ExtraInfo} -> + ?vdebug("received send discovery request: " + "~n Pdu: ~p" + "~n To: ~p" + "~n From: ~p" + "~n ExtraInfo: ~p", + [Pdu, To, toname(From), ExtraInfo]), + NewS = handle_send_discovery(S, Pdu, MsgData, To, From), + loop(NewS); {discarded_pdu, _Vsn, ReqId, _ACMData, Variable, _Extra} -> ?vdebug("discard PDU: ~p", [Variable]), @@ -504,7 +535,6 @@ handle_discovery_response(_Ip, _Port, #pdu{request_id = ReqId} = Pdu, S end. - handle_recv(#state{usock = Sock, mpd_state = MpdState, note_store = NS, @@ -513,7 +543,9 @@ handle_recv(#state{usock = Sock, LogF = fun(Type, Data) -> log(Log, Type, Data, Ip, Port) end, - case (catch snmpa_mpd:process_packet(Packet, snmpUDPDomain, {Ip, Port}, + Domain = snmp_conf:which_domain(Ip), % What the ****... + case (catch snmpa_mpd:process_packet(Packet, + Domain, {Ip, Port}, MpdState, NS, LogF)) of {ok, _Vsn, Pdu, _PduMS, {discovery, ManagerEngineId}} -> handle_discovery_response(Ip, Port, Pdu, ManagerEngineId, S); @@ -636,7 +668,6 @@ process_taddrs([{{_Domain, AddrAndPort}, _SecData}|T], Acc) -> process_taddrs([{_Domain, AddrAndPort}|T], Acc) -> process_taddrs(T, [AddrAndPort|Acc]). - merge_taddrs(To1, To2) -> merge_taddrs(To1, To2, []). @@ -776,15 +807,49 @@ handle_send_pdu1(#state{log = Log, usock = Sock, filter = FilterMod}, Type, Addresses) -> SendFun = - fun({snmpUDPDomain, {Ip, Port}, Packet}) when is_binary(Packet) -> - ?vdebug("sending packet:" + fun({snmpUDPDomain, {Ip, Port}, Packet}) + when is_binary(Packet) -> + ?vdebug("[snmpUDPDomain] sending packet:" + "~n size: ~p" + "~n to: ~p:~p", + [sz(Packet), Ip, Port]), + maybe_udp_send(FilterMod, Log, Type, Sock, Ip, Port, Packet); + + ({snmpUDPDomain, {Ip, Port}, {Packet, _LogData}}) + when is_binary(Packet) -> + ?vdebug("[snmpUDPDomain] sending encrypted packet:" + "~n size: ~p" + "~n to: ~p:~p", + [sz(Packet), Ip, Port]), + maybe_udp_send(FilterMod, Log, Type, Sock, Ip, Port, Packet); + + ({transportDomainUdpIpv4, {Ip, Port}, Packet}) + when is_binary(Packet) -> + ?vdebug("[transportDomainUdpIpv4] sending packet:" + "~n size: ~p" + "~n to: ~p:~p", + [sz(Packet), Ip, Port]), + maybe_udp_send(FilterMod, Log, Type, Sock, Ip, Port, Packet); + + ({transportDomainUdpIpv4, {Ip, Port}, {Packet, _LogData}}) + when is_binary(Packet) -> + ?vdebug("[transportDomainUdpIpv4] sending encrypted packet:" + "~n size: ~p" + "~n to: ~p:~p", + [sz(Packet), Ip, Port]), + maybe_udp_send(FilterMod, Log, Type, Sock, Ip, Port, Packet); + + ({transportDomainUdpIpv6, {Ip, Port}, Packet}) + when is_binary(Packet) -> + ?vdebug("[transportDomainUdpIpv6] sending packet:" "~n size: ~p" "~n to: ~p:~p", [sz(Packet), Ip, Port]), maybe_udp_send(FilterMod, Log, Type, Sock, Ip, Port, Packet); - ({snmpUDPDomain, {Ip, Port}, {Packet, _LogData}}) when is_binary(Packet) -> - ?vdebug("sending encrypted packet:" + ({transportDomainUdpIpv6, {Ip, Port}, {Packet, _LogData}}) + when is_binary(Packet) -> + ?vdebug("[transportDomainUdpIpv6] sending encrypted packet:" "~n size: ~p" "~n to: ~p:~p", [sz(Packet), Ip, Port]), diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 450cb2e9f4..3c7ae804a5 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -24,27 +24,34 @@ %% External exports -export([construct_trap/2, try_initialise_vars/2, - send_trap/6, send_trap/7]). --export([send_discovery/5]). + send_trap/6, send_trap/7, send_trap/8]). +-export([send_discovery/6]). %% Internal exports --export([init_v2_inform/9, - init_v3_inform/9, init_v3_inform/10, +-export([init_v2_inform/9, init_v2_inform/10, + init_v3_inform/9, init_v3_inform/10, init_v3_inform/11, send_inform/6]). --export([init_discovery_inform/12, send_discovery_inform/5]). - --include("snmp_types.hrl"). --include("snmpa_internal.hrl"). --include("SNMPv2-MIB.hrl"). --include("SNMPv2-TM.hrl"). --include("SNMPv2-TC.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). --include("SNMP-TARGET-MIB.hrl"). +-export([init_discovery_inform/13, send_discovery_inform/5]). + +%% <BACKWARD-COMPAT> +-export([send_discovery/5, + init_discovery_inform/12]). +%% </BACKWARD-COMPAT> + +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/src/agent/snmpa_internal.hrl"). +-include_lib("snmp/include/SNMPv2-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). +-include_lib("snmp/include/SNMPv2-TC.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/SNMP-TARGET-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). -define(enterpriseSpecific, 6). -define(VMODULE,"TRAP"). -include("snmp_verbosity.hrl"). +-include("snmpa_internal.hrl"). %%----------------------------------------------------------------- @@ -335,25 +342,36 @@ make_varbind_list(Varbinds) -> %% SnmpTargetAddrTable (using the Tag). %%----------------------------------------------------------------- send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, + send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, NetIf). + +send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, ExtraInfo, NetIf) -> LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf). + LocalEngineID, ExtraInfo, NetIf). -send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, NetIf) -> +send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, + ExtraInfo, NetIf) -> (catch do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf)). + LocalEngineID, ExtraInfo, NetIf)). do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf) -> + LocalEngineID, ExtraInfo, NetIf) -> VarbindList = make_varbind_list(Vbs), Dests = find_dests(NotifyName), send_trap_pdus(Dests, ContextName, {TrapRec, VarbindList}, [], [], [], - Recv, LocalEngineID, NetIf). + Recv, LocalEngineID, ExtraInfo, NetIf). send_discovery(TargetName, Record, ContextName, Vbs, NetIf) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo). +send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo) -> case find_dest(TargetName) of {ok, Dest} -> - send_discovery_pdu(Dest, Record, ContextName, Vbs, NetIf); + send_discovery_pdu(Dest, Record, ContextName, Vbs, NetIf, + ExtraInfo); Error -> Error end. @@ -440,11 +458,13 @@ split_variables([]) -> {[], []}. %% NOTE: This function is executed in the master agent's context %%----------------------------------------------------------------- find_dests("") -> + ?vtrace("find destinations", []), snmp_notification_mib:get_targets(); find_dests(NotifyName) -> + ?vtrace("find destinations for ~p", [NotifyName]), case snmp_notification_mib:get_targets(NotifyName) of [] -> - ?vlog("No dests found for snmpNotifyName: ~p",[NotifyName]), + ?vlog("No dests found for NotifyName: ~p", [NotifyName]), []; Dests -> Dests @@ -529,7 +549,8 @@ find_dest(TargetName) -> send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, Timeout, Retry}, - Record, ContextName, Vbs, NetIf) -> + Record, ContextName, Vbs, NetIf, + ExtraInfo) -> ?vdebug("send_discovery_pdu -> entry with " "~n Destination address: ~p" "~n Target name: ~p" @@ -539,9 +560,10 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, "~n Timeout: ~p" "~n Retry: ~p" "~n Record: ~p" - "~n ContextName: ~p", + "~n ContextName: ~p" + "~n ExtraInfo: ~p", [Dest, TargetName, SecModel, SecName, SecLevel, - Timeout, Retry, Record, ContextName]), + Timeout, Retry, Record, ContextName, ExtraInfo]), case get_mib_view(SecModel, SecName, SecLevel, ContextName) of {ok, MibView} -> case check_all_varbinds(Record, Vbs, MibView) of @@ -551,7 +573,7 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, SecModel, SecName, SecLevel, TargetName, ContextName, Timeout, Retry, - SysUpTime, NetIf); + SysUpTime, NetIf, ExtraInfo); false -> {error, {mibview_validation_failed, Vbs, MibView}} end; @@ -561,7 +583,7 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, send_discovery_pdu(Record, Dest, Vbs, SecModel, SecName, SecLevel, TargetName, - ContextName, Timeout, Retry, SysUpTime, NetIf) -> + ContextName, Timeout, Retry, SysUpTime, NetIf, ExtraInfo) -> {_Oid, IVbs} = mk_v2_trap(Record, Vbs, SysUpTime), % v2 refers to SMIv2; Sender = proc_lib:spawn_link(?MODULE, init_discovery_inform, [self(), @@ -570,13 +592,25 @@ send_discovery_pdu(Record, Dest, Vbs, ContextName, Timeout, Retry, IVbs, NetIf, - get(verbosity)]), + get(verbosity), + ExtraInfo]), {ok, Sender, SecLevel}. init_discovery_inform(Parent, Dest, SecModel, SecName, SecLevel, TargetName, ContextName, Timeout, Retry, Vbs, NetIf, Verbosity) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_discovery_inform(Parent, + Dest, + SecModel, SecName, SecLevel, TargetName, + ContextName, Timeout, Retry, Vbs, NetIf, + Verbosity, ExtraInfo). +init_discovery_inform(Parent, + Dest, + SecModel, SecName, SecLevel, TargetName, + ContextName, Timeout, Retry, Vbs, NetIf, + Verbosity, ExtraInfo) -> put(verbosity, Verbosity), put(sname, madis), Pdu = make_discovery_pdu(Vbs), @@ -584,7 +618,7 @@ init_discovery_inform(Parent, SecLevelFlag = mk_flag(SecLevel), SecData = {SecModel, SecName, SecLevelFlag, TargetName}, MsgData = {SecData, ContextEngineId, ContextName}, - Msg = {send_discovery, Pdu, MsgData, Dest, self()}, + Msg = {send_discovery, Pdu, MsgData, Dest, self(), ExtraInfo}, ?MODULE:send_discovery_inform(Parent, Timeout*10, Retry, Msg, NetIf). %% note_timeout(Timeout, Retry) @@ -628,11 +662,11 @@ send_discovery_inform(Parent, Timeout, Retry, Msg, NetIf) -> %% should be used for the target, and determine the message %% specific parameters to be used. %%----------------------------------------------------------------- -send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, - Type} | T], +send_trap_pdus([{DestAddr, TargetName, + {MpModel, SecModel, SecName, SecLevel}, Type} | T], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) -> + LocalEngineID, ExtraInfo, NetIf) -> ?vdebug("send trap pdus: " "~n Destination address: ~p" "~n Target name: ~p" @@ -658,13 +692,13 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, [{DestAddr, Community} | V1Res], V2Res, V3Res, Recv, - LocalEngineID, NetIf); + LocalEngineID, ExtraInfo, NetIf); undefined -> ?vdebug("No community found for v1 dest: ~p", [element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; true when MpModel =:= ?MP_V2C -> ?vtrace("send_trap_pdus -> v2c mp model",[]), @@ -679,13 +713,14 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, [{DestAddr, Community, Type}|V2Res], - V3Res, Recv, LocalEngineID, NetIf); + V3Res, Recv, + LocalEngineID, ExtraInfo, NetIf); undefined -> ?vdebug("No community found for v2c dest: ~p", [element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; true when MpModel =:= ?MP_V3 -> ?vtrace("send_trap_pdus -> v3 mp model",[]), @@ -694,20 +729,20 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, [{DestAddr, MsgData, Type} | V3Res], - Recv, LocalEngineID, NetIf); + Recv, LocalEngineID, ExtraInfo, NetIf); true -> ?vlog("bad MpModel ~p for dest ~p", [MpModel, element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf); + LocalEngineID, ExtraInfo, NetIf); _ -> ?vlog("no access for dest: " "~n ~p in target ~p", [element(2, DestAddr), TargetName]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; {discarded, Reason} -> ?vlog("mib view error ~p for" @@ -715,24 +750,27 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, "~n SecName: ~w", [Reason, element(2, DestAddr), SecName]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, - V1Res, V2Res, V3Res, Recv, LocalEngineID, NetIf) + V1Res, V2Res, V3Res, Recv, + LocalEngineID, ExtraInfo, NetIf) end; -send_trap_pdus([], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, - Recv, LocalEngineID, NetIf) -> +send_trap_pdus([], ContextName, {TrapRec, Vbs}, + V1Res, V2Res, V3Res, Recv, + LocalEngineID, ExtraInfo, + NetIf) -> SysUpTime = snmp_standard_mib:sys_up_time(), ?vdebug("send trap pdus with sysUpTime ~p", [SysUpTime]), InformRecvs = get_inform_recvs(V2Res ++ V3Res), InformTargets = [Addr || {Addr, _, _, _} <- InformRecvs], deliver_recv(Recv, snmp_targets, InformTargets), - send_v1_trap(TrapRec, V1Res, Vbs, NetIf, SysUpTime), - send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime), - send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, NetIf, + send_v1_trap(TrapRec, V1Res, Vbs, ExtraInfo, NetIf, SysUpTime), + send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime), + send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, SysUpTime, ContextName). -send_v1_trap(_TrapRec, [], _Vbs, _NetIf, _SysUpTime) -> +send_v1_trap(_TrapRec, [], _Vbs, _ExtraInfo, _NetIf, _SysUpTime) -> ok; send_v1_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, - V1Res, Vbs, NetIf, SysUpTime) -> + V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> ?vdebug("prepare to send v1 trap " "~n '~p'" "~n with" @@ -744,9 +782,10 @@ send_v1_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, lists:foreach(fun({Community, Addrs}) -> ?vtrace("send v1 trap pdu to ~p",[Addrs]), NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities); -send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, NetIf, SysUpTime) -> +send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, ExtraInfo, NetIf, + SysUpTime) -> %% Use alg. in rfc2089 to map a v2 trap to a v1 trap % delete Counter64 objects from vbs ?vdebug("prepare to send v1 trap '~p'",[Oid]), @@ -768,31 +807,31 @@ send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, NetIf, SysUpTime) -> lists:foreach(fun({Community, Addrs}) -> ?vtrace("send v1 trap to ~p",[Addrs]), NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities). -send_v2_trap(_TrapRec, [], _Vbs, _Recv, _NetIf, _SysUpTime) -> +send_v2_trap(_TrapRec, [], _Vbs, _Recv, _ExtraInfo, _NetIf, _SysUpTime) -> ok; -send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime) -> +send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime) -> ?vdebug("prepare to send v2 trap",[]), {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), - TrapRecvs = get_trap_recvs(V2Res), - InformRecvs = get_inform_recvs(V2Res), - do_send_v2_trap(TrapRecvs, IVbs, NetIf), - do_send_v2_inform(InformRecvs, IVbs, Recv, NetIf). + TrapRecvs = get_trap_recvs(V2Res), + InformRecvs = get_inform_recvs(V2Res), + do_send_v2_trap(TrapRecvs, IVbs, ExtraInfo, NetIf), + do_send_v2_inform(InformRecvs, IVbs, Recv, ExtraInfo, NetIf). -send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID, +send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID, _ExtraInfo, _NetIf, _SysUpTime, _ContextName) -> ok; -send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, +send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, SysUpTime, ContextName) -> ?vdebug("prepare to send v3 trap",[]), {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), % v2 refers to SMIv2; TrapRecvs = get_trap_recvs(V3Res), % same SMI for v3 InformRecvs = get_inform_recvs(V3Res), - do_send_v3_trap(TrapRecvs, ContextName, IVbs, NetIf), + do_send_v3_trap(TrapRecvs, ContextName, IVbs, ExtraInfo, NetIf), do_send_v3_inform(InformRecvs, ContextName, IVbs, Recv, - LocalEngineID, NetIf). + LocalEngineID, ExtraInfo, NetIf). mk_v2_trap(#notification{oid = Oid}, Vbs, SysUpTime) -> @@ -830,60 +869,70 @@ get_inform_recvs(InformRecvs) -> [{Addr, MsgData, Timeout, Retry} || {Addr, MsgData, {inform, Timeout, Retry}} <- InformRecvs]. -do_send_v2_trap([], _Vbs, _NetIf) -> +do_send_v2_trap([], _Vbs, _ExtraInfo, _NetIf) -> ok; -do_send_v2_trap(Recvs, Vbs, NetIf) -> +do_send_v2_trap(Recvs, Vbs, ExtraInfo, NetIf) -> TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), AddrCommunities = mk_addr_communities(Recvs), lists:foreach(fun({Community, Addrs}) -> ?vtrace("~n send v2 trap to ~p",[Addrs]), NetIf ! {send_pdu, 'version-2', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities), ok. -do_send_v2_inform([], _Vbs, _Recv, _NetIf) -> +do_send_v2_inform([], _Vbs, _Recv, _ExtraInfo, _NetIf) -> ok; -do_send_v2_inform(Recvs, Vbs, Recv, NetIf) -> +do_send_v2_inform(Recvs, Vbs, Recv, ExtraInfo, NetIf) -> lists:foreach( fun({Addr, Community, Timeout, Retry}) -> ?vtrace("~n start inform sender to send v2 inform to ~p", [Addr]), proc_lib:spawn_link(?MODULE, init_v2_inform, [Addr, Timeout, Retry, Vbs, - Recv, NetIf, Community, + Recv, ExtraInfo, NetIf, Community, get(verbosity), get(sname)]) end, Recvs). -do_send_v3_trap([], _ContextName, _Vbs, _NetIf) -> +do_send_v3_trap([], _ContextName, _Vbs, _ExtraInfo, _NetIf) -> ok; -do_send_v3_trap(Recvs, ContextName, Vbs, NetIf) -> +do_send_v3_trap(Recvs, ContextName, Vbs, ExtraInfo, NetIf) -> TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), % Yes, v2 ContextEngineId = snmp_framework_mib:get_engine_id(), lists:foreach(fun(Recv) -> ?vtrace("~n send v3 notif to ~p",[Recv]), NetIf ! {send_pdu, 'version-3', TrapPdu, - {v3, ContextEngineId, ContextName}, [Recv]} + {v3, ContextEngineId, ContextName}, + [Recv], ExtraInfo} end, Recvs), ok. -do_send_v3_inform([], _ContextName, _Vbs, _Recv, _LocalEngineID, _NetIf) -> +do_send_v3_inform([], _ContextName, _Vbs, _Recv, + _LocalEngineID, _ExtraInfo, _NetIf) -> ok; -do_send_v3_inform(Recvs, ContextName, Vbs, Recv, LocalEngineID, NetIf) -> +do_send_v3_inform(Recvs, ContextName, Vbs, Recv, + LocalEngineID, ExtraInfo, NetIf) -> lists:foreach( fun({Addr, MsgData, Timeout, Retry}) -> ?vtrace("~n start inform sender to send v3 inform to ~p", [Addr]), proc_lib:spawn_link(?MODULE, init_v3_inform, [{Addr, MsgData}, Timeout, Retry, Vbs, - Recv, LocalEngineID, NetIf, ContextName, + Recv, LocalEngineID, ExtraInfo, + NetIf, ContextName, get(verbosity), get(sname)]) end, Recvs). %% New process -init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community,V,S) -> +init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, + Community, V, S). + +init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, + Community, V, S) -> %% Make a new Inform for each recipient; they need unique %% request-ids! put(verbosity,V), @@ -892,17 +941,26 @@ init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community,V,S) -> [Timeout,Retry]), InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), Msg = {send_pdu_req, 'version-2', InformPdu, {community, Community}, - [Addr], self()}, + [Addr], self(), ExtraInfo}, ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). %% New process init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, - init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, + init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, + LocalEngineID, ExtraInfo, + NetIf, ContextName, V, S). + +init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, NetIf, + ContextName, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, + LocalEngineID, ExtraInfo, NetIf, ContextName, V, S). -init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, +init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, ContextName, V, S) -> %% Make a new Inform for each recipient; they need unique %% request-ids! @@ -913,7 +971,7 @@ init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), % Yes, v2 ContextEngineId = LocalEngineID, Msg = {send_pdu_req, 'version-3', InformPdu, - {v3, ContextEngineId, ContextName}, [Addr], self()}, + {v3, ContextEngineId, ContextName}, [Addr], self(), ExtraInfo}, ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). send_inform(Addr, _Timeout, -1, _Msg, Recv, _NetIf) -> @@ -1001,9 +1059,27 @@ transform_taddr({?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}) -> % v2 Addr = {A1, A2, A3, A4}, Port = P1 bsl 8 + P2, {Addr, Port}; +transform_taddr({?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}) -> % v2 + Addr = {A1, A2, A3, A4}, + Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({?transportDomainUdpIpv6, + [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}) -> % v2 + Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, + Port = P1 bsl 8 + P2, + {Addr, Port}; transform_taddr({{?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 Addr = {A1, A2, A3, A4}, Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({{?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 + Addr = {A1, A2, A3, A4}, + Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({{?transportDomainUdpIpv6, + [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}, _MsgData}) -> % v3 + Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, + Port = P1 bsl 8 + P2, {Addr, Port}. @@ -1053,13 +1129,14 @@ mk_addr_communities(Recvs) -> [{Addr, Comm} | T] = lists:keysort(2, Recvs), mic(T, Comm, [Addr], []). -mic([{Addr, Comm} | T], CurComm, AddrList, Res) when Comm == CurComm -> +mic([{Addr, Comm} | T], CurComm, AddrList, Res) when Comm =:= CurComm -> mic(T, CurComm, [Addr | AddrList], Res); mic([{Addr, Comm} | T], CurComm, AddrList, Res) -> mic(T, Comm, [Addr], [{CurComm, AddrList} | Res]); mic([], CurComm, AddrList, Res) -> [{CurComm, AddrList} | Res]. + %%----------------------------------------------------------------- %% Convert the SecurityLevel into a flag value used by snmpa_mpd %%----------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl index f35d1f1916..6f54307f9f 100644 --- a/lib/snmp/src/agent/snmpa_usm.erl +++ b/lib/snmp/src/agent/snmpa_usm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -330,7 +330,6 @@ non_authoritative(SecName, end end. - is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _, _) -> % 3.2.5 error(usmStatsUnsupportedSecLevels, ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 @@ -613,8 +612,7 @@ authenticate_outgoing(Message, UsmSecParams, end, ?vtrace("authenticate_outgoing -> encode message only",[]), snmp_pdus:enc_message_only(Message2). - - + %%----------------------------------------------------------------- %% Auth and priv algorithms @@ -753,14 +751,19 @@ set_engine_latest_time(SnmpEngineID, EngineTime) -> %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- +-spec error(term()) -> no_return(). error(Reason) -> throw({error, Reason}). +-spec error(term(), term()) -> no_return(). error(Reason, ErrorInfo) -> throw({error, Reason, ErrorInfo}). +-spec error(term(), term(), term()) -> no_return(). error(Variable, Oid, SecName) -> error(Variable, Oid, SecName, []). + +-spec error(term(), term(), term(), [term()]) -> no_return(). error(Variable, Oid, SecName, Opts) -> Val = inc(Variable), ErrorInfo = {#varbind{oid = Oid, @@ -772,7 +775,6 @@ error(Variable, Oid, SecName, Opts) -> inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). - get_counter(Name) -> case (catch ets:lookup(snmp_agent_table, Name)) of [{_, Val}] -> @@ -780,8 +782,3 @@ get_counter(Name) -> _ -> 0 end. - - - - - diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index de0e5d6e14..5deb40be0f 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,10 +22,56 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.19", + [ + {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpa_usm, soft_purge, soft_purge, []}, + {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmpa_trap, soft_purge, soft_purge, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, + {load_module, snmpa_acm, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd, snmp_target_mib]}, + {load_module, snmpa_conf, soft_purge, soft_purge, + [snmp_notification_mib]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, + [snmp_conf, snmp_target_mib]}, + {load_module, snmp_community_mib, soft_purge, soft_purge, []}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + {update, snmpa_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, + [snmpa_acm, snmpa_mpd, snmpa_trap]} + ] + }, {"4.18", [ + {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpa_usm, soft_purge, soft_purge, []}, + {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmpa_conf, soft_purge, soft_purge, []}, + {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, + {load_module, snmpa_trap, soft_purge, soft_purge, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, + {load_module, snmpa_acm, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa, soft_purge, soft_purge, [snmp_community_mib, snmp_framework_mib, @@ -33,6 +79,8 @@ snmp_target_mib, snmp_user_based_sm_mib, snmp_view_based_acm_mib]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, + [snmp_conf, snmp_target_mib, snmpa_mib_lib]}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, {load_module, snmp_framework_mib, soft_purge, soft_purge, @@ -45,7 +93,15 @@ [snmpa_mib_lib]}, {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmpa_mib_lib, snmpa_vacm]}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []} + {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, + + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + + {update, snmpa_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, + [snmpa_acm, snmpa_mpd, snmpa_trap]} ] } ], @@ -53,10 +109,58 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.19", + [ + {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpa_usm, soft_purge, soft_purge, []}, + {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmpa_trap, soft_purge, soft_purge, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, + {load_module, snmpa_acm, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd, snmp_target_mib]}, + {load_module, snmpa_conf, soft_purge, soft_purge, + [snmp_notification_mib]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, + [snmp_conf, snmp_target_mib]}, + {load_module, snmp_community_mib, soft_purge, soft_purge, []}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf]}, + + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + + {update, snmpa_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, + [snmpa_acm, snmpa_mpd, snmpa_trap]} + ] + }, {"4.18", [ + {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpa_usm, soft_purge, soft_purge, []}, + {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, + {load_module, snmpa_trap, soft_purge, soft_purge, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, + {load_module, snmpa_acm, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa, soft_purge, soft_purge, [snmp_community_mib, snmp_framework_mib, @@ -64,6 +168,8 @@ snmp_target_mib, snmp_user_based_sm_mib, snmp_view_based_acm_mib]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, + [snmp_conf, snmp_target_mib, snmpa_mib_lib]}, {load_module, snmp_community_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, {load_module, snmp_framework_mib, soft_purge, soft_purge, @@ -76,7 +182,15 @@ [snmpa_mib_lib]}, {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmpa_mib_lib, snmpa_vacm]}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []} + {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, + + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + + {update, snmpa_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpa_mpd]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, + [snmpa_acm, snmpa_mpd, snmpa_trap]} ] } ] diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 5b6321b4c3..e457d3b47a 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -57,15 +57,16 @@ usm_user_info/3, update_usm_user_info/4, %% - %% Basic SNMP API - sync_get/3, sync_get/4, sync_get/5, sync_get/6, - async_get/3, async_get/4, async_get/5, async_get/6, - sync_get_next/3, sync_get_next/4, sync_get_next/5, sync_get_next/6, - async_get_next/3, async_get_next/4, async_get_next/5, async_get_next/6, - sync_set/3, sync_set/4, sync_set/5, sync_set/6, - async_set/3, async_set/4, async_set/5, async_set/6, - sync_get_bulk/5, sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, - async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, + %% Basic SNMP API (version "3"). + sync_get2/3, sync_get2/4, + async_get2/3, async_get2/4, + sync_get_next2/3, sync_get_next2/4, + async_get_next2/3, async_get_next2/4, + sync_set2/3, sync_set2/4, + async_set2/3, async_set2/4, + sync_get_bulk2/5, sync_get_bulk2/6, + async_get_bulk2/5, async_get_bulk2/6, + cancel_async_request/2, %% @@ -91,7 +92,61 @@ -export([format_reason/1, format_reason/2]). -%% Backward compatibillity exports +%% Backward compatibility exports (API version "2") +-export([ + sync_get/3, sync_get/4, sync_get/5, sync_get/6, + async_get/3, async_get/4, async_get/5, async_get/6, + sync_get_next/3, sync_get_next/4, sync_get_next/5, sync_get_next/6, + async_get_next/3, async_get_next/4, async_get_next/5, async_get_next/6, + sync_set/3, sync_set/4, sync_set/5, sync_set/6, + async_set/3, async_set/4, async_set/5, async_set/6, + sync_get_bulk/5, sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, + async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8 + ]). + +%% Backward compatibility exports (API version "1") +-deprecated({agent_info, 3}). +-deprecated({update_agent_info, 5}). +-deprecated({g, 3}). +-deprecated({g, 4}). +-deprecated({g, 5}). +-deprecated({g, 6}). +-deprecated({g, 7}). +-deprecated({ag, 3}). +-deprecated({ag, 4}). +-deprecated({ag, 5}). +-deprecated({ag, 6}). +-deprecated({ag, 7}). +-deprecated({gn, 3}). +-deprecated({gn, 4}). +-deprecated({gn, 5}). +-deprecated({gn, 6}). +-deprecated({gn, 7}). +-deprecated({agn, 3}). +-deprecated({agn, 4}). +-deprecated({agn, 5}). +-deprecated({agn, 6}). +-deprecated({agn, 7}). +-deprecated({gb, 5}). +-deprecated({gb, 6}). +-deprecated({gb, 7}). +-deprecated({gb, 8}). +-deprecated({gb, 9}). +-deprecated({agb, 5}). +-deprecated({agb, 6}). +-deprecated({agb, 7}). +-deprecated({agb, 8}). +-deprecated({agb, 9}). +-deprecated({s, 3}). +-deprecated({s, 4}). +-deprecated({s, 5}). +-deprecated({s, 6}). +-deprecated({s, 7}). +-deprecated({as, 3}). +-deprecated({as, 4}). +-deprecated({as, 5}). +-deprecated({as, 6}). +-deprecated({as, 7}). -export([ agent_info/3, update_agent_info/5, g/3, g/4, g/5, g/6, g/7, @@ -108,12 +163,12 @@ -export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]). --include("snmp_debug.hrl"). +-include_lib("snmp/src/misc/snmp_debug.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). -include("snmpm_atl.hrl"). --include("snmp_types.hrl"). +-include("snmpm_internal.hrl"). -define(DEFAULT_AGENT_PORT, 161). --define(DEFAULT_CONTEXT, ""). %% This function is called when the snmp application @@ -332,23 +387,23 @@ register_agent(UserId, TargetName, Config) is_list(Config)) -> do_register_agent(UserId, TargetName, [{reg_type, target_name} | Config]); -%% Backward compatibillity +%% Backward compatibility %% Note that the agent engine id is a mandatory config option, %% so this function *will* fail! register_agent(UserId, Addr, Port) when is_integer(Port) -> register_agent(UserId, Addr, Port, []); -%% Backward compatibillity +%% Backward compatibility register_agent(UserId, Addr, Config) when is_list(Config) -> register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, Config). -%% Backward compatibillity +%% Backward compatibility %% Note that the agent engine id is a mandatory config option, %% so this function *will* fail! register_agent(UserId, Addr) -> register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, []). -%% Backward compatibillity +%% Backward compatibility register_agent(UserId, Addr, Port, Config0) -> case lists:keymember(target_name, 1, Config0) of false -> @@ -368,7 +423,7 @@ register_agent(UserId, Addr, Port, Config0) -> unregister_agent(UserId, TargetName) when is_list(TargetName) -> snmpm_config:unregister_agent(UserId, TargetName); -%% Backward compatibillity functions +%% Backward compatibility functions unregister_agent(UserId, Addr) -> unregister_agent(UserId, Addr, ?DEFAULT_AGENT_PORT). @@ -383,7 +438,7 @@ unregister_agent(UserId, Addr, Port) -> agent_info(TargetName, Item) -> snmpm_config:agent_info(TargetName, Item). -%% Backward compatibillity +%% Backward compatibility agent_info(Addr, Port, Item) -> case target_name(Addr, Port) of {ok, TargetName} -> @@ -393,24 +448,12 @@ agent_info(Addr, Port, Item) -> end. update_agent_info(UserId, TargetName, Item, Val) -> -%% p("update_agent_info -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Item: ~p" -%% "~n Val: ~p", [UserId, TargetName, Item, Val]), snmpm_config:update_agent_info(UserId, TargetName, Item, Val). -%% Backward compatibillity functions +%% Backward compatibility functions update_agent_info(UserId, Addr, Port, Item, Val) -> -%% p("update_agent_info -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Item: ~p" -%% "~n Val: ~p", [UserId, Addr, Port, Item, Val]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("update_agent_info -> TargetName: ~p", [TargetName]), update_agent_info(UserId, TargetName, Item, Val); Error -> Error @@ -472,94 +515,54 @@ which_usm_users(EngineID) when is_list(EngineID) -> %% --- synchroneous get-request --- %% +sync_get2(UserId, TargetName, Oids) -> + sync_get2(UserId, TargetName, Oids, []). + +sync_get2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:sync_get2(UserId, TargetName, Oids, SendOpts). + +%% <BACKWARD-COMPAT> sync_get(UserId, TargetName, Oids) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Oids: ~p", [UserId, TargetName, Oids]), - sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). - -sync_get(UserId, TargetName, Context, Oids) when is_list(Oids) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p", [UserId, TargetName, Context, Oids]), - snmpm_server:sync_get(UserId, TargetName, Context, Oids); - -sync_get(UserId, TargetName, Oids, Timeout) when is_integer(Timeout) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, TargetName, Oids, Timeout]), - sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). + sync_get2(UserId, TargetName, Oids). + +sync_get(UserId, TargetName, Oids, Timeout) + when is_list(Oids) andalso is_integer(Timeout) -> + SendOpts = [{timeout, Timeout}], + sync_get2(UserId, TargetName, Oids, SendOpts); +sync_get(UserId, TargetName, Context, [OH|_] = Oids) + when is_list(Context) andalso is_list(OH) -> + SendOpts = [{context, Context}], + sync_get2(UserId, TargetName, Oids, SendOpts). sync_get(UserId, TargetName, Context, Oids, Timeout) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, TargetName, Context, Oids, Timeout]), - snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout). + SendOpts = [{context, Context}, {timeout, Timeout}], + sync_get2(UserId, TargetName, Oids, SendOpts). sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p" -%% "~n ExtraInfo: ~p", -%% [UserId, TargetName, Context, Oids, Timeout, ExtraInfo]), - snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). + SendOpts = [{context, Context}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get2(UserId, TargetName, Oids, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> g(UserId, Addr, Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p", [UserId, Addr, Oids]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). g(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p", [UserId, Addr, CtxName, Oids]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); g(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Oids: ~p", [UserId, Addr, Port, Oids]), g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); g(UserId, Addr, Oids, Timeout) when is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, Addr, Oids, Timeout]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout). g(UserId, Addr, Port, CtxName, Oids) when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p", [UserId, Addr, Port, CtxName, Oids]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids); Error -> Error @@ -567,59 +570,28 @@ g(UserId, Addr, Port, CtxName, Oids) g(UserId, Addr, Port, Oids, Timeout) when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, Oids, Timeout]), g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout); g(UserId, Addr, CtxName, Oids, Timeout) when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, CtxName, Oids, Timeout]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout). g(UserId, Addr, Port, CtxName, Oids, Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, Port, CtxName, Oids, Timeout]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids, Timeout); Error -> Error end. g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p" -%% "~n ExtraInfo: ~p", -%% [UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo); Error -> Error end. +%% </DEPRECATED> @@ -629,23 +601,36 @@ g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> %% through a call to handle_pdu/5 %% -async_get(UserId, TargetName, Oids) -> - async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). +async_get2(UserId, TargetName, Oids) -> + async_get2(UserId, TargetName, Oids, []). + +async_get2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:async_get2(UserId, TargetName, Oids, SendOpts). -async_get(UserId, TargetName, Context, Oids) when is_list(Oids) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids); +%% <BACKWARD-COMPAT> +async_get(UserId, TargetName, Oids) -> + async_get2(UserId, TargetName, Oids). async_get(UserId, TargetName, Oids, Expire) when is_integer(Expire) -> - async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Expire). + SendOpts = [{timeout, Expire}], + async_get2(UserId, TargetName, Oids, SendOpts); +async_get(UserId, TargetName, Context, Oids) + when is_list(Context) andalso is_list(Oids) -> + SendOpts = [{context, Context}], + async_get2(UserId, TargetName, Oids, SendOpts). async_get(UserId, TargetName, Context, Oids, Expire) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_get2(UserId, TargetName, Oids, SendOpts). async_get(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire, - ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get2(UserId, TargetName, Oids, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> ag(UserId, Addr, Oids) -> ag(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -690,31 +675,44 @@ ag(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> %% --- synchroneous get_next-request --- %% -sync_get_next(UserId, TargetName, Oids) -> - sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). +sync_get_next2(UserId, TargetName, Oids) -> + sync_get_next2(UserId, TargetName, Oids, []). -sync_get_next(UserId, TargetName, Context, Oids) - when is_list(Context) andalso is_list(Oids) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids); +sync_get_next2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:sync_get_next2(UserId, TargetName, Oids, SendOpts). + +%% <BACKWARD-COMPAT> +sync_get_next(UserId, TargetName, Oids) -> + sync_get_next2(UserId, TargetName, Oids). sync_get_next(UserId, TargetName, Oids, Timeout) when is_list(Oids) andalso is_integer(Timeout) -> - sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). + SendOpts = [{timeout, Timeout}], + sync_get_next2(UserId, TargetName, Oids, SendOpts); +sync_get_next(UserId, TargetName, Context, Oids) + when is_list(Context) andalso is_list(Oids) -> + SendOpts = [{context, Context}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). sync_get_next(UserId, TargetName, Context, Oids, Timeout) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> gn(UserId, Addr, Oids) -> gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -759,30 +757,44 @@ gn(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> %% --- asynchroneous get_next-request --- %% +async_get_next2(UserId, TargetName, Oids) -> + async_get_next2(UserId, TargetName, Oids, []). + +async_get_next2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:async_get_next2(UserId, TargetName, Oids, SendOpts). + +%% <BACKWARD-COMPAT> async_get_next(UserId, TargetName, Oids) -> - async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). + async_get_next2(UserId, TargetName, Oids). +async_get_next(UserId, TargetName, Oids, Expire) + when is_list(Oids) andalso is_integer(Expire) -> + SendOpts = [{timeout, Expire}], + async_get_next2(UserId, TargetName, Oids, SendOpts); async_get_next(UserId, TargetName, Context, Oids) when is_list(Context) andalso is_list(Oids) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids); + SendOpts = [{context, Context}], + async_get_next2(UserId, TargetName, Oids, SendOpts). -async_get_next(UserId, TargetName, Oids, Timeout) - when is_list(Oids) andalso is_integer(Timeout) -> - async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). +async_get_next(UserId, TargetName, Context, Oids, Expire) -> + SendOpts = [{timeout, Expire}, {context, Context}], + async_get_next2(UserId, TargetName, Oids, SendOpts). -async_get_next(UserId, TargetName, Context, Oids, Timeout) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout). +async_get_next(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get_next2(UserId, TargetName, Oids, SendOpts). +%% </BACKWARD-COMPAT> -async_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). +%% <DEPRECATED> agn(UserId, Addr, Oids) -> agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -828,31 +840,44 @@ agn(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> %% --- synchroneous set-request --- %% -sync_set(UserId, TargetName, VarsAndVals) -> - sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals). +sync_set2(UserId, TargetName, VarsAndVals) -> + sync_set2(UserId, TargetName, VarsAndVals, []). -sync_set(UserId, TargetName, Context, VarsAndVals) - when is_list(Context) andalso is_list(VarsAndVals) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals); +sync_set2(UserId, TargetName, VarsAndVals, SendOpts) + when is_list(VarsAndVals) andalso is_list(SendOpts) -> + snmpm_server:sync_set2(UserId, TargetName, VarsAndVals, SendOpts). + +%% <BACKWARD-COMPAT> +sync_set(UserId, TargetName, VarsAndVals) -> + sync_set2(UserId, TargetName, VarsAndVals). sync_set(UserId, TargetName, VarsAndVals, Timeout) when is_list(VarsAndVals) andalso is_integer(Timeout) -> - sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Timeout). + SendOpts = [{timeout, Timeout}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts); +sync_set(UserId, TargetName, Context, VarsAndVals) + when is_list(Context) andalso is_list(VarsAndVals) -> + SendOpts = [{context, Context}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). sync_set(UserId, TargetName, Context, VarsAndVals, Timeout) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, - ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> s(UserId, Addr, VarsAndVals) -> s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). @@ -906,31 +931,44 @@ s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> %% --- asynchroneous set-request --- %% -async_set(UserId, TargetName, VarsAndVals) -> - async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals). +async_set2(UserId, TargetName, VarsAndVals) -> + async_set2(UserId, TargetName, VarsAndVals, []). -async_set(UserId, TargetName, Context, VarsAndVals) - when is_list(Context) andalso is_list(VarsAndVals) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals); +async_set2(UserId, TargetName, VarsAndVals, SendOpts) + when is_list(VarsAndVals) andalso is_list(SendOpts) -> + snmpm_server:async_set2(UserId, TargetName, VarsAndVals, SendOpts). + +%% <BACKWARD-COMPAT> +async_set(UserId, TargetName, VarsAndVals) -> + async_set2(UserId, TargetName, VarsAndVals). async_set(UserId, TargetName, VarsAndVals, Expire) when is_list(VarsAndVals) andalso is_integer(Expire) -> - async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Expire). + SendOpts = [{timeout, Expire}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts); +async_set(UserId, TargetName, Context, VarsAndVals) + when is_list(Context) andalso is_list(VarsAndVals) -> + SendOpts = [{context, Context}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). async_set(UserId, TargetName, Context, VarsAndVals, Expire) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). async_set(UserId, TargetName, Context, VarsAndVals, Expire, ExtraInfo) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire, - ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> as(UserId, Addr, VarsAndVals) -> as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). @@ -984,44 +1022,77 @@ as(UserId, Addr, Port, CtxName, VarsAndVals, Expire, ExtraInfo) -> Error -> Error end. - - +%% </DEPRECATED> %% --- synchroneous get-bulk --- %% -sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> - sync_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids). +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). -sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) when is_integer(NonRep) andalso is_integer(MaxRep) andalso - is_list(Context) andalso - is_list(Oids) -> - snmpm_server:sync_get_bulk(UserId, TargetName, - NonRep, MaxRep, - Context, Oids); + is_list(Oids) andalso + is_list(SendOpts) -> + %% p("sync_get_bulk -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n SendOpts: ~p", + %% [UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), + snmpm_server:sync_get_bulk2(UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts). + +%% <BACKWARD-COMPAT> +sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Timeout) when is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> - sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - ?DEFAULT_CONTEXT, Oids, Timeout). + SendOpts = [{timeout, Timeout}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts); +sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) + when is_integer(NonRep) andalso + is_integer(MaxRep) andalso + is_list(Context) andalso + is_list(Oids) -> + %% p("sync_get_bulk -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Context: ~p" + %% "~n Oids: ~p", [UserId, TargetName, NonRep, MaxRep, Context, Oids]), + SendOpts = [{context, Context}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout) -> - snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - Context, Oids, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - Context, Oids, Timeout, ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> gb(UserId, Addr, NonRep, MaxRep, Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, NonRep, MaxRep, Oids]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). gb(UserId, Addr, Port, NonRep, MaxRep, Oids) @@ -1029,6 +1100,14 @@ gb(UserId, Addr, Port, NonRep, MaxRep, Oids) is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, Oids]), gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids); gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) @@ -1036,6 +1115,14 @@ gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) is_integer(MaxRep) andalso is_list(CtxName) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids); gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout) @@ -1043,6 +1130,14 @@ gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout) is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, NonRep, MaxRep, Oids, Timeout]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Timeout). gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) @@ -1051,8 +1146,18 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) is_integer(MaxRep) andalso is_list(CtxName) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids]), case target_name(Addr, Port) of {ok, TargetName} -> + %% p("gb -> TargetName: ~p", [TargetName]), sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids); Error -> Error @@ -1064,6 +1169,15 @@ gb(UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout) is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout]), gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Timeout); gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout) @@ -1072,10 +1186,29 @@ gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids, Timeout). gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout]), case target_name(Addr, Port) of {ok, TargetName} -> sync_get_bulk(UserId, TargetName, @@ -1085,6 +1218,17 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) -> end. gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p" + %% "~n ExtraInfo: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo]), case target_name(Addr, Port) of {ok, TargetName} -> sync_get_bulk(UserId, TargetName, @@ -1092,42 +1236,55 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> %% --- asynchroneous get-bulk --- %% -async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> - async_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids). +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). -async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) when is_integer(NonRep) andalso is_integer(MaxRep) andalso - is_list(Context) andalso - is_list(Oids) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, Context, Oids); + is_list(Oids) andalso + is_list(SendOpts) -> + snmpm_server:async_get_bulk2(UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts). + +%% <BACKWARD-COMPAT> +async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) when is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Expire) -> - async_get_bulk(UserId, TargetName, - NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire). + SendOpts = [{timeout, Expire}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts); +async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) + when is_integer(NonRep) andalso + is_integer(MaxRep) andalso + is_list(Context) andalso + is_list(Oids) -> + SendOpts = [{context, Context}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, Context, Oids, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire, ExtraInfo) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, - Context, Oids, Expire, ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). +%% </BACKWARD-COMPAT> +%% <DEPRECATED> agb(UserId, Addr, NonRep, MaxRep, Oids) -> agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). @@ -1200,6 +1357,7 @@ agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% </DEPRECATED> cancel_async_request(UserId, ReqId) -> @@ -1207,7 +1365,7 @@ cancel_async_request(UserId, ReqId) -> %%%----------------------------------------------------------------- -%%% Audit Trail Log functions (for backward compatibillity) +%%% Audit Trail Log functions (for backward compatibility) %%%----------------------------------------------------------------- log_to_txt(LogDir, Mibs) -> OutFile = "snmpm_log.txt", diff --git a/lib/snmp/src/manager/snmpm_internal.hrl b/lib/snmp/src/manager/snmpm_internal.hrl index 5d9b32e3f6..53ad41c6b0 100644 --- a/lib/snmp/src/manager/snmpm_internal.hrl +++ b/lib/snmp/src/manager/snmpm_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,6 +20,11 @@ -ifndef(snmpm_internal). -define(snmpm_internal, true). +-define(DEFAULT_CONTEXT, ""). +-define(SNMPM_EXTRA_INFO_TAG, snmpm_extra_info_tag). +-define(DEFAULT_EXTRA_INFO, {?SNMPM_EXTRA_INFO_TAG}). + + -include_lib("snmp/src/app/snmp_internal.hrl"). -define(snmpm_info(F, A), ?snmp_info("manager", F, A)). diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index 07156dacd9..3d248fff57 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -99,7 +99,7 @@ stop(Pid) -> call(Pid, stop). send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port) -> - send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, undefined). + send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ?DEFAULT_EXTRA_INFO). send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo) when is_record(Pdu, pdu) -> @@ -380,13 +380,14 @@ handle_call(Req, From, State) -> %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- -handle_cast({send_pdu, Pdu, Vsn, MsgData, Addr, Port, _ExtraInfo}, State) -> +handle_cast({send_pdu, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo}, State) -> ?vlog("received send_pdu message with" "~n Pdu: ~p" "~n Vsn: ~p" "~n MsgData: ~p" "~n Addr: ~p" "~n Port: ~p", [Pdu, Vsn, MsgData, Addr, Port]), + maybe_process_extra_info(ExtraInfo), maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State), {noreply, State}; @@ -999,6 +1000,19 @@ pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) -> %% ------------------------------------------------------------------- +%% At this point this function is used during testing +maybe_process_extra_info(?DEFAULT_EXTRA_INFO) -> + ok; +maybe_process_extra_info({?SNMPM_EXTRA_INFO_TAG, Fun}) + when is_function(Fun, 0) -> + (catch Fun()), + ok; +maybe_process_extra_info(_ExtraInfo) -> + ok. + + +%% ------------------------------------------------------------------- + t() -> {A,B,C} = erlang:now(), A*1000000000+B*1000+(C div 1000). diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index d64b5b1d53..b8d7cf6375 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -32,14 +32,14 @@ register_user/4, register_user_monitor/4, unregister_user/1, - sync_get/4, sync_get/5, sync_get/6, - async_get/4, async_get/5, async_get/6, - sync_get_next/4, sync_get_next/5, sync_get_next/6, - async_get_next/4, async_get_next/5, async_get_next/6, - sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, - async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, - sync_set/4, sync_set/5, sync_set/6, - async_set/4, async_set/5, async_set/6, + sync_get2/4, + async_get2/4, + sync_get_next2/4, + async_get_next2/4, + sync_get_bulk2/6, + async_get_bulk2/6, + sync_set2/4, + async_set2/4, cancel_async_request/2, %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6, @@ -55,6 +55,20 @@ ]). +%% <BACKWARD-COMPAT> +-export([sync_get/4, sync_get/5, sync_get/6, + async_get/4, async_get/5, async_get/6, + sync_get_next/4, sync_get_next/5, sync_get_next/6, + async_get_next/4, async_get_next/5, async_get_next/6, + sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, + async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, + sync_set/4, sync_set/5, sync_set/6, + async_set/4, async_set/5, async_set/6 + ]). +%% </BACKWARD-COMPAT> + + + %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). @@ -75,13 +89,39 @@ -define(SERVER, ?MODULE). --define(SYNC_GET_TIMEOUT, 5000). --define(SYNC_SET_TIMEOUT, 5000). --define(DEFAULT_ASYNC_EXPIRE, 5000). --define(EXTRA_INFO, undefined). - --define(SNMP_AGENT_PORT, 161). - +-define(DEFAULT_SYNC_TIMEOUT, timer:seconds(5)). +-define(DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_GET_NEXT_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_GET_BULK_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_SET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). + +-define(DEFAULT_ASYNC_TIMEOUT, timer:seconds(5)). +-define(DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). + +-define(SNMP_AGENT_PORT, 161). + +-define(SYNC_GET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_TIMEOUT, SendOpts)). +-define(SYNC_GET_NEXT_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_NEXT_TIMEOUT, SendOpts)). +-define(SYNC_GET_BULK_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_BULK_TIMEOUT, SendOpts)). +-define(SYNC_SET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_SET_TIMEOUT, SendOpts)). + +-define(ASYNC_GET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_TIMEOUT, SendOpts)). +-define(ASYNC_GET_NEXT_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_NEXT_TIMEOUT, SendOpts)). +-define(ASYNC_GET_BULK_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_BULK_TIMEOUT, SendOpts)). +-define(ASYNC_SET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_SET_TIMEOUT, SendOpts)). + +-define(GET_EXTRA(SendOpts), get_opt(extra, ?DEFAULT_EXTRA_INFO, SendOpts)). -ifdef(snmp_debug). -define(GS_START_LINK(Args), @@ -192,82 +232,140 @@ unregister_user(UserId) -> %% -- [sync] get -- +%% The reason why we have a sync_get2 is to simplify backward +%% compatibillity. + +%% sync_get2(UserId, TargetName, Oids) -> +%% sync_get2(UserId, TargetName, Oids, []). +sync_get2(UserId, TargetName, Oids, Opts) -> + call({sync_get, self(), UserId, TargetName, Oids, Opts}). + + +%% <BACKWARD-COMPAT> sync_get(UserId, TargetName, CtxName, Oids) -> sync_get(UserId, TargetName, CtxName, Oids, - ?SYNC_GET_TIMEOUT). + ?DEFAULT_SYNC_GET_TIMEOUT). sync_get(UserId, TargetName, CtxName, Oids, Timeout) -> - sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO). + sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?DEFAULT_EXTRA_INFO). sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) when is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get, self(), UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}). + Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get2(UserId, TargetName, Oids, Opts). +%% </BACKWARD-COMPAT> + + %% -- [async] get -- +%% async_get2(UserId, TargetName, Oids) -> +%% async_get2(UserId, TargetName, Oids, []). +async_get2(UserId, TargetName, Oids, SendOpts) -> + call({async_get, self(), UserId, TargetName, Oids, SendOpts}). + + +%% <BACKWARD-COMPAT> async_get(UserId, TargetName, CtxName, Oids) -> async_get(UserId, TargetName, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get(UserId, TargetName, CtxName, Oids, Expire) -> - async_get(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO). + async_get(UserId, TargetName, CtxName, Oids, Expire, ?DEFAULT_EXTRA_INFO). async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_get, self(), UserId, TargetName, CtxName, Oids, Expire, - ExtraInfo}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get2(UserId, TargetName, Oids, SendOpts). +%% </BACKWARD-COMPAT> + + %% -- [sync] get-next -- +%% sync_get_next2(UserId, TargetName, Oids) -> +%% sync_get_next2(UserId, TargetName, Oids, []). +sync_get_next2(UserId, TargetName, Oids, SendOpts) -> + call({sync_get_next, self(), UserId, TargetName, Oids, SendOpts}). + + +%% <BACKWARD-COMPAT> sync_get_next(UserId, TargetName, CtxName, Oids) -> - sync_get_next(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT, - ?EXTRA_INFO). + sync_get_next(UserId, TargetName, CtxName, Oids, + ?DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_get_next(UserId, TargetName, CtxName, Oids, Timeout) -> - sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO). + sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, + ?DEFAULT_EXTRA_INFO). sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) when is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get_next, self(), UserId, TargetName, CtxName, Oids, Timeout, - ExtraInfo}). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). +%% <BACKWARD-COMPAT> + + %% -- [async] get-next -- +%% async_get_next2(UserId, TargetName, Oids) -> +%% async_get_next2(UserId, TargetName, Oids, []). +async_get_next2(UserId, TargetName, Oids, SendOpts) -> + call({async_get_next, self(), UserId, TargetName, Oids, SendOpts}). + + async_get_next(UserId, TargetName, CtxName, Oids) -> async_get_next(UserId, TargetName, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get_next(UserId, TargetName, CtxName, Oids, Expire) -> - async_get_next(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO). + async_get_next(UserId, TargetName, CtxName, Oids, Expire, + ?DEFAULT_EXTRA_INFO). async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_get_next, self(), UserId, TargetName, CtxName, Oids, - Expire, ExtraInfo}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get_next2(UserId, TargetName, Oids, SendOpts). + + %% -- [sync] get-bulk -- +%% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> +%% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + %% p("sync_get_bulk2 -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n SendOpts: ~p", + %% [UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), + call({sync_get_bulk, self(), UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts}). + sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - ?SYNC_GET_TIMEOUT, ?EXTRA_INFO). + ?DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - Timeout, ?EXTRA_INFO). + Timeout, ?DEFAULT_EXTRA_INFO). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) @@ -277,20 +375,28 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get_bulk, self(), UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). + %% -- [async] get-bulk -- +%% async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> +%% async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({async_get_bulk, self(), UserId, TargetName, NonRep, MaxRep, + Oids, SendOpts}). + + async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - Expire, ?EXTRA_INFO). + Expire, ?DEFAULT_EXTRA_INFO). async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) @@ -300,45 +406,61 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) -> - call({async_get_bulk, self(), UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). + + %% -- [sync] set -- %% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value} +%% sync_set2(UserId, TargetName, VarsAndVals) -> +%% sync_set2(UserId, TargetName, VarsAndVals, []). +sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> + call({sync_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). + + sync_set(UserId, TargetName, CtxName, VarsAndVals) -> sync_set(UserId, TargetName, CtxName, VarsAndVals, - ?SYNC_SET_TIMEOUT, ?EXTRA_INFO). + ?DEFAULT_SYNC_SET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout) -> sync_set(UserId, TargetName, CtxName, VarsAndVals, - Timeout, ?EXTRA_INFO). + Timeout, ?DEFAULT_EXTRA_INFO). sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo) when is_list(TargetName) andalso is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Timeout) -> - call({sync_set, self(), UserId, TargetName, - CtxName, VarsAndVals, Timeout, ExtraInfo}). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). + + %% -- [async] set -- +%% async_set2(UserId, TargetName, VarsAndVals) -> +%% async_set2(UserId, TargetName, VarsAndVals, []). +async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> + call({async_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). + + async_set(UserId, TargetName, CtxName, VarsAndVals) -> async_set(UserId, TargetName, CtxName, VarsAndVals, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_set(UserId, TargetName, CtxName, VarsAndVals, Expire) -> async_set(UserId, TargetName, CtxName, VarsAndVals, - Expire, ?EXTRA_INFO). + Expire, ?DEFAULT_EXTRA_INFO). async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_set, self(), UserId, TargetName, - CtxName, VarsAndVals, Expire, ExtraInfo}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). cancel_async_request(UserId, ReqId) -> @@ -571,9 +693,26 @@ handle_call({unregister_user, UserId}, _From, State) -> %% We will reply to this request later, when the reply comes in from the %% agent, or when the timeout hits (unless we get an error now). -handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, +handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_get request for: " + "~n ~p", [UserId, TargetName, Oids]), + case (catch handle_sync_get(Pid, + UserId, TargetName, Oids, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade +handle_call({sync_get, + Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received sync_get request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -582,10 +721,30 @@ handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInf Error -> {reply, Error, State} end; +%% </BACKWARD-COMPAT> + + +handle_call({sync_get_next, Pid, UserId, TargetName, Oids, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_get_next request for: " + "~n ~p", [UserId, TargetName, SendOpts]), + case (catch handle_sync_get_next(Pid, + UserId, TargetName, Oids, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; -handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get_next [~p] request", [CtxName]), +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade +handle_call({sync_get_next, + Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, + From, State) -> + ?vlog("[~p,~p,~p] received sync_get_next request for" + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -594,13 +753,31 @@ handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, Ext Error -> {reply, Error, State} end; +%% </BACKWARD-COMPAT> %% Check agent version? This op not in v1 +handle_call({sync_get_bulk, + Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_get_bulk request for: " + "~n ~p", [UserId, TargetName, Oids]), + case (catch handle_sync_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, + SendOpts, From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({sync_get_bulk, Pid, UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get_bulk [~p] request", [CtxName]), + ?vlog("[~p,~p] received sync_get_bulk request for: ~p" + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, @@ -610,12 +787,31 @@ handle_call({sync_get_bulk, Pid, UserId, TargetName, Error -> {reply, Error, State} end; +%% </BACKWARD-COMPAT> + + +handle_call({sync_set, + Pid, UserId, TargetName, VarsAndVals, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_set request for: " + "~n ~p", [UserId, TargetName, VarsAndVals]), + case (catch handle_sync_set(Pid, + UserId, TargetName, VarsAndVals, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({sync_set, Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_set [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received sync_set request for: " + "~n ~p", [UserId, TargetName, CtxName, VarsAndVals]), case (catch handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo, From, State)) of @@ -624,45 +820,105 @@ handle_call({sync_set, Pid, UserId, TargetName, Error -> {reply, Error, State} end; +%% </BACKWARD-COMPAT> +handle_call({async_get, Pid, UserId, TargetName, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get(Pid, + UserId, TargetName, Oids, SendOpts, + State)), + {reply, Reply, State}; + + +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({async_get, Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% </BACKWARD-COMPAT> + + +handle_call({async_get_next, Pid, UserId, TargetName, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get_next request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get_next(Pid, + UserId, TargetName, Oids, SendOpts, + State)), + {reply, Reply, State}; +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({async_get_next, Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get_next [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get_next request for: ", + [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% </BACKWARD-COMPAT> %% Check agent version? This op not in v1 +handle_call({async_get_bulk, + Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get_bulk request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get_bulk(Pid, + UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts, + State)), + {reply, Reply, State}; + + +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({async_get_bulk, Pid, UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get_bulk [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get_bulk request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% </BACKWARD-COMPAT> + + +handle_call({async_set, + Pid, UserId, TargetName, VarsAndVals, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_set request for: " + "~n ~p", [UserId, TargetName, VarsAndVals]), + Reply = (catch handle_async_set(Pid, + UserId, TargetName, VarsAndVals, SendOpts, + State)), + {reply, Reply, State}; +%% <BACKWARD-COMPAT> +%% The only case where this would be called is during code upgrade handle_call({async_set, Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_set [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_set request for: " + "~n ~p", [UserId, TargetName, CtxName, VarsAndVals]), Reply = (catch handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% </BACKWARD-COMPAT> handle_call({cancel_async_request, UserId, ReqId}, _From, State) -> @@ -901,36 +1157,46 @@ terminate(Reason, #state{gct = GCT}) -> handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State). + +handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> ?vtrace("handle_sync_get -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]), - case agent_data(TargetName, CtxName) of + [Pid, UserId, TargetName, Oids, SendOpts, From]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get -> send a ~p message", [Vsn]), - ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, - ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_request(Oids, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_GET_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; Error -> @@ -940,39 +1206,49 @@ handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, Error end. - handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, From, State). + +handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, + From, State) -> ?vtrace("handle_sync_get_next -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]), - case agent_data(TargetName, CtxName) of + [Pid, UserId, TargetName, Oids, SendOpts, From]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]), - ReqId = send_get_next_request(Oids, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_next_request(Oids, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_get_next -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_GET_NEXT_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get_next -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get_next, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get_next, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; @@ -987,39 +1263,50 @@ handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, + SendOpts, From, State). + +handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + From, State) -> ?vtrace("handle_sync_get_bulk -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n NonRep: ~p" "~n MaxRep: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, - Timeout, From]), - case agent_data(TargetName, CtxName) of + [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, From]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]), - ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, - NonRep, MaxRep, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, + NonRep, MaxRep, Extra, State), ?vdebug("handle_sync_get_bulk -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_GET_BULK_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get_bulk -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get_bulk, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get_bulk, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; @@ -1033,36 +1320,47 @@ handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, + From, State). + +handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, From, State) -> ?vtrace("handle_sync_set -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n VarsAndVals: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, From]), - case agent_data(TargetName, CtxName) of + [Pid, UserId, TargetName, VarsAndVals, From]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_set -> send a ~p message", [Vsn]), - ReqId = send_set_request(VarsAndVals, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_set_request(VarsAndVals, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_set -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_SET_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_set -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = set, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = set, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; @@ -1076,20 +1374,30 @@ handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State). + +handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> ?vtrace("handle_async_get -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Expire]), - case agent_data(TargetName, CtxName) of + "~n SendOpts: ~p", + [Pid, UserId, TargetName, Oids, SendOpts]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, - ExtraInfo, State), + Extra, State), ?vdebug("handle_async_get -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1114,20 +1422,30 @@ handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State). + +handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State) -> ?vtrace("handle_async_get_next -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Expire]), - case agent_data(TargetName, CtxName) of + "~n SendOpts: ~p", + [Pid, UserId, TargetName, Oids, SendOpts]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_next_request(Oids, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Addr, Port, Extra, State), ?vdebug("handle_async_get_next -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_NEXT_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1153,22 +1471,36 @@ handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, handle_async_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + State). + +handle_async_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + State) -> ?vtrace("handle_async_get_bulk -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n NonRep: ~p" "~n MaxRep: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire]), - case agent_data(TargetName, CtxName) of + "~n SendOpts: ~p", + [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_bulk -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, - NonRep, MaxRep, ExtraInfo, State), + NonRep, MaxRep, Extra, State), ?vdebug("handle_async_get_bulk -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_BULK_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1192,20 +1524,30 @@ handle_async_get_bulk(Pid, UserId, TargetName, CtxName, handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, State). + +handle_async_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, State) -> ?vtrace("handle_async_set -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n VarsAndVals: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, VarsAndVals, Expire]), - case agent_data(TargetName, CtxName) of + "~n SendOpts: ~p", + [Pid, UserId, TargetName, VarsAndVals, SendOpts]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_set -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_set_request(VarsAndVals, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Addr, Port, Extra, State), ?vdebug("handle_async_set -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_SET_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -2798,10 +3140,7 @@ request_id() -> %%---------------------------------------------------------------------- -agent_data(TargetName, CtxName) -> - agent_data(TargetName, CtxName, []). - -agent_data(TargetName, CtxName, Config) -> +agent_data(TargetName, SendOpts) -> case snmpm_config:agent_info(TargetName, all) of {ok, Info} -> Version = agent_data_item(version, Info), @@ -2813,15 +3152,18 @@ agent_data(TargetName, CtxName, Config) -> DefSecLevel = agent_data_item(sec_level, Info), EngineId = agent_data_item(engine_id, Info), + CtxName = agent_data_item(context, + SendOpts, + ?DEFAULT_CONTEXT), SecModel = agent_data_item(sec_model, - Config, + SendOpts, DefSecModel), SecName = agent_data_item(sec_name, - Config, + SendOpts, DefSecName), SecLevel = agent_data_item(sec_level, - Config, + SendOpts, DefSecLevel), {SecModel, SecName, mk_sec_level_flag(SecLevel), @@ -2831,10 +3173,10 @@ agent_data(TargetName, CtxName, Config) -> DefSecModel = agent_data_item(sec_model, Info), Comm = agent_data_item(community, - Config, + SendOpts, DefComm), SecModel = agent_data_item(sec_model, - Config, + SendOpts, DefSecModel), {Comm, SecModel} @@ -3003,6 +3345,12 @@ default_agent_config() -> %%---------------------------------------------------------------------- +get_opt(Key, Default, Opts) -> + proplists:get_value(Key, Opts, Default). + + +%%---------------------------------------------------------------------- + is_started(#state{net_if = _Pid, net_if_mod = _Mod}) -> %% Mod:is_started(Pid) and snmpm_config:is_started(). case snmpm_config:is_started() of diff --git a/lib/snmp/src/manager/snmpm_usm.erl b/lib/snmp/src/manager/snmpm_usm.erl index 449127844a..ef2070a90e 100644 --- a/lib/snmp/src/manager/snmpm_usm.erl +++ b/lib/snmp/src/manager/snmpm_usm.erl @@ -476,14 +476,19 @@ set_engine_latest_time(SnmpEngineID, EngineTime) -> %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- +-spec error(term()) -> no_return(). error(Reason) -> throw({error, Reason}). +-spec error(term(), term()) -> no_return(). error(Reason, ErrorInfo) -> throw({error, Reason, ErrorInfo}). +-spec error(term(), term(), term()) -> no_return(). error(Variable, Oid, SecName) -> error(Variable, Oid, SecName, []). + +-spec error(term(), term(), term(), [term()]) -> no_return(). error(Variable, Oid, SecName, Opts) -> Val = inc(Variable), ErrorInfo = {#varbind{oid = Oid, diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 4d2f5d8f92..cb5b3bbfbd 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -37,7 +37,13 @@ check_timer/1, - check_ip/1, check_taddress/1, + check_domain/1, + check_tdomain/1, + mk_tdomain/1, + which_domain/1, + check_ip/1, check_ip/2, + check_taddress/1, check_taddress/2, + mk_taddress/3, check_packet_size/1, @@ -52,8 +58,10 @@ -define(SNMP_USE_V3, true). --include("snmp_types.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). -define(VMODULE,"CONF"). -include("snmp_verbosity.hrl"). @@ -338,15 +346,96 @@ check_sec_level(BadSecLevel) -> %% --------- -check_taddress(X) when is_list(X) andalso (length(X) =:= 6) -> +check_tdomain(TDomain) -> + SupportedTDomains = + [ + ?snmpUDPDomain, + ?transportDomainUdpIpv4, + ?transportDomainUdpIpv6 + ], + AllTDomains = + [ + ?transportDomainUdpIpv4, + ?transportDomainUdpIpv6, + ?transportDomainUdpIpv4z, + ?transportDomainUdpIpv6z, + ?transportDomainTcpIpv4, + ?transportDomainTcpIpv6, + ?transportDomainTcpIpv4z, + ?transportDomainTcpIpv6z, + ?transportDomainSctpIpv4, + ?transportDomainSctpIpv6, + ?transportDomainSctpIpv4z, + ?transportDomainSctpIpv6z, + ?transportDomainLocal, + ?transportDomainUdpDns, + ?transportDomainTcpDns, + ?transportDomainSctpDns + ], + case lists:member(TDomain, SupportedTDomains) of + true -> + ok; + false -> + case lists:member(TDomain, AllTDomains) of + true -> + error({unsupported_tdomain, TDomain}); + false -> + error({unknown_tdomain, TDomain}) + end + end. + + +%% --------- + +mk_tdomain(snmpUDPDomain) -> + ?snmpUDPDomain; +mk_tdomain(transportDomainUdpIpv4) -> + ?transportDomainUdpIpv4; +mk_tdomain(transportDomainUdpIpv6) -> + ?transportDomainUdpIpv6; +mk_tdomain(BadDomain) -> + error({bad_domain, BadDomain}). + + +%% --------- + +check_taddress(X) -> + check_taddress(snmpUDPDomain, X). + +check_taddress(?snmpUDPDomain, X) -> + check_taddress(transportDomainUdpIpv4, X); +check_taddress(snmpUDPDomain, X) -> + check_taddress(transportDomainUdpIpv4, X); + +check_taddress(?transportDomainUdpIpv4, X) -> + check_taddress(transportDomainUdpIpv4, X); +check_taddress(transportDomainUdpIpv4, X) + when is_list(X) andalso (length(X) =:= 6) -> case (catch all_integer(X)) of true -> ok; false -> error({invalid_taddress, X}) end; -check_taddress(X) -> - error({invalid_taddress, X}). +check_taddress(transportDomainUdpIpv4, X) -> + error({invalid_taddress, X}); + +check_taddress(?transportDomainUdpIpv6, X) -> + check_taddress(transportDomainUdpIpv6, X); +check_taddress(transportDomainUdpIpv6, X) + when is_list(X) andalso (length(X) =:= 10) -> + case (catch all_integer(X)) of + true -> + ok; + false -> + error({invalid_taddress, X}) + end; +check_taddress(transportDomainUdpIpv6, X) -> + error({invalid_taddress, X}); + +check_taddress(BadDomain, _X) -> + error({invalid_tdomain, BadDomain}). + %% --------- @@ -385,15 +474,115 @@ do_check_timer(WaitFor, Factor, Incr, Retry) -> %% --------- -check_ip(X) when is_list(X) andalso (length(X) =:= 4) -> +check_domain(Domain) -> + SupportedDomains = + [ + snmpUDPDomain, + transportDomainUdpIpv4, + transportDomainUdpIpv6 + ], + AllDomains = + [ + transportDomainUdpIpv4, + transportDomainUdpIpv6, + transportDomainUdpIpv4z, + transportDomainUdpIpv6z, + transportDomainTcpIpv4, + transportDomainTcpIpv6, + transportDomainTcpIpv4z, + transportDomainTcpIpv6z, + transportDomainSctpIpv4, + transportDomainSctpIpv6, + transportDomainSctpIpv4z, + transportDomainSctpIpv6z, + transportDomainLocal, + transportDomainUdpDns, + transportDomainTcpDns, + transportDomainSctpDns + ], + case lists:member(Domain, SupportedDomains) of + true -> + ok; + false -> + case lists:member(Domain, AllDomains) of + true -> + error({unsupported_domain, Domain}); + false -> + error({unknown_domain, Domain}) + end + end. + + +%% --------- + +%% The values of Ip and Port has both been checked at this +%% point, so we dont need to do that again. +mk_taddress(snmpUDPDomain, Ip, Port) -> + mk_taddress(transportDomainUdpIpv4, Ip, Port); +mk_taddress(transportDomainUdpIpv4, Ip, Port) when is_list(Ip) -> + Ip ++ [Port div 256, Port rem 256]; +mk_taddress(transportDomainUdpIpv4 = Domain, Ip, Port) when is_tuple(Ip) -> + mk_taddress(Domain, tuple_to_list(Ip), Port); +mk_taddress(transportDomainUdpIpv6, Ip, Port) when is_list(Ip) -> + Ip ++ [Port div 256, Port rem 256]; +mk_taddress(transportDomainUdpIpv6 = Domain, Ip, Port) when is_tuple(Ip) -> + mk_taddress(Domain, tuple_to_list(Ip), Port); + +%% These are just for convenience +mk_taddress(?snmpUDPDomain, Ip, Port) -> + mk_taddress(snmpUDPDomain, Ip, Port); +mk_taddress(?transportDomainUdpIpv4, Ip, Port) -> + mk_taddress(transportDomainUdpIpv4, Ip, Port); +mk_taddress(?transportDomainUdpIpv6, Ip, Port) -> + mk_taddress(transportDomainUdpIpv6, Ip, Port); + +%% Bad domain +mk_taddress(BadDomain, _Ip, _Port) -> + error({bad_domain, BadDomain}). + + +%% --------- + +which_domain(Ip) when is_list(Ip) andalso (length(Ip) =:= 4) -> + transportDomainUdpIpv4; +which_domain(Ip) when is_tuple(Ip) andalso (size(Ip) =:= 4) -> + transportDomainUdpIpv4; +which_domain(Ip) when is_list(Ip) andalso (length(Ip) =:= 8) -> + transportDomainUdpIpv6; +which_domain(Ip) when is_tuple(Ip) andalso (size(Ip) =:= 8) -> + transportDomainUdpIpv6. + + +%% --------- + +check_ip(X) -> + check_ip(snmpUDPDomain, X). + +check_ip(snmpUDPDomain, X) -> + check_ip(transportDomainUdpIpv4, X); +check_ip(transportDomainUdpIpv4, X) when is_list(X) andalso (length(X) =:= 4) -> case (catch all_integer(X)) of true -> ok; false -> error({invalid_ip_address, X}) end; -check_ip(X) -> - error({invalid_ip_address, X}). +check_ip(transportDomainUdpIpv4, X) -> + error({invalid_ip_address, X}); + +check_ip(transportDomainUdpIpv6, X) when is_list(X) andalso (length(X) =:= 8) -> + case (catch all_integer(X)) of + true -> + ok; + false -> + error({invalid_ip_address, X}) + end; +check_ip(transportDomainUdpIpv6, X) -> + error({invalid_ip_address, X}); + +check_ip(BadDomain, _X) -> + error({invalid_domain, BadDomain}). + %% --------- diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 25350e08cb..813942225e 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -491,6 +491,9 @@ config_agent_snmp(Dir, Vsns) -> Host = host(), AgentIP = ask("5. IP address for the agent (only used as id ~n" " when sending traps)", Host, fun verify_address/1), + %% We intentionally skip TDomain... + %% If the user wish to use IPv6, the user must create an dummy entry here + %% and then manually edit these entries later. ManagerIP = ask("6. IP address for the manager (only this manager ~n" " will have access to the agent, traps are sent ~n" " to this one)", Host, fun verify_address/1), @@ -1062,9 +1065,19 @@ verify_sec_type(ST) -> {error, "invalid security type: " ++ ST}. verify_address(A) -> - case (catch snmp_misc:ip(A)) of + verify_address(A, snmpUDPDomain). + +verify_address(A, snmpUDPDomain = _Domain) -> + do_verify_address(A, inet); +verify_address(A, transportDomainUdpIpv4 = _Domain) -> + do_verify_address(A, inet); +verify_address(A, transportDomainUdpIpv6 = _Domain) -> + do_verify_address(A, inet6). + +do_verify_address(A, Family) -> + case (catch snmp_misc:ip(A, Family)) of {ok, IP} -> - {ok, tuple_to_list(IP)}; + {ok, tuple_to_list(IP)}; {error, _} -> {error, "invalid address: " ++ A}; _E -> @@ -1721,10 +1734,12 @@ write_agent_snmp_target_addr_conf(Dir, ManagerIp, UDP, Hdr = header() ++ Comment, F = fun(v1 = Vsn, Acc) -> [{mk_ip(ManagerIp, Vsn), + snmp_target_mib:default_domain(), ManagerIp, UDP, Timeout, RetryCount, "std_trap", mk_param(Vsn), "", [], 2048}| Acc]; (v2 = Vsn, Acc) -> [{mk_ip(ManagerIp, Vsn), + snmp_target_mib:default_domain(), ManagerIp, UDP, Timeout, RetryCount, "std_trap", mk_param(Vsn), "", [], 2048}, {lists:flatten(io_lib:format("~s.2",[mk_ip(ManagerIp, Vsn)])), @@ -1732,6 +1747,7 @@ write_agent_snmp_target_addr_conf(Dir, ManagerIp, UDP, "std_inform", mk_param(Vsn), "", [], 2048}| Acc]; (v3 = Vsn, Acc) -> [{mk_ip(ManagerIp, Vsn), + snmp_target_mib:default_domain(), ManagerIp, UDP, Timeout, RetryCount, "std_trap", mk_param(Vsn), "", [], 2048}, {lists:flatten(io_lib:format("~s.3",[mk_ip(ManagerIp, Vsn)])), diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl index f9aa911817..7930e37c66 100644 --- a/lib/snmp/src/misc/snmp_log.erl +++ b/lib/snmp/src/misc/snmp_log.erl @@ -266,9 +266,6 @@ validate_loop(eof, _Log, _Validatior, _PrevTS, _PrevSN) -> ok; validate_loop({error, _} = Error, _Log, _Validator, _PrevTS, _PrevSN) -> Error; -validate_loop({corrupt_log_file, _} = Reason, - _Log, _Validator, _PrevTS, _PrevSN) -> - {error, Reason}; validate_loop({Cont, Terms}, Log, Validator, PrevTS, PrevSN) -> ?vtrace("validate_loop -> entry with" "~n Terms: ~p" @@ -508,8 +505,6 @@ loop(eof, _Log, _Write) -> ok; loop({error, _} = Error, _Log, _Write) -> Error; -loop({corrupt_log_file, _} = Reason, _Log, _Write) -> - {error, Reason}; loop({Cont, Terms}, Log, Write) -> case (catch lists:foreach(Write, Terms)) of {'EXIT', Reason} -> diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl index 1b535743a4..6adef06ab9 100644 --- a/lib/snmp/src/misc/snmp_misc.erl +++ b/lib/snmp/src/misc/snmp_misc.erl @@ -40,7 +40,7 @@ get_option/2, get_option/3, get_sec_level/1, - ip/1, + ip/1, ip/2, is_auth/1, is_BitString/1, is_oid/1, @@ -347,10 +347,15 @@ bits_to_int([Kibble|Ks],Kibbles,Res) -> %%---------------------------------------------------------------------- -%% Returns: {ok, {int(),int(),int(),int()}} | {error, Reason} +%% Returns: {ok, {int(),int(),int(),int()}} | +%% {ok, {int(),int(),int(),int()},int(),int(),int(),int()} | +%% {error, Reason} %%---------------------------------------------------------------------- ip(Host) -> - inet:getaddr(Host, inet). + ip(Host, inet). + +ip(Host, Family) -> + inet:getaddr(Host, Family). ensure_trailing_dir_delimiter([]) -> "/"; ensure_trailing_dir_delimiter(DirSuggestion) -> diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index dc8900c8cd..82618a0b86 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -269,24 +269,35 @@ dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; dec_value([65 | Bytes]) -> + %% Counter32 is an unsigned 32 but is actually encoded as + %% a signed integer 32 (INTEGER). {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> - {{'Counter32', Value}, Rest}; - true -> - exit({error, {bad_counter32, Value}}) - end; + Value2 = + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + %% We accept value above 16#7fffffff + %% in order to be backward bug-compatible + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; + true -> + exit({error, {bad_counter32, Value}}) + end, + {{'Counter32', Value2}, Rest}; dec_value([66 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> + if + (Value >= 0) andalso (Value =< 4294967295) -> {{'Unsigned32', Value}, Rest}; - true -> + true -> exit({error, {bad_unsigned32, Value}}) end; dec_value([67 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> + if + (Value >= 0) andalso (Value =< 4294967295) -> {{'TimeTicks', Value}, Rest}; - true -> + true -> exit({error, {bad_timeticks, Value}}) end; dec_value([68 | Bytes]) -> @@ -642,6 +653,21 @@ enc_value(_Type, endOfMibView) -> [130,0]; enc_value('NULL', _Val) -> [5,0]; +enc_value('Counter32', Val) -> + Val2 = + if + Val > 16#ffffffff -> + exit({error, {bad_counter32, Val}}); + Val >= 16#80000000 -> + (Val band 16#7fffffff) - 16#80000000; + Val >= 0 -> + Val; + true -> + exit({error, {bad_counter32, Val}}) + end, + Bytes2 = enc_integer_notag(Val2), + Len2 = elength(length(Bytes2)), + lists:append([65 | Len2],Bytes2); enc_value('Counter64', Val) -> Val2 = if diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index b7975024b4..0e9c73081d 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -145,9 +145,12 @@ endif # ---------------------------------------------------- EBIN = . -ERL_COMPILE_FLAGS += -I../src \ +ERL_COMPILE_FLAGS += -I../../snmp/src/app \ + -I../../snmp/src/misc \ + -I../../snmp/src/agent \ + -I../../snmp/src/manager \ -I$(ERL_TOP)/lib/test_server/include \ - -I../include \ + -I../../snmp/include \ -Dsnmp_test_data=snmp_test_data \ -Dversion=\"$(VSN)$(PRE_VSN)\" \ +'{parse_transform,sys_pre_attributes}' \ diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 692d29fda0..acf62f5646 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -81,13 +81,12 @@ case get(vsn) of v1 -> V1; v2 -> V2; - _ -> V3 + _ -> V3 end). all() -> - Reqs = [mnesia, distribution, {local_slave_nodes, 2}, - {time, 360}], + %% Reqs = [mnesia, distribution, {local_slave_nodes, 2}, {time, 360}], Conf1 = [{group, all_tcs}], Conf2 = [{group, tickets2}], Conf1 ++ Conf2. @@ -95,67 +94,138 @@ all() -> groups() -> [{all_tcs, [], cases()}, {mib_storage, [], - [{group, mib_storage_ets}, {group, mib_storage_dets}, + [ + {group, mib_storage_ets}, + {group, mib_storage_dets}, {group, mib_storage_mnesia}, {group, mib_storage_size_check_ets}, {group, mib_storage_size_check_dets}, {group, mib_storage_size_check_mnesia}, {group, mib_storage_varm_dets}, - {group, mib_storage_varm_mnesia}]}, - {mib_storage_ets, [], mib_storage_ets_cases()}, - {mib_storage_dets, [], mib_storage_dets_cases()}, - {mib_storage_mnesia, [], mib_storage_mnesia_cases()}, - {mib_storage_size_check_ets, [], - mse_size_check_cases()}, - {mib_storage_size_check_dets, [], - msd_size_check_cases()}, - {mib_storage_size_check_mnesia, [], - msm_size_check_cases()}, - {mib_storage_varm_dets, [], - varm_mib_storage_dets_cases()}, - {mib_storage_varm_mnesia, [], - varm_mib_storage_mnesia_cases()}, - {misc, [], misc_cases()}, {test_v1, [], v1_cases()}, - {test_v2, [], v2_cases()}, - {test_v1_v2, [], v1_v2_cases()}, - {test_v3, [], v3_cases()}, - {test_multi_threaded, [], mt_cases()}, - {multiple_reqs, [], mul_cases()}, - {multiple_reqs_2, [], mul_cases_2()}, - {v2_inform, [], [v2_inform_i]}, + {group, mib_storage_varm_mnesia} + ] + }, + {mib_storage_ets, [], mib_storage_ets_cases()}, + {mib_storage_dets, [], mib_storage_dets_cases()}, + {mib_storage_mnesia, [], mib_storage_mnesia_cases()}, + {mib_storage_size_check_ets, [], mse_size_check_cases()}, + {mib_storage_size_check_dets, [], msd_size_check_cases()}, + {mib_storage_size_check_mnesia, [], msm_size_check_cases()}, + {mib_storage_varm_dets, [], varm_mib_storage_dets_cases()}, + {mib_storage_varm_mnesia, [], varm_mib_storage_mnesia_cases()}, + {misc, [], misc_cases()}, + {test_v1, [], v1_cases()}, + {test_v2, [], v2_cases()}, + {test_v1_v2, [], v1_v2_cases()}, + {test_v3, [], v3_cases()}, + {test_multi_threaded, [], mt_cases()}, + {multiple_reqs, [], mul_cases()}, + {multiple_reqs_2, [], mul_cases_2()}, + {v2_inform, [], + [ + v2_inform_i + ] + }, {v3_security, [], - [v3_crypto_basic, v3_md5_auth, v3_sha_auth, - v3_des_priv]}, + [ + v3_crypto_basic, + v3_md5_auth, + v3_sha_auth, + v3_des_priv + ] + }, {standard_mibs, [], - [snmp_standard_mib, snmp_community_mib, - snmp_framework_mib, snmp_target_mib, - snmp_notification_mib, snmp_view_based_acm_mib]}, + [ + snmp_standard_mib, + snmp_community_mib, + snmp_framework_mib, + snmp_target_mib, + snmp_notification_mib, + snmp_view_based_acm_mib + ] + }, {standard_mibs_2, [], - [snmpv2_mib_2, snmp_community_mib_2, - snmp_framework_mib_2, snmp_target_mib_2, - snmp_notification_mib_2, snmp_view_based_acm_mib_2]}, + [ + snmpv2_mib_2, + snmp_community_mib_2, + snmp_framework_mib_2, + snmp_target_mib_2, + snmp_notification_mib_2, + snmp_view_based_acm_mib_2 + ] + }, {standard_mibs_3, [], - [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3, - snmp_target_mib_3, snmp_notification_mib_3, - snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]}, + [ + snmpv2_mib_3, + snmp_framework_mib_3, + snmp_mpd_mib_3, + snmp_target_mib_3, + snmp_notification_mib_3, + snmp_view_based_acm_mib_3, + snmp_user_based_sm_mib_3 + ] + }, {reported_bugs, [], - [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222, - otp_1298, otp_1331, otp_1338, otp_1342, otp_2776, - otp_2979, otp_3187, otp_3725]}, + [ + otp_1128, + otp_1129, + otp_1131, + otp_1162, + otp_1222, + otp_1298, + otp_1331, + otp_1338, + otp_1342, + otp_2776, + otp_2979, + otp_3187, + otp_3725 + ] + }, {reported_bugs_2, [], - [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2, - otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2, - otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]}, + [ + otp_1128_2, + otp_1129_2, + otp_1131_2, + otp_1162_2, + otp_1222_2, + otp_1298_2, + otp_1331_2, + otp_1338_2, + otp_1342_2, + otp_2776_2, + otp_2979_2, + otp_3187_2 + ] + }, {reported_bugs_3, [], - [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3, - otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3, - otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3, - otp_3542]}, - {tickets1, [], [{group, otp_4394}, {group, otp_7157}]}, + [ + otp_1128_3, + otp_1129_3, + otp_1131_3, + otp_1162_3, + otp_1222_3, + otp_1298_3, + otp_1331_3, + otp_1338_3, + otp_1342_3, + otp_2776_3, + otp_2979_3, + otp_3187_3, + otp_3542 + ] + }, + {tickets1, [], + [ + {group, otp_4394}, + {group, otp_7157} + ] + }, {tickets2, [], [otp8395]}, {otp_4394, [], [otp_4394_test]}, - {otp_7157, [], - begin Reqs = [], Conf = [otp_7157_test], Conf end}]. + {otp_7157, [], [otp_7157_test] + } + ]. init_per_group(all_tcs, Config) -> init_all(Config); @@ -378,17 +448,29 @@ end_per_testcase2(_Case, Config) -> cases() -> -case ?OSTYPE() of - vxworks -> - [{group, misc}, {group, test_v1}, {group, test_v2}, - {group, test_v1_v2}, {group, test_multi_threaded}, - {group, mib_storage}, {group, tickets1}]; - _Else -> - [{group, misc}, {group, test_v1}, {group, test_v2}, - {group, test_v1_v2}, {group, test_v3}, - {group, test_multi_threaded}, {group, mib_storage}, - {group, tickets1}] -end. + case ?OSTYPE() of + vxworks -> + [ + {group, misc}, + {group, test_v1}, + {group, test_v2}, + {group, test_v1_v2}, + {group, test_multi_threaded}, + {group, mib_storage}, + {group, tickets1} + ]; + _Else -> + [ + {group, misc}, + {group, test_v1}, + {group, test_v2}, + {group, test_v1_v2}, + {group, test_v3}, + {group, test_multi_threaded}, + {group, mib_storage}, + {group, tickets1} + ] + end. %%%----------------------------------------------------------------- @@ -1071,11 +1153,29 @@ app_dir(App) -> %v1_cases() -> [loop_mib]; v1_cases() -> -[simple, db_notify_client, v1_processing, big, big2, - loop_mib, api, subagent, mnesia, {group, multiple_reqs}, - sa_register, v1_trap, sa_error, next_across_sa, undo, - {group, reported_bugs}, {group, standard_mibs}, - sparse_table, cnt_64, opaque, change_target_addr_config]. + [ + simple, + db_notify_client, + v1_processing, + big, + big2, + loop_mib, + api, + subagent, + mnesia, + {group, multiple_reqs}, + sa_register, + v1_trap, + sa_error, + next_across_sa, + undo, + {group, reported_bugs}, + {group, standard_mibs}, + sparse_table, + cnt_64, + opaque, + change_target_addr_config + ]. init_v1(Config) when is_list(Config) -> ?line SaNode = ?config(snmp_sa, Config), @@ -1094,12 +1194,31 @@ finish_v1(Config) when is_list(Config) -> v2_cases() -> -[simple_2, v2_processing, big_2, big2_2, loop_mib_2, - api_2, subagent_2, mnesia_2, {group, multiple_reqs_2}, - sa_register_2, v2_trap, {group, v2_inform}, sa_error_2, - next_across_sa_2, undo_2, {group, reported_bugs_2}, - {group, standard_mibs_2}, v2_types, implied, - sparse_table_2, cnt_64_2, opaque_2, v2_caps]. + [ + simple_2, + v2_processing, + big_2, + big2_2, + loop_mib_2, + api_2, + subagent_2, + mnesia_2, + {group, multiple_reqs_2}, + sa_register_2, + v2_trap, + {group, v2_inform}, + sa_error_2, + next_across_sa_2, + undo_2, + {group, reported_bugs_2}, + {group, standard_mibs_2}, + v2_types, + implied, + sparse_table_2, + cnt_64_2, + opaque_2, + v2_caps + ]. init_v2(Config) when is_list(Config) -> SaNode = ?config(snmp_sa, Config), @@ -1118,7 +1237,7 @@ finish_v2(Config) when is_list(Config) -> v1_v2_cases() -> -[simple_bi]. + [simple_bi]. init_v1_v2(Config) when is_list(Config) -> SaNode = ?config(snmp_sa, Config), @@ -1137,13 +1256,32 @@ finish_v1_v2(Config) when is_list(Config) -> v3_cases() -> -[simple_3, v3_processing, big_3, big2_3, api_3, - subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3, - sa_register_3, v3_trap, v3_inform, sa_error_3, - next_across_sa_3, undo_3, {group, reported_bugs_3}, - {group, standard_mibs_3}, {group, v3_security}, - v2_types_3, implied_3, sparse_table_3, cnt_64_3, - opaque_3, v2_caps_3]. + [ + simple_3, + v3_processing, + big_3, + big2_3, + api_3, + subagent_3, + mnesia_3, + loop_mib_3, + multiple_reqs_3, + sa_register_3, + v3_trap, + v3_inform, + sa_error_3, + next_across_sa_3, + undo_3, + {group, reported_bugs_3}, + {group, standard_mibs_3}, + {group, v3_security}, + v2_types_3, + implied_3, + sparse_table_3, + cnt_64_3, + opaque_3, + v2_caps_3 + ]. init_v3(Config) when is_list(Config) -> %% Make sure crypto works, otherwise start_agent will fail diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 9e89aa889c..3ae2409997 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -1311,10 +1311,12 @@ rewrite_target_addr_conf(Dir, NewPort) -> "~n NewPort: ~p", [NewPort]), TAFile = filename:join(Dir, "target_addr.conf"), case file:read_file_info(TAFile) of - {ok, _} -> ok; - {error, R} -> ?ERR("failure reading file info of " - "target address config file: ~p",[R]), - ok + {ok, _} -> + ok; + {error, R} -> + ?ERR("failure reading file info of " + "target address config file: ~p",[R]), + ok end, ?line [TrapAddr|Addrs] = @@ -1335,8 +1337,9 @@ rewrite_target_addr_conf(Dir, NewPort) -> rewrite_target_addr_conf_check(O) -> {ok,O}. -rewrite_target_addr_conf2(NewPort,{Name,Ip,_Port,Timeout,Retry, - "std_trap",EngineId}) -> +rewrite_target_addr_conf2(NewPort, + {Name, Ip, _Port, Timeout, Retry, + "std_trap", EngineId}) -> ?LOG("rewrite_target_addr_conf2 -> entry with std_trap",[]), {Name,Ip,NewPort,Timeout,Retry,"std_trap",EngineId}; rewrite_target_addr_conf2(_NewPort,O) -> diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 50836db731..6bd62df655 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -37,13 +37,16 @@ -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/include/STANDARD-MIB.hrl"). +-include_lib("snmp/src/manager/snmpm_internal.hrl"). %%---------------------------------------------------------------------- %% External exports %%---------------------------------------------------------------------- -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, + all/0, + groups/0, + init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, @@ -54,49 +57,45 @@ notify_started01/1, notify_started02/1, - register_user1/1, - register_agent1/1, register_agent2/1, - info/1, - - - simple_sync_get1/1, simple_sync_get2/1, + simple_sync_get3/1, simple_async_get1/1, simple_async_get2/1, - + simple_async_get3/1, simple_sync_get_next1/1, simple_sync_get_next2/1, + simple_sync_get_next3/1, simple_async_get_next1/1, simple_async_get_next2/1, - + simple_async_get_next3/1, simple_sync_set1/1, simple_sync_set2/1, + simple_sync_set3/1, simple_async_set1/1, simple_async_set2/1, - + simple_async_set3/1, simple_sync_get_bulk1/1, simple_sync_get_bulk2/1, + simple_sync_get_bulk3/1, simple_async_get_bulk1/1, simple_async_get_bulk2/1, - + simple_async_get_bulk3/1, misc_async1/1, misc_async2/1, discovery/1, - - trap1/1, trap2/1, @@ -109,8 +108,6 @@ report/1, - - otp8015_1/1, otp8395_1/1 @@ -220,7 +217,7 @@ init_per_testcase2(Case, Config) -> Conf2. init_per_testcase3(Case, Config) -> - OldApiCases = + ApiCases01 = [ simple_sync_get1, simple_async_get1, @@ -232,7 +229,7 @@ init_per_testcase3(Case, Config) -> simple_async_get_bulk1, misc_async1 ], - NewApiCases = + ApiCases02 = [ simple_sync_get2, simple_async_get2, @@ -245,6 +242,17 @@ init_per_testcase3(Case, Config) -> misc_async2, otp8395_1 ], + ApiCases03 = + [ + simple_sync_get3, + simple_async_get3, + simple_sync_get_next3, + simple_async_get_next3, + simple_sync_set3, + simple_async_set3, + simple_sync_get_bulk3, + simple_async_get_bulk3 + ], Cases = [ trap1, @@ -256,8 +264,9 @@ init_per_testcase3(Case, Config) -> inform_swarm, report ] ++ - OldApiCases ++ - NewApiCases, + ApiCases01 ++ + ApiCases02 ++ + ApiCases03, case lists:member(Case, Cases) of true -> NoAutoInformCases = [inform1, inform2, inform3, inform_swarm], @@ -279,7 +288,7 @@ init_per_testcase3(Case, Config) -> Conf2 = init_agent(Conf1), Conf3 = init_manager(AutoInform, Conf2), Conf4 = init_mgr_user(Conf3), - case lists:member(Case, NewApiCases) of + case lists:member(Case, ApiCases02 ++ ApiCases03) of true -> init_mgr_user_data2(Conf4); false -> @@ -301,7 +310,7 @@ end_per_testcase(Case, Config) when is_list(Config) -> Conf2. end_per_testcase2(Case, Config) -> - OldApiCases = + ApiCases01 = [ simple_sync_get1, simple_async_get1, @@ -313,7 +322,7 @@ end_per_testcase2(Case, Config) -> simple_async_get_bulk1, misc_async1 ], - NewApiCases = + ApiCases02 = [ simple_sync_get2, simple_async_get2, @@ -326,6 +335,17 @@ end_per_testcase2(Case, Config) -> misc_async2, otp8395_1 ], + ApiCases03 = + [ + simple_sync_get3, + simple_async_get3, + simple_sync_get_next3, + simple_async_get_next3, + simple_sync_set3, + simple_async_set3, + simple_sync_get_bulk3, + simple_async_get_bulk3 + ], Cases = [ trap1, @@ -337,11 +357,12 @@ end_per_testcase2(Case, Config) -> inform_swarm, report ] ++ - OldApiCases ++ - NewApiCases, + ApiCases01 ++ + ApiCases02 ++ + ApiCases03, case lists:member(Case, Cases) of true -> - Conf1 = case lists:member(Case, NewApiCases) of + Conf1 = case lists:member(Case, ApiCases02 ++ ApiCases03) of true -> fin_mgr_user_data2(Config); false -> @@ -360,42 +381,129 @@ end_per_testcase2(Case, Config) -> %%====================================================================== all() -> -[{group, start_and_stop_tests}, {group, misc_tests}, - {group, user_tests}, {group, agent_tests}, - {group, request_tests}, {group, event_tests}, discovery, - {group, tickets}]. + [ + {group, start_and_stop_tests}, + {group, misc_tests}, + {group, user_tests}, + {group, agent_tests}, + {group, request_tests}, + {group, event_tests}, + discovery, + {group, tickets} + ]. groups() -> - [{start_and_stop_tests, [], - [simple_start_and_stop1, simple_start_and_stop2, - simple_start_and_monitor_crash1, - simple_start_and_monitor_crash2, notify_started01, - notify_started02]}, - {misc_tests, [], [info]}, - {user_tests, [], [register_user1]}, - {agent_tests, [], [register_agent1, register_agent2]}, - {request_tests, [], - [{group, get_tests}, {group, get_next_tests}, - {group, set_tests}, {group, bulk_tests}, - {group, misc_request_tests}]}, - {get_tests, [], - [simple_sync_get1, simple_sync_get2, simple_async_get1, - simple_async_get2]}, - {get_next_tests, [], - [simple_sync_get_next1, simple_sync_get_next2, - simple_async_get_next1, simple_async_get_next2]}, - {set_tests, [], - [simple_sync_set1, simple_sync_set2, simple_async_set1, - simple_async_set2]}, - {bulk_tests, [], - [simple_sync_get_bulk1, simple_sync_get_bulk2, - simple_async_get_bulk1, simple_async_get_bulk2]}, - {misc_request_tests, [], [misc_async1, misc_async2]}, - {event_tests, [], - [trap1, trap2, inform1, inform2, inform3, inform4, - inform_swarm, report]}, - {tickets, [], [{group, otp8015}, {group, otp8395}]}, - {otp8015, [], [otp8015_1]}, {otp8395, [], [otp8395_1]}]. + [ + {start_and_stop_tests, [], + [ + simple_start_and_stop1, + simple_start_and_stop2, + simple_start_and_monitor_crash1, + simple_start_and_monitor_crash2, + notify_started01, + notify_started02 + ] + }, + {misc_tests, [], + [ + info + ] + }, + {user_tests, [], + [ + register_user1 + ] + }, + {agent_tests, [], + [ + register_agent1, + register_agent2 + ] + }, + {request_tests, [], + [ + {group, get_tests}, + {group, get_next_tests}, + {group, set_tests}, + {group, bulk_tests}, + {group, misc_request_tests} + ] + }, + {get_tests, [], + [ + simple_sync_get1, + simple_sync_get2, + simple_sync_get3, + simple_async_get1, + simple_async_get2, + simple_async_get3 + ] + }, + {get_next_tests, [], + [ + simple_sync_get_next1, + simple_sync_get_next2, + simple_sync_get_next3, + simple_async_get_next1, + simple_async_get_next2, + simple_async_get_next3 + ] + }, + {set_tests, [], + [ + simple_sync_set1, + simple_sync_set2, + simple_sync_set3, + simple_async_set1, + simple_async_set2, + simple_async_set3 + ] + }, + {bulk_tests, [], + [ + simple_sync_get_bulk1, + simple_sync_get_bulk2, + simple_sync_get_bulk3, + simple_async_get_bulk1, + simple_async_get_bulk2, + simple_async_get_bulk3 + ] + }, + {misc_request_tests, [], + [ + misc_async1, + misc_async2 + ] + }, + {event_tests, [], + [ + trap1, + trap2, + inform1, + inform2, + inform3, + inform4, + inform_swarm, + report + ] + }, + {tickets, [], + [ + {group, otp8015}, + {group, otp8395} + ] + }, + {otp8015, [], + [ + otp8015_1 + ] + }, + {otp8395, [], + [ + otp8395_1 + ] + } + ]. init_per_group(_GroupName, Config) -> Config. @@ -404,21 +512,6 @@ end_per_group(_GroupName, Config) -> Config. - - - - - - - - - - - - - - - %%====================================================================== %% Test functions %%====================================================================== @@ -1319,15 +1412,15 @@ simple_sync_get1(Config) when is_list(Config) -> p("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], - ?line ok = do_simple_get(Node, Addr, Port, Oids1), + ?line ok = do_simple_sync_get(Node, Addr, Port, Oids1), p("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - ?line ok = do_simple_get(Node, Addr, Port, Oids2), + ?line ok = do_simple_sync_get(Node, Addr, Port, Oids2), ok. -do_simple_get(Node, Addr, Port, Oids) -> +do_simple_sync_get(Node, Addr, Port, Oids) -> ?line {ok, Reply, Rem} = mgr_user_sync_get(Node, Addr, Port, Oids), ?DBG("~n Reply: ~p" @@ -1360,14 +1453,22 @@ do_simple_get(Node, Addr, Port, Oids) -> %%====================================================================== -simple_sync_get2(doc) -> ["Simple sync get-request - New style (TargetName)"]; +simple_sync_get2(doc) -> + ["Simple sync get-request - Version 2 API (TargetName)"]; simple_sync_get2(suite) -> []; simple_sync_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg2), - do_simple_get(Config). + do_simple_sync_get2(Config). -do_simple_get(Config) -> +do_simple_sync_get2(Config) -> + Get = fun(Node, TargetName, Oids) -> + mgr_user_sync_get(Node, TargetName, Oids) + end, + PostVerify = fun() -> ok end, + do_simple_sync_get2(Config, Get, PostVerify). + +do_simple_sync_get2(Config, Get, PostVerify) -> p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), @@ -1375,20 +1476,21 @@ do_simple_get(Config) -> p("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], - ?line ok = do_simple_get(Node, TargetName, Oids1), + ?line ok = do_simple_sync_get2(Node, TargetName, Oids1, Get, PostVerify), p("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - ?line ok = do_simple_get(Node, TargetName, Oids2), + ?line ok = do_simple_sync_get2(Node, TargetName, Oids2, Get, PostVerify), ok. - -do_simple_get(Node, TargetName, Oids) -> - ?line {ok, Reply, Rem} = mgr_user_sync_get(Node, TargetName, Oids), + +do_simple_sync_get2(Node, TargetName, Oids, Get, PostVerify) + when is_function(Get, 3) andalso is_function(PostVerify, 0) -> + ?line {ok, Reply, Rem} = Get(Node, TargetName, Oids), ?DBG("~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - + %% verify that the operation actually worked: %% The order should be the same, so no need to seach ?line ok = case Reply of @@ -1403,7 +1505,7 @@ do_simple_get(Node, TargetName, Oids) -> "~n SysDescr: ~s" "~n SysUpTime: ~w", [SysObjectID, SysDescr, SysUpTime]), - ok; + PostVerify(); {noError, 0, Vbs} -> p("unexpected varbinds: ~n~p", [Vbs]), {error, {unexpected_vbs, Vbs}}; @@ -1416,6 +1518,38 @@ do_simple_get(Node, TargetName, Oids) -> %%====================================================================== +simple_sync_get3(doc) -> + ["Simple sync get-request - Version 3 API (TargetName and send-opts)"]; +simple_sync_get3(suite) -> []; +simple_sync_get3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssg3), + do_simple_sync_get3(Config). + +do_simple_sync_get3(Config) -> + Self = self(), + Msg = simple_sync_get3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + Get = fun(Node, TargetName, Oids) -> + mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) + end, + PostVerify = + fun() -> + receive + Msg -> + ok + end + end, + do_simple_sync_get2(Config, Get, PostVerify). + + +%%====================================================================== + simple_async_get1(doc) -> ["Simple (async) get-request - " "Old style (Addr & Port)"]; simple_async_get1(suite) -> []; @@ -1435,40 +1569,41 @@ simple_async_get1(Config) when is_list(Config) -> ?line ok = agent_load_mib(AgentNode, Test2Mib), Exec = fun(Data) -> - async_g_exec1(MgrNode, Addr, Port, Data) + async_g_exec1(MgrNode, Addr, Port, Data) end, - Requests = [ - { 1, - [?sysObjectID_instance], - Exec, - fun(X) -> sag_verify(X, [?sysObjectID_instance]) end}, - { 2, - [?sysDescr_instance, ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysUpTime_instance]) - end}, - { 3, - [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end}, - { 4, - [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end} - ], + Requests = + [ + { 1, + [?sysObjectID_instance], + Exec, + fun(X) -> sag_verify(X, [?sysObjectID_instance]) end }, + { 2, + [?sysDescr_instance, ?sysUpTime_instance], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysUpTime_instance]) + end }, + { 3, + [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]) + end }, + { 4, + [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]) + end } + ], p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), @@ -1516,66 +1651,76 @@ sag_verify_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_async_get2(doc) -> ["Simple (async) get-request - " - "New style (TargetName)"]; +simple_async_get2(doc) -> + ["Simple (async) get-request - Version 2 API (TargetName)"]; simple_async_get2(suite) -> []; simple_async_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, sag2), p("starting with Config: ~p~n", [Config]), - MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), + Get = fun(Oids) -> async_g_exec2(MgrNode, TargetName, Oids) end, + PostVerify = fun(Res) -> Res end, + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). +do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify) -> ?line ok = mgr_user_load_mib(MgrNode, std_mib()), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - - Exec = fun(Data) -> - async_g_exec2(MgrNode, TargetName, Data) - end, - - Requests = [ - { 1, - [?sysObjectID_instance], - Exec, - fun(X) -> sag_verify(X, [?sysObjectID_instance]) end}, - { 2, - [?sysDescr_instance, ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysUpTime_instance]) - end}, - { 3, - [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end}, - { 4, - [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end} - ], + do_simple_async_sync_get2(fun() -> mgr_info(MgrNode) end, + fun() -> agent_info(AgentNode) end, + Get, PostVerify). + +do_simple_async_sync_get2(MgrInfo, AgentInfo, Get, PostVerify) + when is_function(MgrInfo, 0) andalso + is_function(AgentInfo, 0) andalso + is_function(Get, 1) andalso + is_function(PostVerify, 1) -> + Requests = + [ + { 1, + [?sysObjectID_instance], + Get, + fun(X) -> + PostVerify(sag_verify(X, [?sysObjectID_instance])) end}, + { 2, + [?sysDescr_instance, ?sysUpTime_instance], + Get, + fun(X) -> + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysUpTime_instance])) + end}, + { 3, + [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], + Get, + fun(X) -> + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance])) + + end}, + { 4, + [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance], + Get, + fun(X) -> + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance])) + end} + ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + p("manager info when starting test: ~n~p", [MgrInfo()]), + p("agent info when starting test: ~n~p", [AgentInfo()]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + p("manager info when ending test: ~n~p", [MgrInfo()]), + p("agent info when ending test: ~n~p", [AgentInfo()]), ok. @@ -1585,6 +1730,36 @@ async_g_exec2(Node, TargetName, Oids) -> %%====================================================================== +simple_async_get3(doc) -> + ["Simple (async) get-request - Version 3 API (TargetName and send-opts)"]; +simple_async_get3(suite) -> []; +simple_async_get3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sag3), + p("starting with Config: ~p~n", [Config]), + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + Self = self(), + Msg = simple_async_get3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + Get = fun(Oids) -> async_g_exec3(MgrNode, TargetName, Oids, SendOpts) end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). + +async_g_exec3(Node, TargetName, Oids, SendOpts) -> + mgr_user_async_get2(Node, TargetName, Oids, SendOpts). + + +%%====================================================================== + simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - " "Old style (Addr & Port)"]; simple_sync_get_next1(suite) -> []; @@ -1722,23 +1897,33 @@ check_ssgn_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_sync_get_next2(doc) -> ["Simple (sync) get_next-request - " - "New style (TargetName)"]; +simple_sync_get_next2(doc) -> + ["Simple (sync) get_next-request - Version 2 API (TargetName)"]; simple_sync_get_next2(suite) -> []; simple_sync_get_next2(Config) when is_list(Config) -> process_flag(trap_exit, true), - put(tname, ssgn), + put(tname, ssgn2), p("starting with Config: ~p~n", [Config]), - MgrNode = ?config(manager_node, Config), - AgentNode = ?config(agent_node, Config), + GetNext = fun(Node, TargetName, Oids) -> + mgr_user_sync_get_next(Node, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + do_simple_sync_get_next2(Config, GetNext, PostVerify). + +do_simple_sync_get_next2(Config, GetNext, PostVerify) + when is_function(GetNext, 3) andalso is_function(PostVerify, 1) -> + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), %% -- 1 -- Oids01 = [[1,3,7,1]], VF01 = fun(X) -> verify_ssgn_reply1(X, [{[1,3,7,1],endOfMibView}]) end, ?line ok = do_simple_get_next(1, - MgrNode, TargetName, Oids01, VF01), + MgrNode, TargetName, Oids01, VF01, + GetNext, PostVerify), ?line ok = mgr_user_load_mib(MgrNode, std_mib()), @@ -1748,7 +1933,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView]) end, ?line ok = do_simple_get_next(2, - MgrNode, TargetName, Oids02, VF02), + MgrNode, TargetName, Oids02, VF02, + GetNext, PostVerify), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), @@ -1761,7 +1947,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}]) end, ?line ok = do_simple_get_next(3, - MgrNode, TargetName, Oids03, VF03), + MgrNode, TargetName, Oids03, VF03, + GetNext, PostVerify), %% -- 4 -- Oids04 = [[TCnt2, 2]], @@ -1769,7 +1956,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}]) end, ?line ok = do_simple_get_next(4, - MgrNode, TargetName, Oids04, VF04), + MgrNode, TargetName, Oids04, VF04, + GetNext, PostVerify), %% -- 5 -- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1), @@ -1778,7 +1966,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]}) end, ?line ok = do_simple_get_next(5, - MgrNode, TargetName, Oids05, VF05), + MgrNode, TargetName, Oids05, VF05, + GetNext, PostVerify), %% -- 6 -- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2), @@ -1787,7 +1976,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]}) end, ?line ok = do_simple_get_next(6, - MgrNode, TargetName, Oids06, VF06), + MgrNode, TargetName, Oids06, VF06, + GetNext, PostVerify), %% -- 7 -- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3), @@ -1797,7 +1987,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> [?sysDescr, TGenErr3]}) end, ?line ok = do_simple_get_next(7, - MgrNode, TargetName, Oids07, VF07), + MgrNode, TargetName, Oids07, VF07, + GetNext, PostVerify), %% -- 8 -- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig), @@ -1806,18 +1997,19 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {tooBig, 0, []}) end, ?line ok = do_simple_get_next(8, - MgrNode, TargetName, Oids08, VF08), + MgrNode, TargetName, Oids08, VF08, + GetNext, PostVerify), ok. -do_simple_get_next(N, Node, TargetName, Oids, Verify) -> +do_simple_get_next(N, Node, TargetName, Oids, Verify, GetNext, PostVerify) -> p("issue get-next command ~w", [N]), - case mgr_user_sync_get_next(Node, TargetName, Oids) of + case GetNext(Node, TargetName, Oids) of {ok, Reply, Rem} -> ?DBG("get-next ok:" "~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - Verify(Reply); + PostVerify(Verify(Reply)); Error -> {error, {unexpected_reply, Error}} @@ -1826,6 +2018,33 @@ do_simple_get_next(N, Node, TargetName, Oids, Verify) -> %%====================================================================== +simple_sync_get_next3(doc) -> + ["Simple (sync) get_next-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_sync_get_next3(suite) -> []; +simple_sync_get_next3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgn3), + p("starting with Config: ~p~n", [Config]), + Self = self(), + Msg = simple_sync_get_next3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + GetNext = fun(Node, TargetName, Oids) -> + mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, + do_simple_sync_get_next2(Config, GetNext, PostVerify). + + +%%====================================================================== + simple_async_get_next1(doc) -> ["Simple (async) get_next-request - " "Old style (Addr & Port)"]; simple_async_get_next1(suite) -> []; @@ -1923,8 +2142,8 @@ async_gn_exec1(Node, Addr, Port, Oids) -> %%====================================================================== -simple_async_get_next2(doc) -> ["Simple (async) get_next-request - " - "New style (TargetName)"]; +simple_async_get_next2(doc) -> + ["Simple (async) get_next-request - Version 2 API (TargetName)"]; simple_async_get_next2(suite) -> []; simple_async_get_next2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -1939,11 +2158,14 @@ simple_async_get_next2(Config) when is_list(Config) -> Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - - Exec = fun(X) -> - async_gn_exec2(MgrNode, TargetName, X) - end, - + GetNext = fun(Oids) -> + async_gn_exec2(MgrNode, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). + +do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify) + when is_function(GetNext, 1) andalso is_function(PostVerify, 1) -> ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1), ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2), @@ -1954,51 +2176,60 @@ simple_async_get_next2(Config) when is_list(Config) -> [ {1, [[1,3,7,1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}]) + PostVerify( + verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}])) + end}, {2, [[sysDescr], [1,3,7,1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView]) + PostVerify( + verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])) end}, {3, [[TCnt2, 1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}]) + PostVerify( + verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])) end}, {4, [[TCnt2, 2]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}]) + PostVerify( + verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])) end}, {5, [TGenErr1], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})) end}, {6, [TGenErr2], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})) end}, {7, [[sysDescr], TGenErr3], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})) end}, {8, [TTooBig], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {tooBig, 0, []}) + PostVerify( + verify_ssgn_reply2(X, {tooBig, 0, []})) end} ], @@ -2019,6 +2250,48 @@ async_gn_exec2(Node, TargetName, Oids) -> %%====================================================================== +simple_async_get_next3(doc) -> + ["Simple (async) get_next-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_async_get_next3(suite) -> []; +simple_async_get_next3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgn2), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_get_next3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetNext = fun(Oids) -> + async_gn_exec3(MgrNode, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, + + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). + +async_gn_exec3(Node, TargetName, Oids, SendOpts) -> + mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts). + + +%%====================================================================== + simple_sync_set1(doc) -> ["Simple (sync) set-request - " "Old style (Addr & Port)"]; simple_sync_set1(suite) -> []; @@ -2088,14 +2361,25 @@ value_of_vavs([{_Oid, Val}|VAVs], Acc) -> %%====================================================================== -simple_sync_set2(doc) -> ["Simple (sync) set-request - New style (TargetName)"]; +simple_sync_set2(doc) -> + ["Simple (sync) set-request - Version 2 API (TargetName)"]; simple_sync_set2(suite) -> []; simple_sync_set2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, sss2), p("starting with Config: ~p~n", [Config]), - Node = ?config(manager_node, Config), + Set = fun(Node, TargetName, VAVs) -> + mgr_user_sync_set(Node, TargetName, VAVs) + end, + PostVerify = fun() -> ok end, + + do_simple_sync_set2(Config, Set, PostVerify). + +do_simple_sync_set2(Config, Set, PostVerify) + when is_function(Set, 3) andalso is_function(PostVerify, 0) -> + + Node = ?config(manager_node, Config), TargetName = ?config(manager_agent_target_name, Config), p("issue set-request without loading the mib"), @@ -2105,7 +2389,7 @@ simple_sync_set2(Config) when is_list(Config) -> {?sysName_instance, s, Val11}, {?sysLocation_instance, s, Val12} ], - ?line ok = do_simple_set2(Node, TargetName, VAVs1), + ?line ok = do_simple_set2(Node, TargetName, VAVs1, Set, PostVerify), p("issue set-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), @@ -2115,12 +2399,12 @@ simple_sync_set2(Config) when is_list(Config) -> {[sysName, 0], Val21}, {[sysLocation, 0], Val22} ], - ?line ok = do_simple_set2(Node, TargetName, VAVs2), + ?line ok = do_simple_set2(Node, TargetName, VAVs2, Set, PostVerify), ok. -do_simple_set2(Node, TargetName, VAVs) -> +do_simple_set2(Node, TargetName, VAVs, Set, PostVerify) -> [SysName, SysLoc] = value_of_vavs(VAVs), - ?line {ok, Reply, Rem} = mgr_user_sync_set(Node, TargetName, VAVs), + ?line {ok, Reply, Rem} = Set(Node, TargetName, VAVs), ?DBG("~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), @@ -2133,7 +2417,7 @@ do_simple_set2(Node, TargetName, VAVs) -> value = SysName}, #varbind{oid = ?sysLocation_instance, value = SysLoc}]} -> - ok; + PostVerify(); {noError, 0, Vbs} -> {error, {unexpected_vbs, Vbs}}; Else -> @@ -2145,6 +2429,33 @@ do_simple_set2(Node, TargetName, VAVs) -> %%====================================================================== +simple_sync_set3(doc) -> + ["Simple (sync) set-request - Version 3 API (TargetName with send-opts)"]; +simple_sync_set3(suite) -> []; +simple_sync_set3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sss3), + p("starting with Config: ~p~n", [Config]), + + Self = self(), + Msg = simple_sync_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + Set = fun(Node, TargetName, VAVs) -> + mgr_user_sync_set2(Node, TargetName, VAVs, SendOpts) + end, + PostVerify = fun() -> receive Msg -> ok end end, + + do_simple_sync_set2(Config, Set, PostVerify). + + +%%====================================================================== + simple_async_set1(doc) -> ["Simple (async) set-request - " "Old style (Addr & Port)"]; simple_async_set1(suite) -> []; @@ -2237,8 +2548,8 @@ sas_verify_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_async_set2(doc) -> ["Simple (async) set-request - " - "New style (TargetName)"]; +simple_async_set2(doc) -> + ["Simple (async) set-request - Version 2 API (TargetName)"]; simple_async_set2(suite) -> []; simple_async_set2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2254,31 +2565,40 @@ simple_async_set2(Config) when is_list(Config) -> ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - Exec = fun(X) -> - async_s_exec2(MgrNode, TargetName, X) - end, + Set = + fun(Oids) -> + async_s_exec2(MgrNode, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). +do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify) -> Requests = [ {1, [{?sysName_instance, s, "Arne Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysName_instance]) + PostVerify(sas_verify(X, [?sysName_instance])) end}, {2, [{?sysLocation_instance, s, "Stockholm"}, {?sysName_instance, s, "Arne Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysLocation_instance, ?sysName_instance]) + PostVerify(sas_verify(X, + [?sysLocation_instance, + ?sysName_instance])) end}, {3, [{[sysName, 0], "Gothenburg"}, {[sysLocation, 0], "Sune Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysName_instance, ?sysLocation_instance]) + PostVerify(sas_verify(X, + [?sysName_instance, + ?sysLocation_instance])) end} ], @@ -2299,6 +2619,48 @@ async_s_exec2(Node, TargetName, VAVs) -> %%====================================================================== +simple_async_set3(doc) -> + ["Simple (async) set-request - Version 3 API (TargetName with send-opts)"]; +simple_async_set3(suite) -> []; +simple_async_set3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sas3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + Set = + fun(Oids) -> + async_s_exec3(MgrNode, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). + +async_s_exec3(Node, TargetName, VAVs, SendOpts) -> + mgr_user_async_set2(Node, TargetName, VAVs, SendOpts). + + +%%====================================================================== + simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - " "Old style (Addr & Port)"]; simple_sync_get_bulk1(suite) -> []; @@ -2470,8 +2832,8 @@ check_ssgb_vbs([R|_], [E|_]) -> %%====================================================================== -simple_sync_get_bulk2(doc) -> ["Simple (sync) get_bulk-request - " - "New style (TargetName)"]; +simple_sync_get_bulk2(doc) -> + ["Simple (sync) get_bulk-request - Version 2 API (TargetName)"]; simple_sync_get_bulk2(suite) -> []; simple_sync_get_bulk2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2482,20 +2844,33 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), + GetBulk = + fun(NonRep, MaxRep, Oids) -> + mgr_user_sync_get_bulk(MgrNode, TargetName, + NonRep, MaxRep, Oids) + end, + PostVerify = fun(Res) -> Res end, + + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + +do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify) -> %% -- 1 -- ?line ok = do_simple_get_bulk2(1, - MgrNode, TargetName, 1, 1, [], - fun verify_ssgb_reply1/1), + 1, 1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 2 -- ?line ok = do_simple_get_bulk2(2, - MgrNode, TargetName, -1, 1, [], - fun verify_ssgb_reply1/1), + -1, 1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 3 -- ?line ok = do_simple_get_bulk2(3, - MgrNode, TargetName, -1, -1, [], - fun verify_ssgb_reply1/1), + -1, -1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), ?line ok = mgr_user_load_mib(MgrNode, std_mib()), %% -- 4 -- @@ -2503,13 +2878,13 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(4, - MgrNode, TargetName, - 2, 0, [[sysDescr],[1,3,7,1]], VF04), + 2, 0, [[sysDescr],[1,3,7,1]], VF04, + GetBulk, PostVerify), %% -- 5 -- ?line ok = do_simple_get_bulk2(5, - MgrNode, TargetName, - 1, 2, [[sysDescr],[1,3,7,1]], VF04), + 1, 2, [[sysDescr],[1,3,7,1]], VF04, + GetBulk, PostVerify), %% -- 6 -- VF06 = fun(X) -> @@ -2518,8 +2893,8 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysObjectID_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(6, - MgrNode, TargetName, - 0, 2, [[sysDescr],[1,3,7,1]], VF06), + 0, 2, [[sysDescr],[1,3,7,1]], VF06, + GetBulk, PostVerify), %% -- 7 -- VF07 = fun(X) -> @@ -2529,10 +2904,10 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysObjectID_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(7, - MgrNode, TargetName, 2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]], - VF07), + VF07, + GetBulk, PostVerify), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), @@ -2545,17 +2920,17 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysDescr_instance]) end, ?line ok = do_simple_get_bulk2(8, - MgrNode, TargetName, 1, 2, [[sysDescr],[sysDescr],[tTooBig]], - VF08), + VF08, + GetBulk, PostVerify), %% -- 9 -- ?line ok = do_simple_get_bulk2(9, - MgrNode, TargetName, 1, 12, [[tDescr2], [sysDescr]], - fun verify_ssgb_reply1/1), + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 10 -- VF10 = fun(X) -> @@ -2566,13 +2941,13 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> {?sysDescr, 'NULL'}]) end, ?line ok = do_simple_get_bulk2(10, - MgrNode, TargetName, 2, 2, [[sysDescr], [sysObjectID], [tGenErr1], [sysDescr]], - VF10), + VF10, + GetBulk, PostVerify), %% -- 11 -- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), @@ -2583,20 +2958,25 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> {fl([TCnt2,2]), endOfMibView}]) end, ?line ok = do_simple_get_bulk2(11, - MgrNode, TargetName, 0, 2, - [[TCnt2, 1]], VF11), + [[TCnt2, 1]], VF11, + GetBulk, PostVerify), ok. -do_simple_get_bulk2(N, Node, TargetName, NonRep, MaxRep, Oids, Verify) -> +do_simple_get_bulk2(N, + NonRep, MaxRep, Oids, + Verify, GetBulk, PostVerify) + when is_function(Verify, 1) andalso + is_function(GetBulk, 3) andalso + is_function(PostVerify) -> p("issue get-bulk command ~w", [N]), - case mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids) of + case GetBulk(NonRep, MaxRep, Oids) of {ok, Reply, Rem} -> ?DBG("get-bulk ok:" "~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - Verify(Reply); + PostVerify(Verify(Reply)); Error -> {error, {unexpected_reply, Error}} @@ -2605,6 +2985,42 @@ do_simple_get_bulk2(N, Node, TargetName, NonRep, MaxRep, Oids, Verify) -> %%====================================================================== +simple_sync_get_bulk3(doc) -> + ["Simple (sync) get_bulk-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_sync_get_bulk3(suite) -> []; +simple_sync_get_bulk3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgb3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + Self = self(), + Msg = simple_async_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetBulk = + fun(NonRep, MaxRep, Oids) -> + mgr_user_sync_get_bulk2(MgrNode, TargetName, + NonRep, MaxRep, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + + +%%====================================================================== + simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - " "Old style (Addr & Port)"]; simple_async_get_bulk1(suite) -> []; @@ -2748,8 +3164,8 @@ async_gb_exec1(Node, Addr, Port, {NR, MR, Oids}) -> %%====================================================================== -simple_async_get_bulk2(doc) -> ["Simple (async) get_bulk-request - " - "New style (TargetName)"]; +simple_async_get_bulk2(doc) -> + ["Simple (async) get_bulk-request - Version 2 API (TargetName)"]; simple_async_get_bulk2(suite) -> []; simple_async_get_bulk2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2765,111 +3181,122 @@ simple_async_get_bulk2(Config) when is_list(Config) -> ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - Exec = fun(Data) -> - async_gb_exec2(MgrNode, TargetName, Data) - end, + GetBulk = + fun(Data) -> + async_gb_exec2(MgrNode, TargetName, Data) + end, + PostVerify = fun(Res) -> Res end, + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). + +do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify) -> %% We re-use the verification functions from the ssgb test-case VF04 = fun(X) -> - verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])) end, VF06 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, endOfMibView, - ?sysObjectID_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, endOfMibView, + ?sysObjectID_instance, endOfMibView])) end, VF07 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, endOfMibView, - ?sysDescr_instance, endOfMibView, - ?sysObjectID_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, endOfMibView, + ?sysDescr_instance, endOfMibView, + ?sysObjectID_instance, endOfMibView])) end, VF08 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, - ?sysDescr_instance]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, + ?sysDescr_instance])) end, VF10 = fun(X) -> - verify_ssgb_reply3(X, - [{?sysDescr, 'NULL'}, - {?sysObjectID, 'NULL'}, - {?tGenErr1, 'NULL'}, - {?sysDescr, 'NULL'}]) + PostVerify( + verify_ssgb_reply3(X, + [{?sysDescr, 'NULL'}, + {?sysObjectID, 'NULL'}, + {?tGenErr1, 'NULL'}, + {?sysDescr, 'NULL'}])) end, ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), VF11 = fun(X) -> - verify_ssgb_reply2(X, - [{fl([TCnt2,2]), 100}, - {fl([TCnt2,2]), endOfMibView}]) + PostVerify( + verify_ssgb_reply2(X, + [{fl([TCnt2,2]), 100}, + {fl([TCnt2,2]), endOfMibView}])) end, Requests = [ { 1, {1, 1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 2, {-1, 1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 3, {-1, -1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 4, {2, 0, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, { 5, {1, 2, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, { 6, {0, 2, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF06}, { 7, {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF07}, { 8, {1, 2, [[sysDescr],[sysDescr],[tTooBig]]}, - Exec, + GetBulk, VF08}, { 9, {1, 12, [[tDescr2], [sysDescr]]}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, {10, {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10}, {11, {0, 2, [[TCnt2, 1]]}, - Exec, + GetBulk, VF11}, {12, {2, 0, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, {13, {1, 12, [[tDescr2], [sysDescr]]}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, {14, {2, 2, [[sysDescr],[sysObjectID],[tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10}, {15, {0, 2, [[TCnt2, 1]]}, - Exec, + GetBulk, VF11}, {16, {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF07}, {17, {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10} ], @@ -2890,6 +3317,49 @@ async_gb_exec2(Node, TargetName, {NR, MR, Oids}) -> %%====================================================================== +simple_async_get_bulk3(doc) -> + ["Simple (async) get_bulk-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_async_get_bulk3(suite) -> []; +simple_async_get_bulk3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sagb3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_get_bulk3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetBulk = + fun(Data) -> + async_gb_exec3(MgrNode, TargetName, Data, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). + +async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) -> + mgr_user_async_get_bulk2(Node, TargetName, NR, MR, Oids, SendOpts). + + +%%====================================================================== + misc_async1(doc) -> ["Misc (async) request(s) - " "Old style (Addr & Port)"]; misc_async1(suite) -> []; @@ -3079,8 +3549,8 @@ misc_async1(Config) when is_list(Config) -> %%====================================================================== -misc_async2(doc) -> ["Misc (async) request(s) - " - "New style (TargetName)"]; +misc_async2(doc) -> + ["Misc (async) request(s) - Version 2 API (TargetName)"]; misc_async2(suite) -> []; misc_async2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -4440,7 +4910,7 @@ otp8395_1(suite) -> []; otp8395_1(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, otp8395_1), - do_simple_get(Config). + do_simple_sync_get2(Config). %%====================================================================== @@ -4986,6 +5456,9 @@ mgr_user_sync_get(Node, Addr_or_TargetName, Oids) -> mgr_user_sync_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]). +mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]). + %% mgr_user_async_get(Node, Oids) -> %% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_async_get(Node, Addr_or_TargetName, Oids) -> @@ -4993,6 +5466,9 @@ mgr_user_async_get(Node, Addr_or_TargetName, Oids) -> mgr_user_async_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]). +mgr_user_async_get2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get2, [TargetName, Oids, SendOpts]). + %% mgr_user_sync_get_next(Node, Oids) -> %% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) -> @@ -5000,6 +5476,9 @@ mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) -> mgr_user_sync_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]). +mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get_next2, [TargetName, Oids, SendOpts]). + %% mgr_user_async_get_next(Node, Oids) -> %% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) -> @@ -5007,6 +5486,9 @@ mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) -> mgr_user_async_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]). +mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get_next2, [TargetName, Oids, SendOpts]). + %% mgr_user_sync_set(Node, VAV) -> %% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). mgr_user_sync_set(Node, Addr_or_TargetName, VAV) -> @@ -5014,6 +5496,9 @@ mgr_user_sync_set(Node, Addr_or_TargetName, VAV) -> mgr_user_sync_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]). +mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) -> + rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]). + %% mgr_user_async_set(Node, VAV) -> %% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). mgr_user_async_set(Node, Addr_or_TargetName, VAV) -> @@ -5021,26 +5506,37 @@ mgr_user_async_set(Node, Addr_or_TargetName, VAV) -> mgr_user_async_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]). +mgr_user_async_set2(Node, TargetName, VAV, SendOpts) -> + rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]). + %% mgr_user_sync_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_sync_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). mgr_user_sync_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, sync_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [Addr_or_TargetName, NonRep, MaxRep, Oids]). mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, sync_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get_bulk2, + [TargetName, NonRep, MaxRep, Oids, SendOpts]). + %% mgr_user_async_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_async_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). mgr_user_async_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, async_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [Addr_or_TargetName, NonRep, MaxRep, Oids]). mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, async_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get_bulk2, + [TargetName, NonRep, MaxRep, Oids, SendOpts]). + mgr_user_purify_oid(Node, Oid) -> rcall(Node, snmp_manager_user, purify_oid, [Oid]). diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index b0e192344d..30b5dd1fc7 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -50,14 +50,14 @@ update_agent_info/3, update_agent_info/4, which_all_agents/0, which_own_agents/0, load_mib/1, unload_mib/1, - sync_get/1, sync_get/2, sync_get/3, - async_get/1, async_get/2, async_get/3, - sync_get_next/1, sync_get_next/2, sync_get_next/3, - async_get_next/1, async_get_next/2, async_get_next/3, - sync_set/1, sync_set/2, sync_set/3, - async_set/1, async_set/2, async_set/3, - sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, - async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, + sync_get/1, sync_get/2, sync_get/3, sync_get2/3, + async_get/1, async_get/2, async_get/3, async_get2/3, + sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, + async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, + sync_set/1, sync_set/2, sync_set/3, sync_set2/3, + async_set/1, async_set/2, async_set/3, async_set2/3, + sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, sync_get_bulk2/5, + async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, async_get_bulk2/5, name_to_oid/1, oid_to_name/1, purify_oid/1 ]). @@ -171,6 +171,10 @@ sync_get(Addr_or_TargetName, Oids) -> sync_get(Addr, Port, Oids) -> call({sync_get, Addr, Port, Oids}). +sync_get2(TargetName, Oids, SendOpts) -> + call({sync_get2, TargetName, Oids, SendOpts}). + + %% -- async_get(Oids) -> @@ -182,6 +186,9 @@ async_get(Addr_or_TargetName, Oids) -> async_get(Addr, Port, Oids) -> call({async_get, Addr, Port, Oids}). +async_get2(TargetName, Oids, SendOpts) -> + call({async_get2, TargetName, Oids, SendOpts}). + %% -- sync_get_next(Oids) -> @@ -193,6 +200,9 @@ sync_get_next(Addr_or_TargetName, Oids) -> sync_get_next(Addr, Port, Oids) -> call({sync_get_next, Addr, Port, Oids}). +sync_get_next2(TargetName, Oids, SendOpts) -> + call({sync_get_next2, TargetName, Oids, SendOpts}). + %% -- async_get_next(Oids) -> @@ -204,6 +214,9 @@ async_get_next(Addr_or_TargetName, Oids) -> async_get_next(Addr, Port, Oids) -> call({async_get_next, Addr, Port, Oids}). +async_get_next2(TargetName, Oids, SendOpts) -> + call({async_get_next2, TargetName, Oids, SendOpts}). + %% -- sync_set(VAV) -> @@ -215,6 +228,9 @@ sync_set(Addr_or_TargetName, VAV) -> sync_set(Addr, Port, VAV) -> call({sync_set, Addr, Port, VAV}). +sync_set2(TargetName, VAV, SendOpts) -> + call({sync_set2, TargetName, VAV, SendOpts}). + %% -- async_set(VAV) -> @@ -226,6 +242,9 @@ async_set(Addr_or_TargetName, VAV) -> async_set(Addr, Port, VAV) -> call({async_set, Addr, Port, VAV}). +async_set2(TargetName, VAV, SendOpts) -> + call({async_set2, TargetName, VAV, SendOpts}). + %% -- sync_get_bulk(NonRep, MaxRep, Oids) -> @@ -237,6 +256,9 @@ sync_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> call({sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). + %% -- async_get_bulk(NonRep, MaxRep, Oids) -> @@ -248,6 +270,9 @@ async_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> call({async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +async_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). + %% -- name_to_oid(Name) -> @@ -400,6 +425,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get-request -- %% + {{sync_get2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get2 request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:sync_get2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get, Oids}, From, Ref} -> d("loop -> received sync_get request " @@ -439,6 +474,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get-request -- %% + {{async_get2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_get2 request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:async_get2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get, Oids}, From, Ref} -> d("loop -> received async_get request"), @@ -472,6 +517,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get_next-request -- %% + {{sync_get_next2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get_next2 request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:sync_get_next2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get_next, Oids}, From, Ref} -> d("loop -> received sync_get_next request"), @@ -505,6 +560,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get_next-request -- %% + {{async_get_next2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_get_next2 request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:async_get_next2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get_next, Oids}, From, Ref} -> d("loop -> received async_get_next request"), @@ -538,6 +603,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) set-request -- %% + {{sync_set2, TargetName, VAV, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_set2 request with" + "~n TargetName: ~p" + "~n VAV: ~p" + "~n SendOpts: ~p", [TargetName, VAV, SendOpts]), + Res = snmpm:sync_set2(Id, TargetName, VAV, SendOpts), + reply(From, Res, Ref), + loop(S); + {{sync_set, VAV}, From, Ref} -> d("loop -> received sync_set request"), Res = [snmpm:sync_set(Id, TargetName, VAV) || @@ -568,6 +643,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) set-request -- %% + {{async_set2, TargetName, VAV, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_set2 request with" + "~n TargetName: ~p" + "~n VAV: ~p" + "~n SendOpts: ~p", [TargetName, VAV, SendOpts]), + Res = snmpm:async_set2(Id, TargetName, VAV, SendOpts), + reply(From, Res, Ref), + loop(S); + {{async_set, VAV}, From, Ref} -> d("loop -> received async_set request"), Res = [snmpm:async_set(Id, TargetName, VAV) || @@ -598,6 +683,20 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get-bulk-request -- %% + {{sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get_bulk request with" + "~n TargetName: ~p" + "~n NonRep: ~w" + "~n MaxRep: ~w" + "~n Oids: ~p" + "~n SendOpts: ~p", + [TargetName, NonRep, MaxRep, Oids, SendOpts]), + Res = snmpm:sync_get_bulk2(Id, TargetName, + NonRep, MaxRep, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get_bulk, NonRep, MaxRep, Oids}, From, Ref} -> d("loop -> received sync_get_bulk request with" @@ -645,6 +744,20 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get-bulk-request -- %% + {{async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}, + From, Ref} when is_list(TargetName) -> + d("loop -> received async_get_bulk2 request with" + "~n TargetName: ~p" + "~n NonRep: ~w" + "~n MaxRep: ~w" + "~n Oids: ~p" + "~n SendOpts: ~p", + [TargetName, NonRep, MaxRep, Oids, SendOpts]), + Res = snmpm:async_get_bulk2(Id, TargetName, + NonRep, MaxRep, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get_bulk, NonRep, MaxRep, Oids}, From, Ref} -> d("loop -> received async_get_bulk request with" @@ -847,7 +960,11 @@ call(Req, To) when is_integer(To) -> {error, timeout} end. -reply(Pid, Reply, Ref) -> +reply(Pid, Reply, Ref) -> + d("reply -> entry with" + "~n Pid: ~p" + "~n Reply: ~p" + "~n Ref: ~p", [Pid, Reply, Ref]), Pid ! {Reply, Ref}. cast(Msg) -> diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl index ef510ad62e..197797c816 100644 --- a/lib/snmp/test/snmp_pdus_test.erl +++ b/lib/snmp/test/snmp_pdus_test.erl @@ -38,6 +38,8 @@ otp7575/1, otp8563/1, + otp9022/1, + init_per_testcase/2, end_per_testcase/2 ]). @@ -75,7 +77,7 @@ all() -> [{group, tickets}]. groups() -> - [{tickets, [], [otp7575, otp8563]}]. + [{tickets, [], [otp7575, otp8563, otp9022]}]. init_per_group(_GroupName, Config) -> Config. @@ -169,7 +171,56 @@ otp8563(Config) when is_list(Config) -> Unexpected7 -> exit({unexpected_encode_result, Unexpected7, Val7}) end, - + + ok. + + +otp9022(suite) -> []; +otp9022(doc) -> ["OTP-9022"]; +otp9022(Config) when is_list(Config) -> + Val1 = 16#7fffffff, + io:format("try encode and decode ~w~n", [Val1]), + Enc1 = snmp_pdus:enc_value('Counter32', Val1), + {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), + + Val2 = Val1 + 1, + io:format("try encode and decode ~w~n", [Val2]), + Enc2 = snmp_pdus:enc_value('Counter32', Val2), + {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), + + Val3 = Val2 + 1, + io:format("try encode and decode ~w~n", [Val3]), + Enc3 = snmp_pdus:enc_value('Counter32', Val3), + {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), + + Val4 = 16#fffffffe, + io:format("try encode and decode ~w~n", [Val4]), + Enc4 = snmp_pdus:enc_value('Counter32', Val4), + {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), + + Val5 = Val4 + 1, + io:format("try encode and decode ~w~n", [Val5]), + Enc5 = snmp_pdus:enc_value('Counter32', Val5), + {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), + + Val6 = 16#ffffffff + 1, + io:format("try and fail to encode ~w~n", [Val6]), + case (catch snmp_pdus:enc_value('Counter32', Val6)) of + {'EXIT', {error, {bad_counter32, Val6}}} -> + ok; + Unexpected6 -> + exit({unexpected_encode_result, Unexpected6, Val6}) + end, + + Val7 = -1, + io:format("try and fail to encode ~w~n", [Val7]), + case (catch snmp_pdus:enc_value('Counter32', Val7)) of + {'EXIT', {error, {bad_counter32, Val7}}} -> + ok; + Unexpected7 -> + exit({unexpected_encode_result, Unexpected7, Val7}) + end, + ok. diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index e70c97dcb8..29228fc59b 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -17,6 +17,6 @@ # # %CopyrightEnd% -SNMP_VSN = 4.19 +SNMP_VSN = 4.20 PRE_VSN = APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 52ee9c086a..b2d17925fd 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -28,59 +28,47 @@ <rev>G</rev> <file>notes.xml</file> </header> - <p>This document describes the changes made to the SSL application. - </p> - - <section><title>SSL 4.1.4</title> - + <p>This document describes the changes made to the SSL application.</p> + + <section> + <title>SSL 4.1.5</title> + <section><title>Improvements and New Features</title> - <list> - <item> - <p> - Reduced memory footprint of an ssl connection.</p> - <p> - Handshake hashes, premaster secret and "public_key_info" - does not need to be saved when the connection has been - established. The own certificate is no longer duplicated - in the state.</p> - <p> - Own Id: OTP-9021</p> - </item> - <item> - <p> - Add the option {hibernate_after, int()} to ssl:connect - and ssl:listen</p> - <p> - Own Id: OTP-9106</p> - </item> - </list> + <list> + <item> + <p>Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in + an exit with reason badarg. Neither SSL nor INETS This was not + catched, resulting in crashes with incomprehensible reasons.</p> + <p>Own Id: OTP-9289 Aux Id: seq11845</p> + </item> + </list> </section> - -</section> - -<section><title>SSL 4.1.3</title> - + + </section> + + <section> + <title>SSL 4.1.3</title> + <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p> - Fixed error in cache-handling fix from ssl-4.1.2</p> - <p> - Own Id: OTP-9018 Aux Id: seq11739 </p> - </item> - <item> - <p> - Verification of a critical extended_key_usage-extension - corrected</p> - <p> - Own Id: OTP-9029 Aux Id: seq11541 </p> - </item> - </list> + <list> + <item> + <p> + Fixed error in cache-handling fix from ssl-4.1.2</p> + <p> + Own Id: OTP-9018 Aux Id: seq11739 </p> + </item> + <item> + <p>Verification of a critical extended_key_usage-extension + corrected</p> + <p>Own Id: OTP-9029 Aux Id: seq11541 </p> + </item> + </list> </section> -</section> + </section> -<section><title>SSL 4.1.2</title> + <section> + <title>SSL 4.1.2</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index a0ecb4ac6f..cf8867245b 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -5,7 +5,7 @@ {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ @@ -13,7 +13,7 @@ {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 380c59b058..0ced6707eb 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -611,8 +611,10 @@ do_new_connect(Address, Port, catch exit:{function_clause, _} -> {error, {eoptions, {cb_info, CbInfo}}}; + exit:badarg -> + {error, {eoptions, {inet_options, UserOpts}}}; exit:{badarg, _} -> - {error,{eoptions, {inet_options, UserOpts}}} + {error, {eoptions, {inet_options, UserOpts}}} end. old_connect(Address, Port, Options, Timeout) -> diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index f845b1ecc0..5a2d0c9496 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,6 +57,7 @@ -define('24H_in_sec', 8640). -define(SESSION_VALIDATION_INTERVAL, 60000). -define(CERTIFICATE_CACHE_CLEANUP, 30000). +-define(CLEAN_SESSION_DB, 60000). %%==================================================================== %% API @@ -70,7 +71,8 @@ start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- --spec connection_init(string()| {der, list()}, client | server) -> {ok, reference(), cache_ref()}. +-spec connection_init(string()| {der, list()}, client | server) -> + {ok, reference(), cache_ref()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- @@ -101,7 +103,9 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- -spec issuer_candidate(cert_key() | no_candidate) -> - {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates. + {cert_key(), + {der_cert(), + #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: Return next issuer candidate. %%-------------------------------------------------------------------- @@ -117,7 +121,8 @@ client_session_id(Host, Port, SslOpts, OwnCert) -> call({client_session_id, Host, Port, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id(). +-spec server_session_id(host(), port_num(), #ssl_options{}, + der_cert()) -> session_id(). %% %% Description: Select a session id for the server. %%-------------------------------------------------------------------- @@ -139,7 +144,9 @@ register_session(Port, Session) -> -spec invalidate_session(port_num(), #session{}) -> ok. -spec invalidate_session(host(), port_num(), #session{}) -> ok. %% -%% Description: Make the session unavilable for reuse. +%% Description: Make the session unavailable for reuse. After +%% a the session has been marked "is_resumable = false" for some while +%% it will be safe to remove the data from the session database. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -259,23 +266,26 @@ handle_cast({register_session, Port, Session}, {noreply, State}; handle_cast({invalidate_session, Host, Port, - #session{session_id = ID}}, + #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - CacheCb:delete(Cache, {{Host, Port}, ID}), + CacheCb:update(Cache, {{Host, Port}, ID}, Session#session{is_resumable = false}), + timer:apply_after(?CLEAN_SESSION_DB, CacheCb, delete, {{Host, Port}, ID}), {noreply, State}; -handle_cast({invalidate_session, Port, #session{session_id = ID}}, +handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - CacheCb:delete(Cache, {Port, ID}), + CacheCb:update(Cache, {Port, ID}, Session#session{is_resumable = false}), + timer:apply_after(?CLEAN_SESSION_DB, CacheCb, delete, {Port, ID}), {noreply, State}; handle_cast({recache_pem, File, LastWrite, Pid, From}, #state{certificate_db = [_, FileToRefDb, _]} = State0) -> case ssl_certificate_db:lookup(File, FileToRefDb) of undefined -> - {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), + {reply, Msg, State} = + handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), gen_server:reply(From, Msg), {noreply, State}; _ -> %% Send message to self letting cleanup messages be handled diff --git a/lib/stdlib/doc/specs/.gitignore b/lib/stdlib/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/stdlib/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index b558697d63..16e0a86e3b 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -125,18 +125,24 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include -I../../../kernel/include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -155,8 +161,13 @@ clean clean_docs: rm -f $(MAN3DIR)/* rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_erl_id_trans.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_id_trans + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml index 5c3ac6a2a9..a79fcd487e 100644 --- a/lib/stdlib/doc/src/array.xml +++ b/lib/stdlib/doc/src/array.xml @@ -3,7 +3,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2009</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -82,19 +82,35 @@ the default value cannot be confused with the values of set entries.</p> %% allow accesses beyond the last set entry {'EXIT',{badarg,_}} = (catch array:set(18, true, A3)). {'EXIT',{badarg,_}} = (catch array:get(18, A3)).</pre></description> -<section><title>DATA TYPES</title><marker id="types"/> - -<taglist> -<tag><c>array()</c></tag> -<item><marker id="type-array"/> -<p>A functional, extendible array. The representation is - not documented and is subject to change without notice. Note that - arrays cannot be directly compared for equality.</p> -</item> -</taglist></section> +<datatypes> + <datatype> + <name><marker id="type-array">array()</marker></name> + <desc> + <p>A functional, extendible array. The representation is + not documented and is subject to change without notice. Note that + arrays cannot be directly compared for equality.</p> + </desc> + </datatype> + <datatype> + <name name="array_indx"/> + </datatype> + <datatype> + <name name="array_opts"/> + </datatype> + <datatype> + <name name="array_opt"/> + </datatype> + <datatype> + <name name="indx_pairs"/> + </datatype> + <datatype> + <name name="indx_pair"/> + </datatype> +</datatypes> + <funcs> <func> -<name>default(Array::array()) -> term()</name> +<name name="default" arity="1"/> <fsummary>Get the value used for uninitialized entries.</fsummary> <desc><marker id="default-1"/> @@ -104,7 +120,7 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>.</p> </desc></func> <func> -<name>fix(Array::array()) -> array()</name> +<name name="fix" arity="1"/> <fsummary>Fix the size of the array.</fsummary> <desc><marker id="fix-1"/> @@ -114,105 +130,100 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#relax-1">relax/1</seealso>.</p> </desc></func> <func> -<name>foldl(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="foldl" arity="3"/> <fsummary>Fold the elements of the array using the given function and initial accumulator value.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="foldl-3"/> - <p>Fold the elements of the array using the given function and initial accumulator value. The elements are visited in order from the - lowest index to the highest. If <c>Function</c> is not a function, the + lowest index to the highest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#map-2">map/2</seealso>, <seealso marker="#sparse_foldl-3">sparse_foldl/3</seealso>.</p> </desc></func> <func> -<name>foldr(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="foldr" arity="3"/> <fsummary>Fold the elements of the array right-to-left using the given function and initial accumulator value.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="foldr-3"/> <p>Fold the elements of the array right-to-left using the given function and initial accumulator value. The elements are visited in - order from the highest index to the lowest. If <c>Function</c> is not a + order from the highest index to the lowest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#map-2">map/2</seealso>.</p> </desc></func> <func> -<name>from_list(List::list()) -> array()</name> +<name name="from_list" arity="1"/> <fsummary>Equivalent to from_list(List, undefined). </fsummary> <desc><marker id="from_list-1"/> -<p>Equivalent to <seealso marker="#from_list-2">from_list(List, undefined)</seealso>.</p> +<p>Equivalent to <seealso marker="#from_list-2">from_list(<anno>List</anno>, undefined)</seealso>.</p> </desc></func> <func> -<name>from_list(List::list(), Default::term()) -> array()</name> +<name name="from_list" arity="2"/> <fsummary>Convert a list to an extendible array.</fsummary> <desc><marker id="from_list-2"/> -<p>Convert a list to an extendible array. <c>Default</c> is used as the value - for uninitialized entries of the array. If <c>List</c> is not a proper list, +<p>Convert a list to an extendible array. <c><anno>Default</anno></c> is used as the value + for uninitialized entries of the array. If <c><anno>List</anno></c> is not a proper list, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#to_list-1">to_list/1</seealso>.</p> </desc></func> <func> -<name>from_orddict(Orddict::list()) -> array()</name> +<name name="from_orddict" arity="1"/> <fsummary>Equivalent to from_orddict(Orddict, undefined). </fsummary> <desc><marker id="from_orddict-1"/> -<p>Equivalent to <seealso marker="#from_orddict-2">from_orddict(Orddict, undefined)</seealso>.</p> +<p>Equivalent to <seealso marker="#from_orddict-2">from_orddict(<anno>Orddict</anno>, undefined)</seealso>.</p> </desc></func> <func> -<name>from_orddict(List::list(), Default::term()) -> array()</name> +<name name="from_orddict" arity="2"/> <fsummary>Convert an ordered list of pairs {Index, Value} to a corresponding extendible array.</fsummary> <desc><marker id="from_orddict-2"/> <p>Convert an ordered list of pairs <c>{Index, Value}</c> to a - corresponding extendible array. <c>Default</c> is used as the value for - uninitialized entries of the array. If <c>List</c> is not a proper, + corresponding extendible array. <c><anno>Default</anno></c> is used as the value for + uninitialized entries of the array. If <c><anno>Orddict</anno></c> is not a proper, ordered list of pairs whose first elements are nonnegative integers, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p> </desc></func> <func> -<name>get(I::integer(), Array::array()) -> term()</name> +<name name="get" arity="2"/> <fsummary>Get the value of entry I.</fsummary> <desc><marker id="get-2"/> -<p>Get the value of entry <c>I</c>. If <c>I</c> is not a nonnegative - integer, or if the array has fixed size and <c>I</c> is larger than the +<p>Get the value of entry <c><anno>I</anno></c>. If <c><anno>I</anno></c> is not a nonnegative + integer, or if the array has fixed size and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>.</p> <p>If the array does not have fixed size, this function will return the - default value for any index <c>I</c> greater than <c>size(Array)-1</c>.</p> + default value for any index <c><anno>I</anno></c> greater than <c>size(<anno>Array</anno>)-1</c>.</p> <p><em>See also:</em> <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>is_array(X::term()) -> bool()</name> +<name name="is_array" arity="1"/> <fsummary>Returns true if X appears to be an array, otherwise false.</fsummary> <desc><marker id="is_array-1"/> -<p>Returns <c>true</c> if <c>X</c> appears to be an array, otherwise <c>false</c>. - Note that the check is only shallow; there is no guarantee that <c>X</c> +<p>Returns <c>true</c> if <c><anno>X</anno></c> appears to be an array, otherwise <c>false</c>. + Note that the check is only shallow; there is no guarantee that <c><anno>X</anno></c> is a well-formed array representation even if this function returns <c>true</c>.</p> </desc></func> <func> -<name>is_fix(Array::array()) -> bool()</name> +<name name="is_fix" arity="1"/> <fsummary>Check if the array has fixed size.</fsummary> <desc><marker id="is_fix-1"/> @@ -222,20 +233,18 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>.</p> </desc></func> <func> -<name>map(Function, Array::array()) -> array()</name> +<name name="map" arity="2"/> <fsummary>Map the given function onto each element of the array.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term()) -> term()</v></type> <desc><marker id="map-2"/> <p>Map the given function onto each element of the array. The elements are visited in order from the lowest index to the highest. - If <c>Function</c> is not a function, the call fails with reason <c>badarg</c>. + If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#sparse_map-2">sparse_map/2</seealso>.</p> </desc></func> <func> -<name>new() -> array()</name> +<name name="new" arity="0"/> <fsummary>Create a new, extendible array with initial size zero.</fsummary> <desc><marker id="new-0"/> @@ -244,7 +253,7 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#new-1">new/1</seealso>, <seealso marker="#new-2">new/2</seealso>.</p> </desc></func> <func> -<name>new(Options::term()) -> array()</name> +<name name="new" arity="1"/> <fsummary>Create a new array according to the given options.</fsummary> <desc><marker id="new-1"/> @@ -253,10 +262,10 @@ the default value cannot be confused with the values of set entries.</p> the array is extendible and has initial size zero. Array indices start at 0.</p> - <p><c>Options</c> is a single term or a list of terms, selected from the + <p><c><anno>Options</anno></c> is a single term or a list of terms, selected from the following: </p><taglist> - <tag><c>N::integer()</c> or <c>{size, N::integer()}</c></tag> + <tag><c>N::integer() >= 0</c> or <c>{size, N::integer() >= 0}</c></tag> <item><p>Specifies the initial size of the array; this also implies <c>{fixed, true}</c>. If <c>N</c> is not a nonnegative integer, the call fails with reason <c>badarg</c>.</p></item> @@ -283,19 +292,19 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>, <seealso marker="#from_list-2">from_list/2</seealso>, <seealso marker="#get-2">get/2</seealso>, <seealso marker="#new-0">new/0</seealso>, <seealso marker="#new-2">new/2</seealso>, <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>new(Size::integer(), Options::term()) -> array()</name> +<name name="new" arity="2"/> <fsummary>Create a new array according to the given size and options.</fsummary> <desc><marker id="new-2"/> <p>Create a new array according to the given size and options. If - <c>Size</c> is not a nonnegative integer, the call fails with reason + <c><anno>Size</anno></c> is not a nonnegative integer, the call fails with reason <c>badarg</c>. By default, the array has fixed size. Note that any size - specifications in <c>Options</c> will override the <c>Size</c> parameter.</p> + specifications in <c><anno>Options</anno></c> will override the <c><anno>Size</anno></c> parameter.</p> - <p>If <c>Options</c> is a list, this is simply equivalent to <c>new([{size, - Size} | Options]</c>, otherwise it is equivalent to <c>new([{size, Size} | - [Options]]</c>. However, using this function directly is more efficient.</p> + <p>If <c><anno>Options</anno></c> is a list, this is simply equivalent to <c>new([{size, + <anno>Size</anno>} | <anno>Options</anno>]</c>, otherwise it is equivalent to <c>new([{size, <anno>Size</anno>} | + [<anno>Options</anno>]]</c>. However, using this function directly is more efficient.</p> <p>Example: </p><pre> array:new(100, {default,0})</pre><p> creates a fixed-size array of size @@ -304,7 +313,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#new-1">new/1</seealso>.</p> </desc></func> <func> -<name>relax(Array::array()) -> array()</name> +<name name="relax" arity="1"/> <fsummary>Make the array resizable.</fsummary> <desc><marker id="relax-1"/> @@ -313,24 +322,24 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>.</p> </desc></func> <func> -<name>reset(I::integer(), Array::array()) -> array()</name> +<name name="reset" arity="2"/> <fsummary>Reset entry I to the default value for the array.</fsummary> <desc><marker id="reset-2"/> -<p>Reset entry <c>I</c> to the default value for the array. - If the value of entry <c>I</c> is the default value the array will be +<p>Reset entry <c><anno>I</anno></c> to the default value for the array. + If the value of entry <c><anno>I</anno></c> is the default value the array will be returned unchanged. Reset will never change size of the array. Shrinking can be done explicitly by calling <seealso marker="#resize-2">resize/2</seealso>.</p> - <p>If <c>I</c> is not a nonnegative integer, or if the array has fixed size - and <c>I</c> is larger than the maximum index, the call fails with reason + <p>If <c><anno>I</anno></c> is not a nonnegative integer, or if the array has fixed size + and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>; cf. <seealso marker="#set-3">set/3</seealso> </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>resize(Array::array()) -> array()</name> +<name name="resize" arity="1"/> <fsummary>Change the size of the array to that reported by sparse_size/1.</fsummary> <desc><marker id="resize-1"/> @@ -340,90 +349,84 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#resize-2">resize/2</seealso>, <seealso marker="#sparse_size-1">sparse_size/1</seealso>.</p> </desc></func> <func> -<name>resize(Size::integer(), Array::array()) -> array()</name> +<name name="resize" arity="2"/> <fsummary>Change the size of the array.</fsummary> <desc><marker id="resize-2"/> -<p>Change the size of the array. If <c>Size</c> is not a nonnegative +<p>Change the size of the array. If <c><anno>Size</anno></c> is not a nonnegative integer, the call fails with reason <c>badarg</c>. If the given array has fixed size, the resulting array will also have fixed size.</p> </desc></func> <func> -<name>set(I::integer(), Value::term(), Array::array()) -> array()</name> +<name name="set" arity="3"/> <fsummary>Set entry I of the array to Value.</fsummary> <desc><marker id="set-3"/> -<p>Set entry <c>I</c> of the array to <c>Value</c>. If <c>I</c> is not a - nonnegative integer, or if the array has fixed size and <c>I</c> is larger +<p>Set entry <c><anno>I</anno></c> of the array to <c><anno>Value</anno></c>. If <c><anno>I</anno></c> is not a + nonnegative integer, or if the array has fixed size and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>.</p> - <p>If the array does not have fixed size, and <c>I</c> is greater than - <c>size(Array)-1</c>, the array will grow to size <c>I+1</c>. + <p>If the array does not have fixed size, and <c><anno>I</anno></c> is greater than + <c>size(<anno>Array</anno>)-1</c>, the array will grow to size <c><anno>I</anno>+1</c>. </p> <p><em>See also:</em> <seealso marker="#get-2">get/2</seealso>, <seealso marker="#reset-2">reset/2</seealso>.</p> </desc></func> <func> -<name>size(Array::array()) -> integer()</name> +<name name="size" arity="1"/> <fsummary>Get the number of entries in the array.</fsummary> <desc><marker id="size-1"/> <p>Get the number of entries in the array. Entries are numbered - from 0 to <c>size(Array)-1</c>; hence, this is also the index of the first + from 0 to <c>size(<anno>Array</anno>)-1</c>; hence, this is also the index of the first entry that is guaranteed to not have been previously set.</p> <p><em>See also:</em> <seealso marker="#set-3">set/3</seealso>, <seealso marker="#sparse_size-1">sparse_size/1</seealso>.</p> </desc></func> <func> -<name>sparse_foldl(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="sparse_foldl" arity="3"/> <fsummary>Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="sparse_foldl-3"/> <p>Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the lowest index to the highest. - If <c>Function</c> is not a function, the call fails with reason <c>badarg</c>. + If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#sparse_foldr-3">sparse_foldr/3</seealso>.</p> </desc></func> <func> -<name>sparse_foldr(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="sparse_foldr" arity="3"/> <fsummary>Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="sparse_foldr-3"/> <p>Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the highest index to - the lowest. If <c>Function</c> is not a function, the call fails with + the lowest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#sparse_foldl-3">sparse_foldl/3</seealso>.</p> </desc></func> <func> -<name>sparse_map(Function, Array::array()) -> array()</name> +<name name="sparse_map" arity="2"/> <fsummary>Map the given function onto each element of the array, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term()) -> term()</v></type> <desc><marker id="sparse_map-2"/> <p>Map the given function onto each element of the array, skipping default-valued entries. The elements are visited in order from the - lowest index to the highest. If <c>Function</c> is not a function, the + lowest index to the highest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#map-2">map/2</seealso>.</p> </desc></func> <func> -<name>sparse_size(A::array()) -> integer()</name> +<name name="sparse_size" arity="1"/> <fsummary>Get the number of entries in the array up until the last non-default valued entry.</fsummary> @@ -436,7 +439,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#resize-1">resize/1</seealso>, <seealso marker="#size-1">size/1</seealso>.</p> </desc></func> <func> -<name>sparse_to_list(Array::array()) -> list()</name> +<name name="sparse_to_list" arity="1"/> <fsummary>Converts the array to a list, skipping default-valued entries.</fsummary> <desc><marker id="sparse_to_list-1"/> @@ -446,7 +449,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#to_list-1">to_list/1</seealso>.</p> </desc></func> <func> -<name>sparse_to_orddict(Array::array()) -> [{Index::integer(), Value::term()}]</name> +<name name="sparse_to_orddict" arity="1"/> <fsummary>Convert the array to an ordered list of pairs {Index, Value}, skipping default-valued entries.</fsummary> @@ -458,7 +461,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p> </desc></func> <func> -<name>to_list(Array::array()) -> list()</name> +<name name="to_list" arity="1"/> <fsummary>Converts the array to a list.</fsummary> <desc><marker id="to_list-1"/> @@ -468,7 +471,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#from_list-2">from_list/2</seealso>, <seealso marker="#sparse_to_list-1">sparse_to_list/1</seealso>.</p> </desc></func> <func> -<name>to_orddict(Array::array()) -> [{Index::integer(), Value::term()}]</name> +<name name="to_orddict" arity="1"/> <fsummary>Convert the array to an ordered list of pairs {Index, Value}.</fsummary> <desc><marker id="to_orddict-1"/> diff --git a/lib/stdlib/doc/src/base64.xml b/lib/stdlib/doc/src/base64.xml index d3fd7a843b..bfe8494a73 100644 --- a/lib/stdlib/doc/src/base64.xml +++ b/lib/stdlib/doc/src/base64.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2009</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,32 +33,33 @@ <description> <p>Implements base 64 encode and decode, see RFC2045. </p> </description> + <datatypes> + <datatype> + <name name="ascii_string"/> + </datatype> + </datatypes> <funcs> <func> - <name>encode(Data) -> Base64 </name> - <name>encode_to_string(Data) -> Base64String</name> + <name name="encode" arity="1"/> + <name name="encode_to_string" arity="1"/> <fsummary>Encodes data into base64. </fsummary> - <type> - <v>Data = string() | binary()</v> - <v>Base64 = binary()</v> - <v>Base64String = string()</v> - </type> + <type variable="Data"/> + <type variable="Base64" name_i="1"/> + <type variable="Base64String"/> <desc> <p>Encodes a plain ASCII string into base64. The result will be 33% larger than the data.</p> </desc> </func> <func> - <name>decode(Base64) -> Data</name> - <name>decode_to_string(Base64) -> DataString</name> - <name>mime_decode(Base64) -> Data</name> - <name>mime_decode_to_string(Base64) -> DataString</name> + <name name="decode" arity="1"/> + <name name="decode_to_string" arity="1"/> + <name name="mime_decode" arity="1"/> + <name name="mime_decode_to_string" arity="1"/> <fsummary>Decodes a base64 encoded string to data. </fsummary> - <type> - <v>Base64 = string() | binary()</v> - <v>Data = binary()</v> - <v>DataString = string()</v> - </type> + <type variable="Base64" name_i="1"/> + <type variable="Data" name_i="1"/> + <type variable="DataString" name_i="2"/> <desc> <p>Decodes a base64 encoded string to plain ASCII. See RFC4648. <c>mime_decode/1</c> and <c>mime_decode_to_string/1</c> diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml index adc411e272..013e94c393 100644 --- a/lib/stdlib/doc/src/beam_lib.xml +++ b/lib/stdlib/doc/src/beam_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -154,70 +154,78 @@ </section> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -beam() -> Module | Filename | binary() - Module = atom() - Filename = string() | atom()</code> - <p>Each of the functions described below accept either the module - name, the filename, or a binary containing the beam module.</p> - <code type="none"> -chunkdata() = {ChunkId, DataB} | {ChunkName, DataT} - ChunkId = chunkid() - DataB = binary() - {ChunkName, DataT} = - {abstract_code, AbstractCode} - | {attributes, [{Attribute, [AttributeValue]}]} - | {compile_info, [{InfoKey, [InfoValue]}]} - | {exports, [{Function, Arity}]} - | {labeled_exports, [{Function, Arity, Label}]} - | {imports, [{Module, Function, Arity}]} - | {indexed_imports, [{Index, Module, Function, Arity}]} - | {locals, [{Function, Arity}]}]} - | {labeled_locals, [{Function, Arity, Label}]}]} - | {atoms, [{integer(), atom()}]} - AbstractCode = {AbstVersion, Forms} | no_abstract_code - AbstVersion = atom() - Attribute = atom() - AttributeValue = term() - Module = Function = atom() - Arity = int() - Label = int()</code> - <p>It is not checked that the forms conform to the abstract format - indicated by <c>AbstVersion</c>. <c>no_abstract_code</c> means - that the <c>"Abst"</c> chunk is present, but empty.</p> - <p>The list of attributes is sorted on <c>Attribute</c>, and each - attribute name occurs once in the list. The attribute values - occur in the same order as in the file. The lists of functions - are also sorted.</p> - <code type="none"> -chunkid() = "Abst" | "Attr" | "CInf" - | "ExpT" | "ImpT" | "LocT" - | "Atom" + <datatypes> + <datatype> + <name name="beam"/> + <desc> + <p>Each of the functions described below accept either the + module name, the filename, or a binary containing the beam + module.</p> + </desc> + </datatype> + <datatype> + <name name="chunkdata"/> + <desc> + <p>The list of attributes is sorted on <c>Attribute</c> + (in attrib_entry()), and each + attribute name occurs once in the list. The attribute values + occur in the same order as in the file. The lists of functions + are also sorted.</p> + </desc> + </datatype> + <datatype> + <name name="chunkid"/> + <desc> + <p>"Abst" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom"</p> + </desc> + </datatype> + <datatype> + <name name="dataB"/> + </datatype> + <datatype> + <name name="abst_code"/> + <desc> + <p>It is not checked that the forms conform to the abstract format + indicated by <c><anno>AbstVersion</anno></c>. <c>no_abstract_code</c> means + that the <c>"Abst"</c> chunk is present, but empty.</p> + </desc> + </datatype> + <datatype> + <name name="forms"/> + </datatype> + <datatype> + <name name="compinfo_entry"/> + </datatype> + <datatype> + <name name="attrib_entry"/> + </datatype> + <datatype> + <name name="labeled_entry"/> + </datatype> + <datatype> + <name name="index"/> + </datatype> + <datatype> + <name name="label"/> + </datatype> + <datatype> + <name name="chunkref"/> + </datatype> + <datatype> + <name name="chunkname"/> + </datatype> + <datatype> + <name name="chnk_rsn"/> + </datatype> + <datatype> + <name name="info_rsn"/> + </datatype> + </datatypes> -chunkname() = abstract_code | attributes | compile_info - | exports | labeled_exports - | imports | indexed_imports - | locals | labeled_locals - | atoms - -chunkref() = chunkname() | chunkid()</code> - </section> <funcs> <func> - <name>chunks(Beam, [ChunkRef]) -> {ok, {Module, [ChunkData]}} | {error, beam_lib, Reason}</name> + <name name="chunks" arity="2"/> <fsummary>Read selected chunks from a BEAM file or binary</fsummary> - <type> - <v>Beam = beam()</v> - <v>ChunkRef = chunkref()</v> - <v>Module = atom()</v> - <v>ChunkData = chunkdata()</v> - <v>Reason = {unknown_chunk, Filename, atom()}</v> - <v> | {key_missing_or_invalid, Filename, abstract_code}</v> - <v> | Reason1 -- see info/1</v> - <v> Filename = string()</v> - </type> <desc> <p>Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order @@ -225,43 +233,26 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>chunks(Beam, [ChunkRef], [Option]) -> {ok, {Module, [ChunkResult]}} | {error, beam_lib, Reason}</name> + <name name="chunks" arity="3"/> <fsummary>Read selected chunks from a BEAM file or binary</fsummary> - <type> - <v>Beam = beam()</v> - <v>ChunkRef = chunkref()</v> - <v>Module = atom()</v> - <v>Option = allow_missing_chunks</v> - <v>ChunkResult = {chunkref(), ChunkContents} | {chunkref(), missing_chunk}</v> - <v>Reason = {missing_chunk, Filename, atom()}</v> - <v> | {key_missing_or_invalid, Filename, abstract_code}</v> - <v> | Reason1 -- see info/1</v> - <v> Filename = string()</v> - </type> <desc> <p>Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order of the list of chunks references.</p> - <p>By default, if any requested chunk is missing in <c>Beam</c>, + <p>By default, if any requested chunk is missing in <c><anno>Beam</anno></c>, an <c>error</c> tuple is returned. However, if the option <c>allow_missing_chunks</c> has been given, a result will be returned even if chunks are missing. In the result list, any missing chunks will be represented as - <c>{ChunkRef,missing_chunk}</c>. + <c>{<anno>ChunkRef</anno>,missing_chunk}</c>. Note, however, that if the <c>"Atom"</c> chunk if missing, that is considered a fatal error and the return value will be an <c>error</c> tuple.</p> </desc> </func> <func> - <name>version(Beam) -> {ok, {Module, [Version]}} | {error, beam_lib, Reason}</name> + <name name="version" arity="1"/> <fsummary>Read the BEAM file's module version</fsummary> - <type> - <v>Beam = beam()</v> - <v>Module = atom()</v> - <v>Version = term()</v> - <v>Reason -- see chunks/2</v> - </type> <desc> <p>Returns the module version(s). A version is defined by the module attribute <c>-vsn(Vsn)</c>. If this attribute is @@ -282,51 +273,30 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>md5(Beam) -> {ok, {Module, MD5}} | {error, beam_lib, Reason}</name> + <name name="md5" arity="1"/> <fsummary>Read the BEAM file's module version</fsummary> - <type> - <v>Beam = beam()</v> - <v>Module = atom()</v> - <v>MD5 = binary()</v> - <v>Reason -- see chunks/2</v> - </type> <desc> <p>Calculates an MD5 redundancy check for the code of the module (compilation date and other attributes are not included).</p> </desc> </func> <func> - <name>info(Beam) -> [{Item, Info}] | {error, beam_lib, Reason1}</name> + <name name="info" arity="1"/> <fsummary>Information about a BEAM file</fsummary> - <type> - <v>Beam = beam()</v> - <v>Item, Info -- see below</v> - <v>Reason1 = {chunk_too_big, Filename, ChunkId, ChunkSize, FileSize}</v> - <v> | {invalid_beam_file, Filename, Pos}</v> - <v> | {invalid_chunk, Filename, ChunkId}</v> - <v> | {missing_chunk, Filename, ChunkId}</v> - <v> | {not_a_beam_file, Filename}</v> - <v> | {file_error, Filename, Posix}</v> - <v> Filename = string()</v> - <v> ChunkId = chunkid()</v> - <v> ChunkSize = FileSize = int()</v> - <v> Pos = int()</v> - <v> Posix = posix() -- see file(3)</v> - </type> <desc> <p>Returns a list containing some information about a BEAM file as tuples <c>{Item, Info}</c>:</p> <taglist> - <tag><c>{file, Filename} | {binary, Binary}</c></tag> + <tag><c>{file, <anno>Filename</anno>} | {binary, <anno>Binary</anno>}</c></tag> <item> <p>The name (string) of the BEAM file, or the binary from which the information was extracted.</p> </item> - <tag><c>{module, Module}</c></tag> + <tag><c>{module, <anno>Module</anno>}</c></tag> <item> <p>The name (atom) of the module.</p> </item> - <tag><c>{chunks, [{ChunkId, Pos, Size}]}</c></tag> + <tag><c>{chunks, [{<anno>ChunkId</anno>, <anno>Pos</anno>, <anno>Size</anno>}]}</c></tag> <item> <p>For each chunk, the identifier (string) and the position and size of the chunk data, in bytes.</p> @@ -335,17 +305,9 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>cmp(Beam1, Beam2) -> ok | {error, beam_lib, Reason}</name> + <name name="cmp" arity="2"/> <fsummary>Compare two BEAM files</fsummary> - <type> - <v>Beam1 = Beam2 = beam()</v> - <v>Reason = {modules_different, Module1, Module2}</v> - <v> | {chunks_different, ChunkId}</v> - <v> | different_chunks</v> - <v> | Reason1 -- see info/1</v> - <v> Module1 = Module2 = atom()</v> - <v> ChunkId = chunkid()</v> - </type> + <type name="cmp_rsn"/> <desc> <p>Compares the contents of two BEAM files. If the module names are the same, and all chunks except for the <c>"CInf"</c> chunk @@ -356,34 +318,23 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>cmp_dirs(Dir1, Dir2) -> {Only1, Only2, Different} | {error, beam_lib, Reason1}</name> + <name name="cmp_dirs" arity="2"/> <fsummary>Compare the BEAM files in two directories</fsummary> - <type> - <v>Dir1 = Dir2 = string() | atom()</v> - <v>Different = [{Filename1, Filename2}]</v> - <v>Only1 = Only2 = [Filename]</v> - <v>Filename = Filename1 = Filename2 = string()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>cmp_dirs/2</c> function compares the BEAM files in two directories. Only files with extension <c>".beam"</c> are - compared. BEAM files that exist in directory <c>Dir1</c> - (<c>Dir2</c>) only are returned in <c>Only1</c> - (<c>Only2</c>). BEAM files that exist on both directories but + compared. BEAM files that exist in directory <c><anno>Dir1</anno></c> + (<c><anno>Dir2</anno></c>) only are returned in <c><anno>Only1</anno></c> + (<c><anno>Only2</anno></c>). BEAM files that exist on both directories but are considered different by <c>cmp/2</c> are returned as - pairs {<c>Filename1</c>, <c>Filename2</c>} where - <c>Filename1</c> (<c>Filename2</c>) exists in directory - <c>Dir1</c> (<c>Dir2</c>).</p> + pairs {<c><anno>Filename1</anno></c>, <c><anno>Filename2</anno></c>} where + <c><anno>Filename1</anno></c> (<c><anno>Filename2</anno></c>) exists in directory + <c><anno>Dir1</anno></c> (<c><anno>Dir2</anno></c>).</p> </desc> </func> <func> - <name>diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason1}</name> + <name name="diff_dirs" arity="2"/> <fsummary>Compare the BEAM files in two directories</fsummary> - <type> - <v>Dir1 = Dir2 = string() | atom()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>diff_dirs/2</c> function compares the BEAM files in two directories the way <c>cmp_dirs/2</c> does, but names of @@ -392,13 +343,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip(Beam1) -> {ok, {Module, Beam2}} | {error, beam_lib, Reason1}</name> + <name name="strip" arity="1"/> <fsummary>Removes chunks not needed by the loader from a BEAM file</fsummary> - <type> - <v>Beam1 = Beam2 = beam()</v> - <v>Module = atom()</v> - <v>Reason1 -- see info/1</v> - </type> <desc> <p>The <c>strip/1</c> function removes all chunks from a BEAM file except those needed by the loader. In particular, @@ -406,15 +352,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip_files(Files) -> {ok, [{Module, Beam2}]} | {error, beam_lib, Reason1}</name> + <name name="strip_files" arity="1"/> <fsummary>Removes chunks not needed by the loader from BEAM files</fsummary> - <type> - <v>Files = [Beam1]</v> - <v> Beam1 = beam()</v> - <v>Module = atom()</v> - <v>Beam2 = beam()</v> - <v>Reason1 -- see info/1</v> - </type> <desc> <p>The <c>strip_files/1</c> function removes all chunks except those needed by the loader from BEAM files. In particular, @@ -424,30 +363,20 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip_release(Dir) -> {ok, [{Module, Filename]}} | {error, beam_lib, Reason1}</name> + <name name="strip_release" arity="1"/> <fsummary>Removes chunks not needed by the loader from all BEAM files of a release</fsummary> - <type> - <v>Dir = string() | atom()</v> - <v>Module = atom()</v> - <v>Filename = string()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>strip_release/1</c> function removes all chunks except those needed by the loader from the BEAM files of a - release. <c>Dir</c> should be the installation root + release. <c><anno>Dir</anno></c> should be the installation root directory. For example, the current OTP release can be stripped with the call <c>beam_lib:strip_release(code:root_dir())</c>.</p> </desc> </func> <func> - <name>format_error(Reason) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return an English description of a BEAM read error reply</fsummary> - <type> - <v>Reason -- see other functions</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Given the error returned by any function in this module, the function <c>format_error</c> returns a descriptive string @@ -456,12 +385,11 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason}</name> + <name name="crypto_key_fun" arity="1"/> <fsummary>Register a fun that provides a crypto key</fsummary> - <type> - <v>CryptoKeyFun = fun() -- see below</v> - <v>Reason = badfun | exists | term()</v> - </type> + <type name="crypto_fun"/> + <type name="crypto_fun_arg"/> + <type name="mode"/> <desc> <p>The <c>crypto_key_fun/1</c> function registers a unary fun that will be called if <c>beam_lib</c> needs to read an @@ -495,11 +423,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>clear_crypto_key_fun() -> {ok, Result}</name> + <name name="clear_crypto_key_fun" arity="0"/> <fsummary>Unregister the current crypto key fun</fsummary> - <type> - <v>Result = undefined | term()</v> - </type> <desc> <p>Unregisters the crypto key fun and terminates the process holding it, started by <c>crypto_key_fun/1</c>.</p> diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index c81023862e..88ce77e0d0 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2009</year> - <year>2010</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -53,37 +53,35 @@ module. </p> </note> - - </description> - <section> - <title>DATA TYPES</title> - <code type="none"> - cp() - - Opaque data-type representing a compiled search-pattern. Guaranteed to be a tuple() - to allow programs to distinguish it from non precompiled search patterns. - </code> - <code type="none"> - part() = {Start,Length} - Start = int() - Length = int() - - A representaion of a part (or range) in a binary. Start is a + <datatypes> + <datatype> + <name name="cp"/> + <desc><p>Opaque data-type representing a compiled + search-pattern. Guaranteed to be a tuple() to allow programs to + distinguish it from non precompiled search patterns.</p> + </desc> + </datatype> + <datatype> + <name name="part"/> + <desc><p>A representaion of a part (or range) in a binary. Start is a zero-based offset into a binary() and Length is the length of that part. As input to functions in this module, a reverse part specification is allowed, constructed with a negative Length, so that the part of the binary begins at Start + Length and is -Length long. This is useful for referencing the last N bytes of a binary as {size(Binary), -N}. The functions - in this module always return part()'s with positive Length. - </code> - </section> + in this module always return part()'s with positive Length.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>at(Subject, Pos) -> int()</name> + <name>at(Subject, Pos) -> byte()</name> <fsummary>Returns the byte at a specific position in a binary</fsummary> <type> <v>Subject = binary()</v> - <v>Pos = int() >= 0</v> + <v>Pos = integer() >= 0</v> </type> <desc> @@ -95,7 +93,7 @@ </desc> </func> <func> - <name>bin_to_list(Subject) -> list()</name> + <name>bin_to_list(Subject) -> [byte()]</name> <fsummary>Convert a binary to a list of integers</fsummary> <type> <v>Subject = binary()</v> @@ -105,7 +103,7 @@ </desc> </func> <func> - <name>bin_to_list(Subject, PosLen) -> list()</name> + <name>bin_to_list(Subject, PosLen) -> [byte()]</name> <fsummary>Convert a binary to a list of integers</fsummary> <type> <v>Subject = binary()</v> @@ -113,7 +111,7 @@ </type> <desc> - <p>Converts <c>Subject</c> to a list of <c>int()</c>s, each representing + <p>Converts <c>Subject</c> to a list of <c>byte()</c>s, each representing the value of one byte. The <c>part()</c> denotes which part of the <c>binary()</c> to convert. Example:</p> @@ -126,12 +124,12 @@ </desc> </func> <func> - <name>bin_to_list(Subject, Pos, Len) -> list()</name> + <name>bin_to_list(Subject, Pos, Len) -> [byte()]</name> <fsummary>Convert a binary to a list of integers</fsummary> <type> <v>Subject = binary()</v> - <v>Pos = int()</v> - <v>Len = int()</v> + <v>Pos = integer() >= 0</v> + <v>Len = integer() >= 0</v> </type> <desc> <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p> @@ -185,7 +183,7 @@ <fsummary>Duplicates a binary N times and creates a new</fsummary> <type> <v>Subject = binary()</v> - <v>N = int() >= 0</v> + <v>N = integer() >= 0</v> </type> <desc> <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p> @@ -211,7 +209,7 @@ <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary> <type> <v>Subject = binary()</v> - <v>Unsigned = int() >= 0</v> + <v>Unsigned = integer() >= 0</v> </type> <desc> <p>The same as <c>decode_unsigned(Subject,big)</c>.</p> @@ -223,12 +221,12 @@ <type> <v>Subject = binary()</v> <v>Endianess = big | little</v> - <v>Unsigned = int() >= 0</v> + <v>Unsigned = integer() >= 0</v> </type> <desc> <p>Converts the binary digit representation, in big or little - endian, of a positive integer in <c>Subject</c> to an Erlang <c>int()</c>.</p> + endian, of a positive integer in <c>Subject</c> to an Erlang <c>integer()</c>.</p> <p>Example:</p> @@ -242,7 +240,7 @@ <name>encode_unsigned(Unsigned) -> binary()</name> <fsummary>Encodes an unsigned integer into the minimal binary</fsummary> <type> - <v>Unsigned = int() >= 0</v> + <v>Unsigned = integer() >= 0</v> </type> <desc> <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p> @@ -252,7 +250,7 @@ <name>encode_unsigned(Unsigned,Endianess) -> binary()</name> <fsummary>Encodes an unsigned integer into the minimal binary</fsummary> <type> - <v>Unsigned = int() >= 0</v> + <v>Unsigned = integer() >= 0</v> <v>Endianess = big | little</v> </type> <desc> @@ -270,7 +268,7 @@ </desc> </func> <func> - <name>first(Subject) -> int()</name> + <name>first(Subject) -> byte()</name> <fsummary>Returns the first byte of a binary</fsummary> <type> <v>Subject = binary()</v> @@ -283,7 +281,7 @@ </desc> </func> <func> - <name>last(Subject) -> int()</name> + <name>last(Subject) -> byte()</name> <fsummary>Returns the last byte of a binary</fsummary> <type> <v>Subject = binary()</v> @@ -306,7 +304,7 @@ </desc> </func> <func> - <name>longest_common_prefix(Binaries) -> int()</name> + <name>longest_common_prefix(Binaries) -> integer() >= 0</name> <fsummary>Returns length of longest common prefix for a set of binaries</fsummary> <type> <v>Binaries = [ binary() ]</v> @@ -327,7 +325,7 @@ </desc> </func> <func> - <name>longest_common_suffix(Binaries) -> int()</name> + <name>longest_common_suffix(Binaries) -> integer() >= 0</name> <fsummary>Returns length of longest common suffix for a set of binaries</fsummary> <type> <v>Binaries = [ binary() ]</v> @@ -450,7 +448,7 @@ [{1,4}] </code> - <p>The result shows that <<bcde">> is selected instead of the + <p>The result shows that <<"bcde">> is selected instead of the shorter match <<"bc">> (which would have given raise to one more match,<<"de">>). This corresponds to the behavior of posix regular expressions (and programs like awk), but is not @@ -506,15 +504,15 @@ <fsummary>Extracts a part of a binary</fsummary> <type> <v>Subject = binary()</v> - <v>Pos = int()</v> - <v>Len = int()</v> + <v>Pos = integer() >= 0</v> + <v>Len = integer() >= 0</v> </type> <desc> <p>The same as <c>part(Subject, {Pos, Len})</c>.</p> </desc> </func> <func> - <name>referenced_byte_size(binary()) -> int()</name> + <name>referenced_byte_size(binary()) -> integer() >= 0</name> <fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary> <desc> @@ -581,42 +579,28 @@ store(Binary, GBSet) -> </desc> </func> <func> - <name>replace(Subject,Pattern,Replacement) -> Result</name> + <name name="replace" arity="3"/> <fsummary>Replaces bytes in a binary according to a pattern</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Replacement = binary()</v> - <v>Result = binary()</v> - </type> <desc> - <p>The same as <c>replace(Subject,Pattern,Replacement,[])</c>.</p> + <p>The same as <c>replace(<anno>Subject</anno>,<anno>Pattern</anno>,<anno>Replacement</anno>,[])</c>.</p> </desc> </func> <func> - <name>replace(Subject,Pattern,Replacement,Options) -> Result</name> + <name name="replace" arity="4"/> <fsummary>Replaces bytes in a binary according to a pattern</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Replacement = binary()</v> - <v>Result = binary()</v> - <v>Options = [ Option ]</v> - <v>Option = global | {scope, part()} | {insert_replaced, InsPos}</v> - <v>InsPos = OnePos | [ OnePos ]</v> - <v>OnePos = int() =< byte_size(Replacement)</v> - </type> + <type_desc variable="OnePos">An integer() =< byte_size(<anno>Replacement</anno>) + </type_desc> <desc> <p>Constructs a new binary by replacing the parts in - <c>Subject</c> matching <c>Pattern</c> with the content of - <c>Replacement</c>.</p> + <c><anno>Subject</anno></c> matching <c><anno>Pattern</anno></c> with the content of + <c><anno>Replacement</anno></c>.</p> - <p>If the matching sub-part of <c>Subject</c> giving raise to the + <p>If the matching sub-part of <c><anno>Subject</anno></c> giving raise to the replacement is to be inserted in the result, the option - <c>{insert_replaced, InsPos}</c> will insert the matching part into - <c>Replacement</c> at the given position (or positions) before actually - inserting <c>Replacement</c> into the <c>Subject</c>. Example:</p> + <c>{insert_replaced, <anno>InsPos</anno>}</c> will insert the matching part into + <c><anno>Replacement</anno></c> at the given position (or positions) before actually + inserting <c><anno>Replacement</anno></c> into the <c><anno>Subject</anno></c>. Example:</p> <code> 1> binary:replace(<<"abcde">>,<<"b">>,<<"[]">>,[{insert_replaced,1}]). @@ -632,42 +616,30 @@ store(Binary, GBSet) -> <<"a[b-b]c[d-d]e">> </code> - <p>If any position given in <c>InsPos</c> is greater than the size of the replacement binary, a <c>badarg</c> exception is raised.</p> + <p>If any position given in <c><anno>InsPos</anno></c> is greater than the size of the replacement binary, a <c>badarg</c> exception is raised.</p> <p>The options <c>global</c> and <c>{scope, part()}</c> work as for <seealso marker="#split-3">split/3</seealso>. The return type is always a <c>binary()</c>.</p> - <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> </desc> </func> <func> - <name>split(Subject,Pattern) -> Parts</name> + <name name="split" arity="2"/> <fsummary>Splits a binary according to a pattern</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Parts = [ binary() ]</v> - </type> <desc> - <p>The same as <c>split(Subject, Pattern, [])</c>.</p> + <p>The same as <c>split(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p> </desc> </func> <func> - <name>split(Subject,Pattern,Options) -> Parts</name> + <name name="split" arity="3"/> <fsummary>Splits a binary according to a pattern</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Parts = [ binary() ]</v> - <v>Options = [ Option ]</v> - <v>Option = {scope, part()} | trim | global</v> - </type> <desc> - <p>Splits Binary into a list of binaries based on Pattern. If + <p>Splits <c><anno>Subject</anno></c> into a list of binaries based on <c><anno>Pattern</anno></c>. If the option global is not given, only the first occurrence of - Pattern in Subject will give rise to a split.</p> + <c><anno>Pattern</anno></c> in <c><anno>Subject</anno></c> will give rise to a split.</p> - <p>The parts of Pattern actually found in Subject are not included in the result.</p> + <p>The parts of <c><anno>Pattern</anno></c> actually found in <c><anno>Subject</anno></c> are not included in the result.</p> <p>Example:</p> @@ -696,7 +668,7 @@ store(Binary, GBSet) -> <tag>global</tag> - <item><p>Repeats the split until the <c>Subject</c> is + <item><p>Repeats the split until the <c><anno>Subject</anno></c> is exhausted. Conceptually the global option makes split work on the positions returned by <seealso marker="#matches-3">matches/3</seealso>, while it normally @@ -716,12 +688,12 @@ store(Binary, GBSet) -> </code> <p>The return type is always a list of binaries that are all - referencing <c>Subject</c>. This means that the data in <c>Subject</c> is not - actually copied to new binaries and that <c>Subject</c> cannot be + referencing <c><anno>Subject</anno></c>. This means that the data in <c><anno>Subject</anno></c> is not + actually copied to new binaries and that <c><anno>Subject</anno></c> cannot be garbage collected until the results of the split are no longer referenced.</p> - <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml index 19e3ac1f08..ddae388a1b 100644 --- a/lib/stdlib/doc/src/c.xml +++ b/lib/stdlib/doc/src/c.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,43 +39,33 @@ </description> <funcs> <func> - <name>bt(Pid) -> void()</name> + <name name="bt" arity="1"/> <fsummary>Stack backtrace for a process</fsummary> - <type> - <v>Pid = pid()</v> - </type> <desc> <p>Stack backtrace for a process. Equivalent to - <c>erlang:process_display(Pid, backtrace)</c>.</p> + <c>erlang:process_display(<anno>Pid</anno>, backtrace)</c>.</p> </desc> </func> <func> - <name>c(File) -> {ok, Module} | error</name> - <name>c(File, Options) -> {ok, Module} | error</name> + <name name="c" arity="1"/> + <name name="c" arity="2"/> <fsummary>Compile and load code in a file</fsummary> - <type> - <v>File = name() -- see filename(3)</v> - <v>Options = [Opt] -- see compile:file/2</v> - </type> <desc> <p><c>c/1,2</c> compiles and then purges and loads the code for - a file. <c>Options</c> defaults to []. Compilation is + a file. <c><anno>Options</anno></c> defaults to []. Compilation is equivalent to:</p> <code type="none"> -compile:file(File, Options ++ [report_errors, report_warnings])</code> +compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_warnings])</code> <p>Note that purging the code means that any processes lingering in old code for the module are killed without warning. See <c>code/3</c> for more information.</p> </desc> </func> <func> - <name>cd(Dir) -> void()</name> + <name name="cd" arity="1"/> <fsummary>Change working directory</fsummary> - <type> - <v>Dir = name() -- see filename(3)</v> - </type> <desc> - <p>Changes working directory to <c>Dir</c>, which may be a + <p>Changes working directory to <c><anno>Dir</anno></c>, which may be a relative name, and then prints the name of the new working directory.</p> <pre> @@ -84,14 +74,14 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>flush() -> void()</name> + <name name="flush" arity="0"/> <fsummary>Flush any messages sent to the shell</fsummary> <desc> <p>Flushes any messages sent to the shell.</p> </desc> </func> <func> - <name>help() -> void()</name> + <name name="help" arity="0"/> <fsummary>Help information</fsummary> <desc> <p>Displays help information: all valid shell internal commands, @@ -99,8 +89,8 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>i() -> void()</name> - <name>ni() -> void()</name> + <name name="i" arity="0"/> + <name name="ni" arity="0"/> <fsummary>Information about the system</fsummary> <desc> <p><c>i/0</c> displays information about the system, listing @@ -109,26 +99,20 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>i(X, Y, Z) -> void()</name> + <name name="i" arity="3"/> <fsummary>Information about pid <X.Y.Z></fsummary> - <type> - <v>X = Y = Z = int()</v> - </type> <desc> <p>Displays information about a process, Equivalent to - <c>process_info(pid(X, Y, Z))</c>, but location transparent.</p> + <c>process_info(pid(<anno>X</anno>, <anno>Y</anno>, <anno>Z</anno>))</c>, but location transparent.</p> </desc> </func> <func> - <name>l(Module) -> void()</name> + <name name="l" arity="1"/> <fsummary>Load or reload module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> <p>Purges and loads, or reloads, a module by calling - <c>code:purge(Module)</c> followed by - <c>code:load_file(Module)</c>.</p> + <c>code:purge(<anno>Module</anno>)</c> followed by + <c>code:load_file(<anno>Module</anno>)</c>.</p> <p>Note that purging the code means that any processes lingering in old code for the module are killed without warning. See <c>code/3</c> for more information.</p> @@ -136,35 +120,33 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </func> <func> <name>lc(Files) -> ok</name> - <fsummary>Compile a list of files</fsummary> <type> <v>Files = [File]</v> - <v> File = name() -- see filename(3)</v> + <v>File = <seealso marker="file#type-filename">file:filename() + </seealso></v> </type> + <fsummary>Compile a list of files</fsummary> <desc> <p>Compiles a list of files by calling <c>compile:file(File, [report_errors, report_warnings])</c> for each <c>File</c> in <c>Files</c>.</p> </desc> </func> <func> - <name>ls() -> void()</name> + <name name="ls" arity="0"/> <fsummary>List files in the current directory</fsummary> <desc> <p>Lists files in the current directory.</p> </desc> </func> <func> - <name>ls(Dir) -> void()</name> + <name name="ls" arity="1"/> <fsummary>List files in a directory</fsummary> - <type> - <v>Dir = name() -- see filename(3)</v> - </type> <desc> - <p>Lists files in directory <c>Dir</c>.</p> + <p>Lists files in directory <c><anno>Dir</anno></c>.</p> </desc> </func> <func> - <name>m() -> void()</name> + <name name="m" arity="0"/> <fsummary>Which modules are loaded</fsummary> <desc> <p>Displays information about the loaded modules, including @@ -172,84 +154,67 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>m(Module) -> void()</name> + <name name="m" arity="1"/> <fsummary>Information about a module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Displays information about <c>Module</c>.</p> + <p>Displays information about <c><anno>Module</anno></c>.</p> </desc> </func> <func> - <name>memory() -> [{Type, Size}]</name> + <name name="memory" arity="0"/> <fsummary>Memory allocation information</fsummary> - <type> - <v>Type, Size -- see erlang:memory/0</v> - </type> <desc> <p>Memory allocation information. Equivalent to - <c>erlang:memory/0</c>.</p> + <seealso marker="erts:erlang#memory/0"><c>erlang:memory/0</c> + </seealso>.</p> </desc> </func> <func> - <name>memory(Type) -> Size</name> - <name>memory([Type]) -> [{Type, Size}]</name> + <name name="memory" arity="1" clause_i="1"/> + <name name="memory" arity="1" clause_i="2"/> <fsummary>Memory allocation information</fsummary> - <type> - <v>Type, Size -- see erlang:memory/0</v> - </type> <desc> <p>Memory allocation information. Equivalent to - <c>erlang:memory/1</c>.</p> + <seealso marker="erts:erlang#memory/1"><c>erlang:memory/1</c> + </seealso>.</p> </desc> </func> <func> - <name>nc(File) -> {ok, Module} | error</name> - <name>nc(File, Options) -> {ok, Module} | error</name> + <name name="nc" arity="1"/> + <name name="nc" arity="2"/> <fsummary>Compile and load code in a file on all nodes</fsummary> - <type> - <v>File = name() -- see filename(3)</v> - <v>Options = [Opt] -- see compile:file/2</v> - </type> <desc> <p>Compiles and then loads the code for a file on all nodes. - <c>Options</c> defaults to []. Compilation is equivalent to:</p> + <c><anno>Options</anno></c> defaults to []. Compilation is equivalent to:</p> <code type="none"> -compile:file(File, Opts ++ [report_errors, report_warnings])</code> +compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>nl(Module) -> void()</name> + <name name="nl" arity="1"/> <fsummary>Load module on all nodes</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Loads <c>Module</c> on all nodes.</p> + <p>Loads <c><anno>Module</anno></c> on all nodes.</p> </desc> </func> <func> - <name>pid(X, Y, Z) -> pid()</name> + <name name="pid" arity="3"/> <fsummary>Convert X,Y,Z to a pid</fsummary> - <type> - <v>X = Y = Z = int()</v> - </type> <desc> - <p>Converts <c>X</c>, <c>Y</c>, <c>Z</c> to the pid + <p>Converts <c><anno>X</anno></c>, <c><anno>Y</anno></c>, <c><anno>Z</anno></c> to the pid <c><![CDATA[<X.Y.Z>]]></c>. This function should only be used when debugging.</p> </desc> </func> <func> - <name>pwd() -> void()</name> + <name name="pwd" arity="0"/> <fsummary>Print working directory</fsummary> <desc> <p>Prints the name of the working directory.</p> </desc> </func> <func> - <name>q() -> void()</name> + <name name="q" arity="0"/> <fsummary>Quit - shorthand for <c>init:stop()</c></fsummary> <desc> <p>This function is shorthand for <c>init:stop()</c>, that is, @@ -257,8 +222,8 @@ compile:file(File, Opts ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>regs() -> void()</name> - <name>nregs() -> void()</name> + <name name="regs" arity="0"/> + <name name="nregs" arity="0"/> <fsummary>Information about registered processes</fsummary> <desc> <p><c>regs/0</c> displays information about all registered diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml index 075c7f9c78..4876b37127 100644 --- a/lib/stdlib/doc/src/calendar.xml +++ b/lib/stdlib/doc/src/calendar.xml @@ -73,100 +73,125 @@ week number.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -date() = {Year, Month, Day} - Year = int() - Month = 1..12 - Day = 1..31 -Year cannot be abbreviated. Example: 93 denotes year 93, not 1993. -Valid range depends on the underlying OS. -The date tuple must denote a valid date. + <datatypes> + <datatype> + <name name="t_datetime"/> + </datatype> + <datatype> + <name name="t_datetime1970"/> + </datatype> + <datatype> + <name name="t_date"/> + </datatype> + <datatype> + <name name="year"/> + <desc><p>Year cannot be abbreviated. Example: 93 denotes year + 93, not 1993. Valid range depends on the underlying OS. The + date tuple must denote a valid date.</p> + </desc> + </datatype> + <datatype> + <name name="year1970"/> + </datatype> + <datatype> + <name name="month"/> + </datatype> + <datatype> + <name name="day"/> + </datatype> + <datatype> + <name name="t_time"/> + </datatype> + <datatype> + <name name="hour"/> + </datatype> + <datatype> + <name name="minute"/> + </datatype> + <datatype> + <name name="second"/> + </datatype> + <datatype> + <name name="daynum"/> + </datatype> + <datatype> + <name name="ldom"/> + </datatype> + <datatype> + <name name="t_now"/> + <desc><p>See <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="t_yearweeknum"/> + </datatype> + <datatype> + <name name="weeknum"/> + </datatype> + </datatypes> -time() = {Hour, Minute, Second} - Hour = 0..23 - Minute = Second = 0..59</code> - </section> <funcs> <func> - <name>date_to_gregorian_days(Date) -> Days</name> - <name>date_to_gregorian_days(Year, Month, Day) -> Days</name> + <name name="date_to_gregorian_days" arity="1"/> + <name name="date_to_gregorian_days" arity="3"/> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <fsummary>Compute the number of days from year 0 up to the given date</fsummary> - <type> - <v>Date = date()</v> - <v>Days = int()</v> - </type> <desc> <p>This function computes the number of gregorian days starting with year 0 and ending at the given date.</p> </desc> </func> <func> - <name>datetime_to_gregorian_seconds({Date, Time}) -> Seconds</name> + <name name="datetime_to_gregorian_seconds" arity="1"/> <fsummary>Compute the number of seconds from year 0 up to the given date and time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - <v>Seconds = int()</v> - </type> <desc> <p>This function computes the number of gregorian seconds starting with year 0 and ending at the given date and time.</p> </desc> </func> <func> - <name>day_of_the_week(Date) -> DayNumber</name> - <name>day_of_the_week(Year, Month, Day) -> DayNumber</name> + <name name="day_of_the_week" arity="1"/> + <name name="day_of_the_week" arity="3"/> <fsummary>Compute the day of the week</fsummary> - <type> - <v>Date = date()</v> - <v>DayNumber = 1..7</v> - </type> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <desc> - <p>This function computes the day of the week given <c>Year</c>, - <c>Month</c> and <c>Day</c>. The return value denotes the day + <p>This function computes the day of the week given <c><anno>Year</anno></c>, + <c><anno>Month</anno></c> and <c><anno>Day</anno></c>. The return value denotes the day of the week as <c>1</c>: Monday, <c>2</c>: Tuesday, and so on.</p> </desc> </func> <func> - <name>gregorian_days_to_date(Days) -> Date</name> + <name name="gregorian_days_to_date" arity="1"/> <fsummary>Compute the date given the number of gregorian days</fsummary> - <type> - <v>Days = int()</v> - <v>Date = date()</v> - </type> <desc> <p>This function computes the date given the number of gregorian days.</p> </desc> </func> <func> - <name>gregorian_seconds_to_datetime(Seconds) -> {Date, Time}</name> + <name name="gregorian_seconds_to_datetime" arity="1"/> <fsummary>Compute the date given the number of gregorian days</fsummary> - <type> - <v>Seconds = int()</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function computes the date and time from the given number of gregorian seconds.</p> </desc> </func> <func> - <name>is_leap_year(Year) -> bool()</name> + <name name="is_leap_year" arity="1"/> <fsummary>Check if a year is a leap year</fsummary> <desc> <p>This function checks if a year is a leap year.</p> </desc> </func> <func> - <name>iso_week_number() -> IsoWeekNumber</name> + <name name="iso_week_number" arity="0"/> <fsummary>Compute the iso week number for the actual date</fsummary> - <type> - <v>IsoWeekNumber = {int(), int()}</v> - </type> <desc> <p>This function returns the tuple {Year, WeekNum} representing the iso week number for the actual date. For determining the @@ -174,42 +199,34 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>iso_week_number(Date) -> IsoWeekNumber</name> + <name name="iso_week_number" arity="1"/> <fsummary>Compute the iso week number for the given date</fsummary> - <type> - <v>Date = date()</v> - <v>IsoWeekNumber = {int(), int()}</v> - </type> <desc> <p>This function returns the tuple {Year, WeekNum} representing the iso week number for the given date.</p> </desc> </func> <func> - <name>last_day_of_the_month(Year, Month) -> int()</name> + <name name="last_day_of_the_month" arity="2"/> <fsummary>Compute the number of days in a month</fsummary> <desc> <p>This function computes the number of days in a month.</p> </desc> </func> <func> - <name>local_time() -> {Date, Time}</name> + <name name="local_time" arity="0"/> <fsummary>Compute local time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns the local time reported by the underlying operating system.</p> </desc> </func> <func> - <name>local_time_to_universal_time({Date1, Time1}) -> {Date2, Time2}</name> + <name name="local_time_to_universal_time" arity="1"/> <fsummary>Convert from local time to universal time (deprecated)</fsummary> <desc> <p>This function converts from local time to Universal - Coordinated Time (UTC). <c>Date1</c> must refer to a local + Coordinated Time (UTC). <c><anno>DateTime1</anno></c> must refer to a local date after Jan 1, 1970.</p> <warning> <p>This function is deprecated. Use @@ -222,15 +239,11 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>local_time_to_universal_time_dst({Date1, Time1}) -> [{Date, Time}]</name> + <name name="local_time_to_universal_time_dst" arity="1"/> <fsummary>Convert from local time to universal time(s)</fsummary> - <type> - <v>Date1 = Date = date()</v> - <v>Time1 = Time = time()</v> - </type> <desc> <p>This function converts from local time to Universal - Coordinated Time (UTC). <c>Date1</c> must refer to a local + Coordinated Time (UTC). <c><anno>DateTime1</anno></c> must refer to a local date after Jan 1, 1970.</p> <p>The return value is a list of 0, 1 or 2 possible UTC times:</p> <taglist> @@ -258,65 +271,48 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>now_to_local_time(Now) -> {Date, Time}</name> + <name name="now_to_local_time" arity="1"/> <fsummary>Convert now to local date and time</fsummary> - <type> - <v>Now -- see erlang:now/0</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns local date and time converted from the return value from <c>erlang:now()</c>.</p> </desc> </func> <func> - <name>now_to_universal_time(Now) -> {Date, Time}</name> - <name>now_to_datetime(Now) -> {Date, Time}</name> + <name name="now_to_universal_time" arity="1"/> + <name name="now_to_datetime" arity="1"/> <fsummary>Convert now to date and time</fsummary> - <type> - <v>Now -- see erlang:now/0</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns Universal Coordinated Time (UTC) converted from the return value from <c>erlang:now()</c>.</p> </desc> </func> <func> - <name>seconds_to_daystime(Seconds) -> {Days, Time}</name> + <name name="seconds_to_daystime" arity="1"/> <fsummary>Compute days and time from seconds</fsummary> - <type> - <v>Seconds = Days = int()</v> - <v>Time = time()</v> - </type> <desc> <p>This function transforms a given number of seconds into days, - hours, minutes, and seconds. The <c>Time</c> part is always - non-negative, but <c>Days</c> is negative if the argument - <c>Seconds</c> is.</p> + hours, minutes, and seconds. The <c><anno>Time</anno></c> part is always + non-negative, but <c><anno>Days</anno></c> is negative if the argument + <c><anno>Seconds</anno></c> is.</p> </desc> </func> <func> - <name>seconds_to_time(Seconds) -> Time</name> + <name name="seconds_to_time" arity="1"/> <fsummary>Compute time from seconds</fsummary> - <type> - <v>Seconds = int() < 86400</v> - <v>Time = time()</v> - </type> + <type name="secs_per_day"/> <desc> <p>This function computes the time from the given number of - seconds. <c>Seconds</c> must be less than the number of + seconds. <c><anno>Seconds</anno></c> must be less than the number of seconds per day (86400).</p> </desc> </func> <func> - <name>time_difference(T1, T2) -> {Days, Time}</name> + <name name="time_difference" arity="2"/> <fsummary>Compute the difference between two times (deprecated)</fsummary> <desc> - <p>This function returns the difference between two <c>{Date, Time}</c> tuples. <c>T2</c> should refer to an epoch later - than <c>T1</c>.</p> + <p>This function returns the difference between two <c>{Date, Time}</c> tuples. <c><anno>T2</anno></c> should refer to an epoch later + than <c><anno>T1</anno></c>.</p> <warning> <p>This function is obsolete. Use the conversion functions for gregorian days and seconds instead.</p> @@ -324,24 +320,17 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>time_to_seconds(Time) -> Seconds</name> + <name name="time_to_seconds" arity="1"/> <fsummary>Compute the number of seconds since midnight up to the given time</fsummary> - <type> - <v>Time = time()</v> - <v>Seconds = int()</v> - </type> + <type name="secs_per_day"/> <desc> <p>This function computes the number of seconds since midnight up to the specified time.</p> </desc> </func> <func> - <name>universal_time() -> {Date, Time}</name> + <name name="universal_time" arity="0"/> <fsummary>Compute universal time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns the Universal Coordinated Time (UTC) reported by the underlying operating system. Local time is @@ -349,25 +338,22 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>universal_time_to_local_time({Date1, Time1}) -> {Date2, Time2}</name> + <name name="universal_time_to_local_time" arity="1"/> <fsummary>Convert from universal time to local time</fsummary> - <type> - <v>Date1 = Date2 = date()</v> - <v>Time1 = Time2 = time()</v> - </type> <desc> <p>This function converts from Universal Coordinated Time (UTC) - to local time. <c>Date1</c> must refer to a date after Jan 1, + to local time. <c><anno>DateTime</anno></c> must refer to a date after Jan 1, 1970.</p> </desc> </func> <func> - <name>valid_date(Date) -> bool()</name> - <name>valid_date(Year, Month, Day) -> bool()</name> + <name name="valid_date" arity="1"/> + <name name="valid_date" arity="3"/> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <fsummary>Check if a date is valid</fsummary> - <type> - <v>Date = date()</v> - </type> <desc> <p>This function checks if a date is a valid.</p> </desc> diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml index b002af6616..2512c84e18 100644 --- a/lib/stdlib/doc/src/dets.xml +++ b/lib/stdlib/doc/src/dets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -100,50 +100,99 @@ the process with the error tuple). If given badly formed arguments, all functions exit the process with a <c>badarg</c> message.</p> - <p><em>Types</em></p> - <pre> -access() = read | read_write -auto_save() = infinity | int() -bindings_cont() = tuple() -bool() = true | false -file() = string() -int() = integer() >= 0 -keypos() = integer() >= 1 -name() = atom() | reference() -no_slots() = integer() >= 0 | default -object() = tuple() -object_cont() = tuple() -select_cont() = tuple() -type() = bag | duplicate_bag | set -version() = 8 | 9 | default </pre> </description> + <datatypes> + <datatype> + <name name="access"/> + </datatype> + <datatype> + <name name="auto_save"/> + </datatype> + <datatype> + <name name="bindings_cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#match/1"> + <c>match/1</c></seealso> and <seealso marker="#match/3"> + <c>match/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#bchunk/2"> + <c>bchunk/2</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="keypos"/> + </datatype> + <datatype> + <name name="match_spec"/> + <desc> + <p>Match specifications, see the <seealso + marker="erts:match_spec">match specification</seealso> + documentation in the ERTS User's Guide and <seealso + marker="ms_transform">ms_transform(3).</seealso></p> + </desc> + </datatype> + <datatype> + <name name="no_slots"/> + </datatype> + <datatype> + <name name="object"/> + </datatype> + <datatype> + <name name="object_cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#match_object/1"> + <c>match_object/1</c></seealso> and <seealso marker="#match_object/3"> + <c>match_object/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="pattern"/> + <desc> + <p>See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> + </desc> + </datatype> + <datatype> + <name name="select_cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#select/1"> + <c>select/1</c></seealso> and <seealso marker="#select/3"> + <c>select/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="tab_name"/> + </datatype> + <datatype> + <name name="type"/> + </datatype> + <datatype> + <name name="version"/> + </datatype> + </datatypes> <funcs> <func> - <name>all() -> [Name]</name> + <name name="all" arity="0"/> <fsummary>Return a list of the names of all open Dets tables on this node.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Returns a list of the names of all open tables on this node.</p> </desc> </func> <func> - <name>bchunk(Name, Continuation) -> {Continuation2, Data} | '$end_of_table' | {error, Reason}</name> + <name name="bchunk" arity="2"/> <fsummary>Return a chunk of objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Continuation = start | cont()</v> - <v>Continuation2 = cont()</v> - <v>Data = binary() | tuple()</v> - </type> <desc> <p>Returns a list of objects stored in a table. The exact representation of the returned objects is not public. The lists of data can be used for initializing a table by giving the value <c>bchunk</c> to the <c>format</c> option of the - <c>init_table/3</c> function. The Mnesia application uses this + <seealso marker="#init_table/3"><c>init_table/3</c></seealso> + function. The Mnesia application uses this function for copying open tables.</p> <p>Unless the table is protected using <c>safe_fixtable/2</c>, calls to <c>bchunk/2</c> may not work as expected if @@ -151,24 +200,23 @@ version() = 8 | 9 | default </pre> <p>The first time <c>bchunk/2</c> is called, an initial continuation, the atom <c>start</c>, must be provided.</p> <p>The <c>bchunk/2</c> function returns a tuple - <c>{Continuation2, Data}</c>, where <c>Data</c> is a list of - objects. <c>Continuation2</c> is another continuation which is + <c>{<anno>Continuation2</anno>, <anno>Data</anno>}</c>, + where <c><anno>Data</anno></c> is a list of + objects. <c><anno>Continuation2</anno></c> is another continuation + which is to be passed on to a subsequent call to <c>bchunk/2</c>. With a series of calls to <c>bchunk/2</c> it is possible to extract all objects of the table. </p> <p><c>bchunk/2</c> returns <c>'$end_of_table'</c> when all - objects have been returned, or <c>{error, Reason}</c> if an - error occurs. + objects have been returned, or <c>{error, <anno>Reason</anno>}</c> + if an error occurs. </p> </desc> </func> <func> - <name>close(Name) -> ok | {error, Reason} </name> + <name name="close" arity="1"/> <fsummary>Close a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Closes a table. Only processes that have opened a table are allowed to close it. @@ -180,22 +228,16 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>delete(Name, Key) -> ok | {error, Reason}</name> + <name name="delete" arity="2"/> <fsummary>Delete all objects with a given key from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Deletes all objects with the key <c>Key</c> from the table - <c>Name</c>.</p> + <p>Deletes all objects with the key <c><anno>Key</anno></c> from + the table <c><anno>Name</anno></c>.</p> </desc> </func> <func> - <name>delete_all_objects(Name) -> ok | {error, Reason}</name> + <name name="delete_all_objects" arity="1"/> <fsummary>Delete all objects from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Deletes all objects from a table in almost constant time. However, if the table if fixed, <c>delete_all_objects(T)</c> @@ -203,12 +245,8 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>delete_object(Name, Object) -> ok | {error, Reason}</name> + <name name="delete_object" arity="2"/> <fsummary>Delete a given object from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> <p>Deletes all instances of a given object from a table. If a table is of type <c>bag</c> or <c>duplicate_bag</c>, the @@ -218,18 +256,15 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>first(Name) -> Key | '$end_of_table'</name> + <name name="first" arity="1"/> <fsummary>Return the first key stored in a Dets table.</fsummary> - <type> - <v>Key = term()</v> - <v>Name = name()</v> - </type> <desc> - <p>Returns the first key stored in the table <c>Name</c> + <p>Returns the first key stored in the table <c><anno>Name</anno></c> according to the table's internal order, or <c>'$end_of_table'</c> if the table is empty.</p> <p>Unless the table is protected using <c>safe_fixtable/2</c>, - subsequent calls to <c>next/2</c> may not work as expected if + subsequent calls to <seealso marker="#next/2"><c>next/2</c></seealso> + may not work as expected if concurrent updates are made to the table.</p> <p>Should an error occur, the process is exited with an error tuple <c>{error, Reason}</c>. The reason for not returning the @@ -243,107 +278,78 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>foldl(Function, Acc0, Name) -> Acc1 | {error, Reason}</name> - <fsummary>Fold a function over a Dets table.</fsummary> - <type> - <v>Function = fun(Object, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> - <desc> - <p>Calls <c>Function</c> on successive elements of the table - <c>Name</c> together with an extra argument <c>AccIn</c>. The - order in which the elements of the table are traversed is - unspecified. <c>Function</c> must return a new accumulator - which is passed to the next call. <c>Acc0</c> is returned if - the table is empty.</p> - </desc> - </func> - <func> - <name>foldr(Function, Acc0, Name) -> Acc1 | {error, Reason}</name> + <name name="foldl" arity="3"/> + <name name="foldr" arity="3"/> <fsummary>Fold a function over a Dets table.</fsummary> - <type> - <v>Function = fun(Object, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> - <p>Calls <c>Function</c> on successive elements of the table - <c>Name</c> together with an extra argument <c>AccIn</c>. The + <p>Calls <c><anno>Function</anno></c> on successive elements of + the table <c><anno>Name</anno></c> together with an extra argument + <c>AccIn</c>. The order in which the elements of the table are traversed is - unspecified. <c>Function</c> must return a new accumulator - which is passed to the next call. <c>Acc0</c> is returned if + unspecified. <c><anno>Function</anno></c> must return a new + accumulator which is passed to the next call. + <c><anno>Acc0</anno></c> is returned if the table is empty.</p> </desc> </func> <func> - <name>from_ets(Name, EtsTab) -> ok | {error, Reason}</name> + <name name="from_ets" arity="2"/> <fsummary>Replace the objects of a Dets table with the objects of an Ets table.</fsummary> - <type> - <v>Name = name()</v> - <v>EtsTab = - see ets(3) -</v> - </type> <desc> - <p>Deletes all objects of the table <c>Name</c> and then - inserts all the objects of the Ets table <c>EtsTab</c>. The - order in which the objects are inserted is not specified. + <p>Deletes all objects of the table <c><anno>Name</anno></c> and then + inserts all the objects of the Ets table <c><anno>EtsTab</anno></c>. + The order in which the objects are inserted is not specified. Since <c>ets:safe_fixtable/2</c> is called the Ets table must be public or owned by the calling process.</p> </desc> </func> <func> - <name>info(Name) -> InfoList | undefined</name> + <name name="info" arity="1"/> <fsummary>Return information about a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>InfoList = [{Item, Value}]</v> - </type> <desc> - <p>Returns information about the table <c>Name</c> as a list of - <c>{Item, Value}</c> tuples:</p> + <p>Returns information about the table <c><anno>Name</anno></c> + as a list of tuples:</p> <list type="bulleted"> <item> - <p><c>{file_size, int()}</c>, the size of the file in + <p><c>{file_size, integer() >= 0}</c>, the size of the file in bytes.</p> </item> <item> - <p><c>{filename, file()}</c>, the name of the file - where objects are stored.</p> + <p><c>{filename, <seealso marker="file#type-name">file:name()</seealso>}</c>, + the name of the file where objects are stored.</p> </item> <item> - <p><c>{keypos, keypos()}</c>, the position of the - key.</p> + <p><c>{keypos, <seealso marker="#type-keypos">keypos()</seealso>} + </c>, the position of the key.</p> </item> <item> - <p><c>{size, int()}</c>, the number of objects stored + <p><c>{size, integer() >= 0}</c>, the number of objects stored in the table.</p> </item> <item> - <p><c>{type, type()}</c>, the type of the table.</p> + <p><c>{type, <seealso marker="#type-type">type()</seealso>}</c>, + the type of the table.</p> </item> </list> </desc> </func> <func> - <name>info(Name, Item) -> Value | undefined</name> + <name name="info" arity="2"/> <fsummary>Return the information associated with a given item for a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Returns the information associated with <c>Item</c> for the - table <c>Name</c>. In addition to the <c>{Item, Value}</c> + <p>Returns the information associated with <c><anno>Item</anno></c> + for the table <c><anno>Name</anno></c>. + In addition to the <c>{<anno>Item</anno>, <anno>Value</anno>}</c> pairs defined for <c>info/1</c>, the following items are allowed:</p> <list type="bulleted"> <item> - <p><c>{access, access()}</c>, the access mode.</p> + <p><c>{access, <seealso marker="#type-access">access()</seealso>} + </c>, the access mode.</p> </item> <item> - <p><c>{auto_save, auto_save()}</c>, the auto save - interval.</p> + <p><c>{auto_save, <seealso marker="#type-auto_save"> + auto_save()</seealso>}</c>, the auto save interval.</p> </item> <item> <p><c>{bchunk_format, binary()}</c>, an opaque binary @@ -362,21 +368,22 @@ version() = 8 | 9 | default </pre> <c>erlang:phash2/1</c> BIF is used.</p> </item> <item> - <p><c>{memory, int()}</c>, the size of the file in + <p><c>{memory, integer() >= 0}</c>, the size of the file in bytes. The same value is associated with the item <c>file_size</c>.</p> </item> <item> - <p><c>{no_keys, int()}</c>, the number of different + <p><c>{no_keys, integer >= 0()}</c>, the number of different keys stored in the table. Only available for version 9 tables.</p> </item> <item> - <p><c>{no_objects, int()}</c>, the number of objects + <p><c>{no_objects, integer >= 0()}</c>, the number of objects stored in the table.</p> </item> <item> - <p><c>{no_slots, {Min, Used, Max}}</c>, the number of + <p><c>{no_slots, {</c>Min<c>, </c>Used<c>, </c>Max<c>}}</c>, + the number of slots of the table. <c>Min</c> is the minimum number of slots, <c>Used</c> is the number of currently used slots, and <c>Max</c> is the maximum number of slots. Only @@ -387,7 +394,7 @@ version() = 8 | 9 | default </pre> handles requests to the Dets table.</p> </item> <item> - <p><c>{ram_file, bool()}</c>, whether the table is + <p><c>{ram_file, boolean()}</c>, whether the table is kept in RAM.</p> </item> <item> @@ -399,32 +406,28 @@ version() = 8 | 9 | default </pre> table is not fixed, SafeFixed is the atom <c>false</c>.</p> </item> <item> - <p><c>{version, int()}</c>, the version of the format - of the table.</p> + <p><c>{version, integer()</c>, the version of the format of + the table.</p> </item> </list> </desc> </func> <func> - <name>init_table(Name, InitFun [, Options]) -> ok | {error, Reason}</name> + <name name="init_table" arity="2"/> + <name name="init_table" arity="3"/> <fsummary>Replace all objects of a Dets table.</fsummary> - <type> - <v>Name = atom()</v> - <v>InitFun = fun(Arg) -> Res</v> - <v>Arg = read | close</v> - <v>Res = end_of_input | {[object()], InitFun} | {Data, InitFun} | term()</v> - <v>Data = binary() | tuple()</v> - </type> <desc> - <p>Replaces the existing objects of the table <c>Name</c> with - objects created by calling the input function <c>InitFun</c>, + <p>Replaces the existing objects of the table <c><anno>Name</anno></c> + with objects created by calling the input function + <c><anno>InitFun</anno></c>, see below. The reason for using this function rather than calling <c>insert/2</c> is that of efficiency. It should be noted that the input functions are called by the process that handles requests to the Dets table, not by the calling process.</p> <p>When called with the argument <c>read</c> the function - <c>InitFun</c> is assumed to return <c>end_of_input</c> when + <c><anno>InitFun</anno></c> is assumed to return + <c>end_of_input</c> when there is no more input, or <c>{Objects, Fun}</c>, where <c>Objects</c> is a list of objects and <c>Fun</c> is a new input function. Any other value Value is returned as an error @@ -448,7 +451,8 @@ version() = 8 | 9 | default </pre> item <c>no_slots</c>. See also the <c>min_no_slots</c> option below. </p> - <p>The <c>Options</c> argument is a list of <c>{Key, Val}</c> + <p>The <c><anno>Options</anno></c> argument is a list of + <c>{Key, Val}</c> tuples where the following values are allowed:</p> <list type="bulleted"> <item> @@ -461,87 +465,68 @@ version() = 8 | 9 | default </pre> </item> <item> <p><c>{format, Format}</c>. Specifies the format of the - objects returned by the function <c>InitFun</c>. If + objects returned by the function <c><anno>InitFun</anno></c>. If <c>Format</c> is <c>term</c> (the default), - <c>InitFun</c> is assumed to return a list of tuples. If - <c>Format</c> is <c>bchunk</c>, <c>InitFun</c> is - assumed to return <c>Data</c> as returned by - <c>bchunk/2</c>. This option overrides the + <c><anno>InitFun</anno></c> is assumed to return a list of tuples. If + <c>Format</c> is <c>bchunk</c>, <c><anno>InitFun</anno></c> is + assumed to return <c><anno>Data</anno></c> as returned by + <seealso marker="#bchunk/2"><c>bchunk/2</c></seealso>. + This option overrides the <c>min_no_slots</c> option.</p> </item> </list> </desc> </func> <func> - <name>insert(Name, Objects) -> ok | {error, Reason}</name> + <name name="insert" arity="2"/> <fsummary>Insert one or more objects into a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Objects = object() | [object()]</v> - </type> <desc> - <p>Inserts one or more objects into the table <c>Name</c>. If - there already exists an object with a key matching the key of + <p>Inserts one or more objects into the table <c><anno>Name</anno></c>. + If there already exists an object with a key matching the key of some of the given objects and the table type is <c>set</c>, the old object will be replaced.</p> </desc> </func> <func> - <name>insert_new(Name, Objects) -> Bool</name> + <name name="insert_new" arity="2"/> <fsummary>Insert one or more objects into a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Objects = object() | [object()]</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Inserts one or more objects into the table <c>Name</c>. If - there already exists some object with a key matching the key + <p>Inserts one or more objects into the table <c><anno>Name</anno></c>. + If there already exists some object with a key matching the key of any of the given objects the table is not updated and <c>false</c> is returned, otherwise the objects are inserted and <c>true</c> returned.</p> </desc> </func> <func> - <name>is_compatible_bchunk_format(Name, BchunkFormat) -> Bool</name> + <name name="is_compatible_bchunk_format" arity="2"/> <fsummary>Test compatibility of a table's chunk data.</fsummary> - <type> - <v>Name = name()</v> - <v>BchunkFormat = binary()</v> - <v>Bool = bool()</v> - </type> <desc> <p>Returns <c>true</c> if it would be possible to initialize - the table <c>Name</c>, using <c>init_table/3</c> with the + the table <c><anno>Name</anno></c>, using + <seealso marker="#init_table/3"><c>init_table/3</c></seealso> + with the option <c>{format, bchunk}</c>, with objects read with - <c>bchunk/2</c> from some table <c>T</c> such that calling + <seealso marker="#bchunk/2"><c>bchunk/2</c></seealso> from some + table <c>T</c> such that calling <c>info(T, bchunk_format)</c> returns <c>BchunkFormat</c>.</p> </desc> </func> <func> - <name>is_dets_file(FileName) -> Bool | {error, Reason}</name> + <name name="is_dets_file" arity="1"/> <fsummary>Test for a Dets table.</fsummary> - <type> - <v>FileName = file()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if the file <c>FileName</c> is a Dets - table, <c>false</c> otherwise.</p> + <p>Returns <c>true</c> if the file <c><anno>Filename</anno></c> + is a Dets table, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>lookup(Name, Key) -> [Object] | {error, Reason}</name> + <name name="lookup" arity="2"/> <fsummary>Return all objects with a given key stored in a Dets table.</fsummary> - <type> - <v>Key = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> - <p>Returns a list of all objects with the key <c>Key</c> - stored in the table <c>Name</c>. For example:</p> + <p>Returns a list of all objects with the key <c><anno>Key</anno></c> + stored in the table <c><anno>Name</anno></c>. For example:</p> <pre> 2> <input>dets:open_file(abc, [{type, bag}]).</input> {ok,abc} @@ -562,71 +547,57 @@ ok </desc> </func> <func> - <name>match(Continuation) -> {[Match], Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="match" arity="1"/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Continuation = Continuation2 = bindings_cont()</v> - <v>Match = [term()]</v> - </type> <desc> <p>Matches some objects stored in a table and returns a non-empty list of the bindings that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched are all defined by - <c>Continuation</c>, which has been returned by a prior call - to <c>match/1</c> or <c>match/3</c>.</p> + <c><anno>Continuation</anno></c>, which has been returned by a prior + call to <c>match/1</c> or <c>match/3</c>.</p> <p>When all objects of the table have been matched, <c>'$end_of_table'</c> is returned.</p> </desc> </func> <func> - <name>match(Name, Pattern) -> [Match] | {error, Reason}</name> + <name name="match" arity="2"/> <fsummary>Match the objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>Match = [term()]</v> - </type> <desc> - <p>Returns for each object of the table <c>Name</c> that - matches <c>Pattern</c> a list of bindings in some unspecified - order. See <seealso marker="ets">ets(3)</seealso> for a + <p>Returns for each object of the table <c><anno>Name</anno></c> that + matches <c><anno>Pattern</anno></c> a list of bindings in some unspecified + order. See <seealso marker="ets#match/2">ets:match/2</seealso> for a description of patterns. If the keypos'th element of - <c>Pattern</c> is unbound, all objects of the table are + <c><anno>Pattern</anno></c> is unbound, all objects of the table are matched. If the keypos'th element is bound, only the objects with the right key are matched.</p> </desc> </func> <func> - <name>match(Name, Pattern, N) -> {[Match], Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="match" arity="3"/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>N = default | int()</v> - <v>Match = [term()]</v> - <v>Continuation = bindings_cont()</v> - </type> <desc> - <p>Matches some or all objects of the table <c>Name</c> and + <p>Matches some or all objects of the table <c><anno>Name</anno></c> and returns a non-empty list of the bindings that match - <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of - patterns.</p> + <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> <p>A tuple of the bindings and a continuation is returned, unless the table is empty, in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling - <c>match/1</c>.</p> - <p>If the keypos'th element of <c>Pattern</c> is bound, all - objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, <c>N</c> + <seealso marker="#match/1"><c>match/1</c></seealso>.</p> + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound, + all objects of the table are matched. If the keypos'th element is + unbound, all objects of the table are matched, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving <c>N</c> the value <c>default</c>, is to let the number + giving <c><anno>N</anno></c> the value <c>default</c>, + is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all objects with the same + <c><anno>Name</anno></c> is a version 9 table, all objects with the same key are always matched at the same time which implies that - more than N objects may sometimes be matched. + more than <anno>N</anno> objects may sometimes be matched. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>match/3</c>, or @@ -634,15 +605,11 @@ ok </desc> </func> <func> - <name>match_delete(Name, Pattern) -> ok | {error, Reason}</name> + <name name="match_delete" arity="2"/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - </type> <desc> - <p>Deletes all objects that match <c>Pattern</c> from the - table <c>Name</c>. + <p>Deletes all objects that match <c><anno>Pattern</anno></c> from the + table <c><anno>Name</anno></c>. See <seealso marker="ets#match/2">ets:match/2</seealso> for a description of patterns.</p> <p>If the keypos'th element of <c>Pattern</c> is bound, @@ -650,17 +617,13 @@ ok </desc> </func> <func> - <name>match_object(Continuation) -> {[Object], Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="match_object" arity="1"/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Continuation = Continuation2 = object_cont()</v> - <v>Object = object()</v> - </type> <desc> <p>Returns a non-empty list of some objects stored in a table that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched - are all defined by <c>Continuation</c>, which has been + are all defined by <c><anno>Continuation</anno></c>, which has been returned by a prior call to <c>match_object/1</c> or <c>match_object/3</c>.</p> <p>When all objects of the table have been matched, @@ -668,20 +631,17 @@ ok </desc> </func> <func> - <name>match_object(Name, Pattern) -> [Object] | {error, Reason}</name> + <name name="match_object" arity="2"/> <fsummary>Match the objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>Object = object()</v> - </type> <desc> - <p>Returns a list of all objects of the table <c>Name</c> that - match <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of patterns. + <p>Returns a list of all objects of the table <c><anno>Name</anno></c> that + match <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns. </p> - <p>If the keypos'th element of <c>Pattern</c> is + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is unbound, all objects of the table are matched. If the - keypos'th element of <c>Pattern</c> is bound, only the + keypos'th element of <c><anno>Pattern</anno></c> is bound, only the objects with the right key are matched.</p> <p>Using the <c>match_object</c> functions for traversing all objects of a table is more efficient than calling @@ -689,34 +649,28 @@ ok </desc> </func> <func> - <name>match_object(Name, Pattern, N) -> {[Object], Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="match_object" arity="3"/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>N = default | int()</v> - <v>Object = object()</v> - <v>Continuation = object_cont()</v> - </type> <desc> - <p>Matches some or all objects stored in the table <c>Name</c> + <p>Matches some or all objects stored in the table <c><anno>Name</anno></c> and returns a non-empty list of the objects that match - <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of - patterns.</p> + <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> <p>A list of objects and a continuation is returned, unless the table is empty, in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling <c>match_object/1</c>.</p> - <p>If the keypos'th element of <c>Pattern</c> is bound, all + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound, all objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, <c>N</c> + unbound, all objects of the table are matched, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving <c>N</c> the value <c>default</c>, is to let the number + giving <c><anno>N</anno></c> the value <c>default</c>, is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all matching objects with + <c><anno>Name</anno></c> is a version 9 table, all matching objects with the same key are always returned in the same reply which - implies that more than N objects may sometimes be returned. + implies that more than <anno>N</anno> objects may sometimes be returned. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>match_object/3</c>, @@ -724,43 +678,31 @@ ok </desc> </func> <func> - <name>member(Name, Key) -> Bool | {error, Reason}</name> + <name name="member" arity="2"/> <fsummary>Test for occurrence of a key in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key = term()</v> - <v>Bool = bool()</v> - </type> <desc> <p>Works like <c>lookup/2</c>, but does not return the objects. The function returns <c>true</c> if one or more - elements of the table has the key <c>Key</c>, <c>false</c> + elements of the table has the key <c><anno>Key</anno></c>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>next(Name, Key1) -> Key2 | '$end_of_table'</name> + <name name="next" arity="2"/> <fsummary>Return the next key in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key1 = Key2 = term()</v> - </type> <desc> - <p>Returns the key following <c>Key1</c> in the table - <c>Name</c> according to the table's internal order, or + <p>Returns the key following <c><anno>Key1</anno></c> in the table + <c><anno>Name</anno></c> according to the table's internal order, or <c>'$end_of_table'</c> if there is no next key.</p> <p>Should an error occur, the process is exited with an error tuple <c>{error, Reason}</c>.</p> - <p>Use <c>first/1</c> to find the first key in the table.</p> + <p>Use <seealso marker="#first/1"><c>first/1</c></seealso> to find + the first key in the table.</p> </desc> </func> <func> - <name>open_file(Filename) -> {ok, Reference} | {error, Reason}</name> + <name name="open_file" arity="1"/> <fsummary>Open an existing Dets table.</fsummary> - <type> - <v>FileName = file()</v> - <v>Reference = reference()</v> - </type> <desc> <p>Opens an existing table. If the table has not been properly closed, it will be repaired. The returned reference is to be @@ -769,15 +711,12 @@ ok </desc> </func> <func> - <name>open_file(Name, Args) -> {ok, Name} | {error, Reason}</name> + <name name="open_file" arity="2"/> <fsummary>Open a Dets table.</fsummary> - <type> - <v>Name = atom()</v> - </type> <desc> <p>Opens a table. An empty Dets table is created if no file exists.</p> - <p>The atom <c>Name</c> is the name of the table. The table + <p>The atom <c><anno>Name</anno></c> is the name of the table. The table name must be provided in all subsequent operations on the table. The name can be used by other processes as well, and several process can share one table. @@ -786,18 +725,20 @@ ok name and arguments, then the table will have two users. If one user closes the table, it still remains open until the second user closes the table.</p> - <p>The <c>Args</c> argument is a list of <c>{Key, Val}</c> + <p>The <c><anno>Args</anno></c> argument is a list of <c>{Key, Val}</c> tuples where the following values are allowed:</p> <list type="bulleted"> <item> - <p><c>{access, access()}</c>. It is possible to open + <p><c>{access, <seealso marker="#type-access"> + access()</seealso>}</c>. It is possible to open existing tables in read-only mode. A table which is opened in read-only mode is not subjected to the automatic file reparation algorithm if it is later opened after a crash. The default value is <c>read_write</c>.</p> </item> <item> - <p><c>{auto_save, auto_save()}</c>, the auto save + <p><c>{auto_save, <seealso marker="#type-auto_save"> + auto_save()</seealso>}</c>, the auto save interval. If the interval is an integer <c>Time</c>, the table is flushed to disk whenever it is not accessed for <c>Time</c> milliseconds. A table that has been flushed @@ -807,15 +748,18 @@ ok is 180000 (3 minutes).</p> </item> <item> - <p><c>{estimated_no_objects, int()}</c>. Equivalent to the + <p><c>{estimated_no_objects, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>. Equivalent to the <c>min_no_slots</c> option.</p> </item> <item> - <p><c>{file, file()}</c>, the name of the file to be + <p><c>{file, <seealso marker="file#type-name"> + file:name()</seealso>}</c>, the name of the file to be opened. The default value is the name of the table.</p> </item> <item> - <p><c>{max_no_slots, no_slots()}</c>, the maximum number + <p><c>{max_no_slots, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>, the maximum number of slots that will be used. The default value as well as the maximal value is 32 M. Note that a higher value may increase the fragmentation of the table, and conversely, @@ -824,14 +768,16 @@ ok 9 tables.</p> </item> <item> - <p><c>{min_no_slots, no_slots()}</c>. Application + <p><c>{min_no_slots, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>. Application performance can be enhanced with this flag by specifying, when the table is created, the estimated number of different keys that will be stored in the table. The default value as well as the minimum value is 256.</p> </item> <item> - <p><c>{keypos, keypos()}</c>, the position of the + <p><c>{keypos, <seealso marker="#type-keypos"> + keypos()</seealso>}</c>, the position of the element of each object to be used as key. The default value is 1. The ability to explicitly state the key position is most convenient when we want to store Erlang @@ -839,7 +785,7 @@ ok name of the record type.</p> </item> <item> - <p><c>{ram_file, bool()}</c>, whether the table is to + <p><c>{ram_file, boolean()}</c>, whether the table is to be kept in RAM. Keeping the table in RAM may sound like an anomaly, but can enhance the performance of applications which open a table, insert a set of objects, and then @@ -849,7 +795,7 @@ ok </item> <item> <p><c>{repair, Value}</c>. <c>Value</c> can be either - a <c>bool()</c> or the atom <c>force</c>. The flag + a <c>boolean()</c> or the atom <c>force</c>. The flag specifies whether the Dets server should invoke the automatic file reparation algorithm. The default is <c>true</c>. If <c>false</c> is specified, there is no @@ -868,11 +814,12 @@ ok already open.</p> </item> <item> - <p><c>{type, type()}</c>, the type of the table. The - default value is <c>set</c>.</p> + <p><c>{type, <seealso marker="#type-type">type()</seealso>}</c>, + the type of the table. The default value is <c>set</c>.</p> </item> <item> - <p><c>{version, version()}</c>, the version of the format + <p><c>{version, <seealso marker="#type-version"> + version()</seealso>}</c>, the version of the format used for the table. The default value is <c>9</c>. Tables on the format used before OTP R8 can be created by giving the value <c>8</c>. A version 8 table can be converted to @@ -883,12 +830,8 @@ ok </desc> </func> <func> - <name>pid2name(Pid) -> {ok, Name} | undefined</name> + <name name="pid2name" arity="1"/> <fsummary>Return the name of the Dets table handled by a pid.</fsummary> - <type> - <v>Name = name()</v> - <v>Pid = pid()</v> - </type> <desc> <p>Returns the name of the table given the pid of a process that handles requests to a table, or <c>undefined</c> if @@ -897,12 +840,8 @@ ok </desc> </func> <func> - <name>repair_continuation(Continuation, MatchSpec) -> Continuation2</name> + <name name="repair_continuation" arity="2"/> <fsummary>Repair a continuation from select/1 or select/3.</fsummary> - <type> - <v>Continuation = Continuation2 = select_cont()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>This function can be used to restore an opaque continuation returned by <c>select/3</c> or <c>select/1</c> if the @@ -932,14 +871,11 @@ ok </desc> </func> <func> - <name>safe_fixtable(Name, Fix)</name> + <name name="safe_fixtable" arity="2"/> <fsummary>Fix a Dets table for safe traversal.</fsummary> - <type> - <v>Name = name()</v> - <v>Fix = bool()</v> - </type> <desc> - <p>If <c>Fix</c> is <c>true</c>, the table <c>Name</c> is + <p>If <c><anno>Fix</anno></c> is <c>true</c>, the table + <c><anno>Name</anno></c> is fixed (once more) by the calling process, otherwise the table is released. The table is also released when a fixing process terminates. @@ -961,17 +897,13 @@ ok </desc> </func> <func> - <name>select(Continuation) -> {Selection, Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="select" arity="1"/> <fsummary>Apply a match specification to some objects stored in a Dets table.</fsummary> - <type> - <v>Continuation = Continuation2 = select_cont()</v> - <v>Selection = [term()]</v> - </type> <desc> <p>Applies a match specification to some objects stored in a table and returns a non-empty list of the results. The table, the match specification, and the number of objects - that are matched are all defined by <c>Continuation</c>, + that are matched are all defined by <c><anno>Continuation</anno></c>, which has been returned by a prior call to <c>select/1</c> or <c>select/3</c>.</p> <p>When all objects of the table have been matched, @@ -979,20 +911,15 @@ ok </desc> </func> <func> - <name>select(Name, MatchSpec) -> Selection | {error, Reason}</name> + <name name="select" arity="2"/> <fsummary>Apply a match specification to all objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>Selection = [term()]</v> - </type> <desc> <p>Returns the results of applying the match specification - <c>MatchSpec</c> to all or some objects stored in the table - <c>Name</c>. The order of the objects is not specified. See + <c><anno>MatchSpec</anno></c> to all or some objects stored in the table + <c><anno>Name</anno></c>. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is unbound, the match specification is applied to all objects of the table. If the keypos'th element is bound, the match specification is applied to the objects with the right key(s) @@ -1004,19 +931,12 @@ ok </desc> </func> <func> - <name>select(Name, MatchSpec, N) -> {Selection, Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="select" arity="3"/> <fsummary>Apply a match specification to the first chunk of objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>N = default | int()</v> - <v>Selection = [term()]</v> - <v>Continuation = select_cont()</v> - </type> <desc> <p>Returns the results of applying the match specification - <c>MatchSpec</c> to some or all objects stored in the table - <c>Name</c>. The order of the objects is not specified. See + <c><anno>MatchSpec</anno></c> to some or all objects stored in the table + <c><anno>Name</anno></c>. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.</p> <p>A tuple of the results of applying the match specification @@ -1024,18 +944,18 @@ ok in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling <c>select/1</c>.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is bound, the + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is bound, the match specification is applied to all objects of the table with the right key(s). If the keypos'th element of - <c>MatchSpec</c> is unbound, the match specification is - applied to all objects of the table, <c>N</c> objects at a + <c><anno>MatchSpec</anno></c> is unbound, the match specification is + applied to all objects of the table, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by giving - <c>N</c> the value <c>default</c>, is to let the number of + <c><anno>N</anno></c> the value <c>default</c>, is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all objects with the same + <c><anno>Name</anno></c> is a version 9 table, all objects with the same key are always handled at the same time which implies that the - match specification may be applied to more than N objects. + match specification may be applied to more than <anno>N</anno> objects. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>select/3</c>, or @@ -1043,48 +963,35 @@ ok </desc> </func> <func> - <name>select_delete(Name, MatchSpec) -> N | {error, Reason}</name> + <name name="select_delete" arity="2"/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>N = int()</v> - </type> <desc> - <p>Deletes each object from the table <c>Name</c> such that - applying the match specification <c>MatchSpec</c> to the + <p>Deletes each object from the table <c><anno>Name</anno></c> such that + applying the match specification <c><anno>MatchSpec</anno></c> to the object returns the value <c>true</c>. See the ERTS User's Guide for a description of match specifications. Returns the number of deleted objects.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is bound, the match specification is applied to the objects with the right key(s) only.</p> </desc> </func> <func> - <name>slot(Name, I) -> '$end_of_table' | [Object] | {error, Reason}</name> + <name name="slot" arity="2"/> <fsummary>Return the list of objects associated with a slot of a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>I = int()</v> - <v>Object = object()</v> - </type> <desc> <p>The objects of a table are distributed among slots, starting with slot <c>0</c> and ending with slot n. This function returns the list of objects associated with slot - <c>I</c>. If <c>I</c> is greater than n <c>'$end_of_table'</c> - is returned.</p> + <c><anno>I</anno></c>. If <c><anno>I</anno></c> is greater than n + <c>'$end_of_table'</c> is returned.</p> </desc> </func> <func> - <name>sync(Name) -> ok | {error, Reason}</name> + <name name="sync" arity="1"/> <fsummary>Ensure that all updates made to a Dets table are written to disk.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Ensures that all updates made to the table <c>Name</c> are + <p>Ensures that all updates made to the table <c><anno>Name</anno></c> are written to disk. This also applies to tables which have been opened with the <c>ram_file</c> flag set to <c>true</c>. In this case, the contents of the RAM file are flushed to @@ -1095,25 +1002,17 @@ ok </desc> </func> <func> - <name>table(Name [, Options]) -> QueryHandle</name> + <name name="table" arity="1"/> + <name name="table" arity="2"/> <fsummary>Return a QLC query handle.</fsummary> - <type> - <v>Name = name()</v> - <v>QueryHandle = - a query handle, see qlc(3) -</v> - <v>Options = [Option] | Option</v> - <v>Option = {n_objects, Limit} | {traverse, TraverseMethod}</v> - <v>Limit = default | integer() >= 1</v> - <v>TraverseMethod = first_next | select | {select, MatchSpec}</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> - <p> <marker id="qlc_table"></marker> -Returns a QLC (Query List + <p><marker id="qlc_table"></marker> + Returns a QLC (Query List Comprehension) query handle. The module <c>qlc</c> implements a query language aimed mainly at Mnesia but Ets tables, Dets tables, and lists are also recognized by <c>qlc</c> as sources of data. Calling <c>dets:table/1,2</c> is the - means to make the Dets table <c>Name</c> usable to <c>qlc</c>.</p> + means to make the Dets table <c><anno>Name</anno></c> usable to <c>qlc</c>.</p> <p>When there are only simple restrictions on the key position <c>qlc</c> uses <c>dets:lookup/2</c> to look up the keys, but when that is not possible the whole table is traversed. The @@ -1137,7 +1036,8 @@ Returns a QLC (Query List specification that matches all objects.</p> </item> <item> - <p><c>{select, MatchSpec}</c>. As for <c>select</c> + <p><c>{select, <seealso marker="#type-match_spec"> + match_spec()}</seealso></c>. As for <c>select</c> the table is traversed by calling <c>dets:select/3</c> and <c>dets:select/1</c>. The difference is that the match specification is explicitly given. This is how to @@ -1166,35 +1066,23 @@ true </pre> </desc> </func> <func> - <name>to_ets(Name, EtsTab) -> EtsTab | {error, Reason}</name> + <name name="to_ets" arity="2"/> <fsummary>Insert all objects of a Dets table into an Ets table.</fsummary> - <type> - <v>Name = name()</v> - <v>EtsTab = - see ets(3) -</v> - </type> <desc> - <p>Inserts the objects of the Dets table <c>Name</c> into the - Ets table <c>EtsTab</c>. The order in which the objects are + <p>Inserts the objects of the Dets table <c><anno>Name</anno></c> into the + Ets table <c><anno>EtsTab</anno></c>. The order in which the objects are inserted is not specified. The existing objects of the Ets table are kept unless overwritten.</p> </desc> </func> <func> - <name>traverse(Name, Fun) -> Return | {error, Reason}</name> + <name name="traverse" arity="2"/> <fsummary>Apply a function to all or some objects stored in a Dets table.</fsummary> - <type> - <v>Fun = fun(Object) -> FunReturn</v> - <v>FunReturn = continue | {continue, Val} | {done, Value}</v> - <v>Val = Value = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - <v>Return = [term()]</v> - </type> <desc> - <p>Applies <c>Fun</c> to each object stored in the table - <c>Name</c> in some unspecified order. Different actions are - taken depending on the return value of <c>Fun</c>. The - following <c>Fun</c> return values are allowed:</p> + <p>Applies <c><anno>Fun</anno></c> to each object stored in the table + <c><anno>Name</anno></c> in some unspecified order. Different actions are + taken depending on the return value of <c><anno>Fun</anno></c>. The + following <c><anno>Fun</anno></c> return values are allowed:</p> <taglist> <tag><c>continue</c></tag> <item> @@ -1206,35 +1094,31 @@ fun(X) -> io:format("~p~n", [X]), continue end. </pre> </item> <tag><c>{continue, Val}</c></tag> <item> - <p>Continue the traversal and accumulate <c>Val</c>. The + <p>Continue the traversal and accumulate <c><anno>Val</anno></c>. The following function is supplied in order to collect all objects of a table in a list: </p> <pre> fun(X) -> {continue, X} end. </pre> </item> - <tag><c>{done, Value}</c></tag> + <tag><c>{done, <anno>Value</anno>}</c></tag> <item> - <p>Terminate the traversal and return <c>[Value | Acc]</c>.</p> + <p>Terminate the traversal and return <c>[<anno>Value</anno> | Acc]</c>.</p> </item> </taglist> - <p>Any other value returned by <c>Fun</c> terminates the + <p>Any other value returned by <c><anno>Fun</anno></c> terminates the traversal and is immediately returned. </p> </desc> </func> <func> - <name>update_counter(Name, Key, Increment) -> Result</name> + <name name="update_counter" arity="3"/> <fsummary>Update a counter object stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key = term()</v> - <v>Increment = {Pos, Incr} | Incr</v> - <v>Pos = Incr = Result = integer()</v> - </type> <desc> - <p>Updates the object with key <c>Key</c> stored in the table - <c>Name</c> of type <c>set</c> by adding <c>Incr</c> to the - element at the <c>Pos</c>:th position. The new counter value + <p>Updates the object with key <c><anno>Key</anno></c> stored in the + table <c><anno>Name</anno></c> of type <c>set</c> by adding + <c><anno>Incr</anno></c> to the + element at the <c><anno>Pos</anno></c>:th position. + The new counter value is returned. If no position is specified, the element directly following the key is updated.</p> <p>This functions provides a way of updating a counter, @@ -1252,4 +1136,3 @@ fun(X) -> {continue, X} end. </pre> <seealso marker="qlc">qlc(3)</seealso></p> </section> </erlref> - diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index 0cc76e0c78..b01acd02bf 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -39,174 +39,120 @@ they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -dictionary() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-dict">dict()</marker></name> + <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>append(Key, Value, Dict1) -> Dict2</name> + <name name="append" arity="3"/> <fsummary>Append a value to keys in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function appends a new <c>Value</c> to the current list - of values associated with <c>Key</c>.</p> - </desc> + <p>This function appends a new <c><anno>Value</anno></c> to the current list + of values associated with <c><anno>Key</anno></c>.</p> + </desc> </func> <func> - <name>append_list(Key, ValList, Dict1) -> Dict2</name> + <name name="append_list" arity="3"/> <fsummary>Append new values to keys in a dictionary</fsummary> - <type> - <v>ValList = [Value]</v> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function appends a list of values <c>ValList</c> to - the current list of values associated with <c>Key</c>. An + <p>This function appends a list of values <c><anno>ValList</anno></c> to + the current list of values associated with <c><anno>Key</anno></c>. An exception is generated if the initial value associated with - <c>Key</c> is not a list of values.</p> + <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>erase(Key, Dict1) -> Dict2</name> + <name name="erase" arity="2"/> <fsummary>Erase a key from a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> <p>This function erases all items with a given key from a dictionary.</p> </desc> </func> <func> - <name>fetch(Key, Dict) -> Value</name> + <name name="fetch" arity="2"/> <fsummary>Look-up values in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function returns the value associated with <c>Key</c> - in the dictionary <c>Dict</c>. <c>fetch</c> assumes that - the <c>Key</c> is present in the dictionary and an exception - is generated if <c>Key</c> is not in the dictionary.</p> + <p>This function returns the value associated with <c><anno>Key</anno></c> + in the dictionary <c><anno>Dict</anno></c>. <c>fetch</c> assumes that + the <c><anno>Key</anno></c> is present in the dictionary and an exception + is generated if <c><anno>Key</anno></c> is not in the dictionary.</p> </desc> </func> <func> - <name>fetch_keys(Dict) -> Keys</name> + <name name="fetch_keys" arity="1"/> <fsummary>Return all keys in a dictionary</fsummary> - <type> - <v>Dict = dictionary()</v> - <v>Keys = [term()]</v> - </type> <desc> <p>This function returns a list of all keys in the dictionary.</p> </desc> </func> <func> - <name>filter(Pred, Dict1) -> Dict2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Key, Value) -> bool()</v> - <v> Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p><c>Dict2</c> is a dictionary of all keys and values in - <c>Dict1</c> for which <c>Pred(Key, Value)</c> is <c>true</c>.</p> + <p><c><anno>Dict2</anno></c> is a dictionary of all keys and values in + <c><anno>Dict1</anno></c> for which <c><anno>Pred</anno>(<anno>Key</anno>, <anno>Value</anno>)</c> is <c>true</c>.</p> </desc> </func> <func> - <name>find(Key, Dict) -> {ok, Value} | error</name> + <name name="find" arity="2"/> <fsummary>Search for a key in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> <p>This function searches for a key in a dictionary. Returns - <c>{ok, Value}</c> where <c>Value</c> is the value associated - with <c>Key</c>, or <c>error</c> if the key is not present in + <c>{ok, <anno>Value</anno>}</c> where <c><anno>Value</anno></c> is the value associated + with <c><anno>Key</anno></c>, or <c>error</c> if the key is not present in the dictionary.</p> </desc> </func> <func> - <name>fold(Fun, Acc0, Dict) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value, AccIn) -> AccOut</v> - <v>Key = Value = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>Calls <c>Fun</c> on successive keys and values of - <c>Dict</c> together with an extra argument <c>Acc</c> - (short for accumulator). <c>Fun</c> must return a new - accumulator which is passed to the next call. <c>Acc0</c> is + <p>Calls <c><anno>Fun</anno></c> on successive keys and values of + <c><anno>Dict</anno></c> together with an extra argument <c>Acc</c> + (short for accumulator). <c><anno>Fun</anno></c> must return a new + accumulator which is passed to the next call. <c><anno>Acc0</anno></c> is returned if the list is empty. The evaluation order is undefined.</p> </desc> </func> <func> - <name>from_list(List) -> Dict</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list of pairs to a dictionary</fsummary> - <type> - <v>List = [{Key, Value}]</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function converts the <c>Key</c> - <c>Value</c> list - <c>List</c> to a dictionary.</p> + <p>This function converts the <c><anno>Key</anno></c> - <c><anno>Value</anno></c> list + <c><anno>List</anno></c> to a dictionary.</p> </desc> </func> <func> - <name>is_key(Key, Dict) -> bool()</name> + <name name="is_key" arity="2"/> <fsummary>Test if a key is in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function tests if <c>Key</c> is contained in - the dictionary <c>Dict</c>.</p> + <p>This function tests if <c><anno>Key</anno></c> is contained in + the dictionary <c><anno>Dict</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, Dict1) -> Dict2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value1) -> Value2</v> - <v> Key = Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p><c>map</c> calls <c>Func</c> on successive keys and values - of <c>Dict</c> to return a new value for each key. + <p><c>map</c> calls <c><anno>Fun</anno></c> on successive keys and values + of <c><anno>Dict1</anno></c> to return a new value for each key. The evaluation order is undefined.</p> </desc> </func> <func> - <name>merge(Fun, Dict1, Dict2) -> Dict3</name> + <name name="merge" arity="3"/> <fsummary>Merge two dictionaries</fsummary> - <type> - <v>Fun = fun(Key, Value1, Value2) -> Value</v> - <v> Key = Value1 = Value2 = Value3 = term()</v> - <v>Dict1 = Dict2 = Dict3 = dictionary()</v> - </type> <desc> - <p><c>merge</c> merges two dictionaries, <c>Dict1</c> and - <c>Dict2</c>, to create a new dictionary. All the <c>Key</c> - - <c>Value</c> pairs from both dictionaries are included in + <p><c>merge</c> merges two dictionaries, <c><anno>Dict1</anno></c> and + <c><anno>Dict2</anno></c>, to create a new dictionary. All the <c><anno>Key</anno></c> + - <c><anno>Value</anno></c> pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - <c>Fun</c> is called with the key and both values to return a + <c><anno>Fun</anno></c> is called with the key and both values to return a new value. <c>merge</c> could be defined as:</p> <code type="none"> merge(Fun, D1, D2) -> @@ -217,75 +163,52 @@ merge(Fun, D1, D2) -> </desc> </func> <func> - <name>new() -> dictionary()</name> + <name name="new" arity="0"/> <fsummary>Create a dictionary</fsummary> <desc> <p>This function creates a new dictionary.</p> </desc> </func> <func> - <name>size(Dict) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a dictionary</fsummary> - <type> - <v>Dict = dictionary()</v> - </type> <desc> - <p>Returns the number of elements in a <c>Dict</c>.</p> + <p>Returns the number of elements in a <c><anno>Dict</anno></c>.</p> </desc> </func> <func> - <name>store(Key, Value, Dict1) -> Dict2</name> + <name name="store" arity="3"/> <fsummary>Store a value in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function stores a <c>Key</c> - <c>Value</c> pair in a - dictionary. If the <c>Key</c> already exists in <c>Dict1</c>, - the associated value is replaced by <c>Value</c>.</p> + <p>This function stores a <c><anno>Key</anno></c> - <c><anno>Value</anno></c> pair in a + dictionary. If the <c><anno>Key</anno></c> already exists in <c><anno>Dict1</anno></c>, + the associated value is replaced by <c><anno>Value</anno></c>.</p> </desc> </func> <func> - <name>to_list(Dict) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a dictionary to a list of pairs</fsummary> - <type> - <v>Dict = dictionary()</v> - <v>List = [{Key, Value}]</v> - </type> <desc> <p>This function converts the dictionary to a list representation.</p> </desc> </func> <func> - <name>update(Key, Fun, Dict1) -> Dict2</name> + <name name="update" arity="3"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Update a value in a dictionary by calling <c>Fun</c> on + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on the value to get a new value. An exception is generated if - <c>Key</c> is not present in the dictionary.</p> + <c><anno>Key</anno></c> is not present in the dictionary.</p> </desc> </func> <func> - <name>update(Key, Fun, Initial, Dict1) -> Dict2</name> + <name name="update" arity="4"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = Initial = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Update a value in a dictionary by calling <c>Fun</c> on - the value to get a new value. If <c>Key</c> is not present - in the dictionary then <c>Initial</c> will be stored as + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on + the value to get a new value. If <c><anno>Key</anno></c> is not present + in the dictionary then <c><anno>Initial</anno></c> will be stored as the first value. For example <c>append/3</c> could be defined as:</p> <code type="none"> @@ -294,17 +217,12 @@ append(Key, Val, D) -> </desc> </func> <func> - <name>update_counter(Key, Increment, Dict1) -> Dict2</name> + <name name="update_counter" arity="3"/> <fsummary>Increment a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Increment = number()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Add <c>Increment</c> to the value associated with <c>Key</c> - and store this value. If <c>Key</c> is not present in - the dictionary then <c>Increment</c> will be stored as + <p>Add <c><anno>Increment</anno></c> to the value associated with <c><anno>Key</anno></c> + and store this value. If <c><anno>Key</anno></c> is not present in + the dictionary then <c><anno>Increment</anno></c> will be stored as the first value.</p> <p>This could be defined as:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml index ad256e671f..0afc70ebe0 100644 --- a/lib/stdlib/doc/src/digraph.xml +++ b/lib/stdlib/doc/src/digraph.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -87,67 +87,79 @@ is a digraph that has no cycles. </p> </description> + <datatypes> + <datatype> + <name name="d_type"/> + </datatype> + <datatype> + <name name="d_cyclicity"/> + </datatype> + <datatype> + <name name="d_protection"/> + </datatype> + <datatype> + <name><marker id="type-digraph">digraph()</marker></name> + <desc><p>A digraph as returned by <c>new/0,1</c>.</p></desc> + </datatype> + <datatype> + <name><marker id="type-edge">edge()</marker></name> + </datatype> + <datatype> + <name name="label"/> + </datatype> + <datatype> + <name><marker id="type-vertex">vertex()</marker></name> + </datatype> + </datatypes> <funcs> <func> - <name>add_edge(G, E, V1, V2, Label) -> edge() | {error, Reason}</name> - <name>add_edge(G, V1, V2, Label) -> edge() | {error, Reason}</name> - <name>add_edge(G, V1, V2) -> edge() | {error, Reason}</name> + <name name="add_edge" arity="3"/> + <name name="add_edge" arity="4"/> + <name name="add_edge" arity="5"/> <fsummary>Add an edge to a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - <v>V1 = V2 = vertex()</v> - <v>Label = label()</v> - <v>Reason = {bad_edge, Path} | {bad_vertex, V}</v> - <v>Path = [vertex()]</v> - </type> + <type name="add_edge_err_rsn"/> <desc> - <p><c>add_edge/5</c> creates (or modifies) the edge <c>E</c> - of the digraph <c>G</c>, using <c>Label</c> as the (new) + <p><c>add_edge/5</c> creates (or modifies) the edge <c><anno>E</anno></c> + of the digraph <c><anno>G</anno></c>, using <c><anno>Label</anno></c> as the (new) <seealso marker="#label">label</seealso> of the edge. The edge is <seealso marker="#emanate">emanating</seealso> from - <c>V1</c> and <seealso marker="#incident">incident</seealso> - on <c>V2</c>. Returns <c>E</c>. + <c><anno>V1</anno></c> and <seealso marker="#incident">incident</seealso> + on <c><anno>V2</anno></c>. Returns <c><anno>E</anno></c>. </p> - <p><c>add_edge(G, V1, V2, Label)</c> is + <p><c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>)</c> is equivalent to - <c>add_edge(G, E, V1, V2, Label)</c>, - where <c>E</c> is a created edge. The created edge is + <c>add_edge(<anno>G</anno>, <anno>E</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>)</c>, + where <c><anno>E</anno></c> is a created edge. The created edge is represented by the term <c>['$e' | N]</c>, where N is an integer >= 0. </p> - <p><c>add_edge(G, V1, V2)</c> is equivalent to - <c>add_edge(G, V1, V2, [])</c>. + <p><c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>)</c> is equivalent to + <c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>, [])</c>. </p> <p>If the edge would create a cycle in an <seealso marker="#acyclic_digraph">acyclic digraph</seealso>, - then <c>{error, {bad_edge, Path}}</c> is returned. If - either of <c>V1</c> or <c>V2</c> is not a vertex of the - digraph <c>G</c>, then - <c>{error, {bad_vertex, </c>V<c>}}</c> is - returned, V = <c>V1</c> or - V = <c>V2</c>. + then <c>{error, {bad_edge, <anno>Path</anno>}}</c> is returned. If + either of <c><anno>V1</anno></c> or <c><anno>V2</anno></c> is not a vertex of the + digraph <c><anno>G</anno></c>, then + <c>{error, {bad_vertex, </c><anno>V</anno><c>}}</c> is + returned, <anno>V</anno> = <c><anno>V1</anno></c> or + <anno>V</anno> = <c><anno>V2</anno></c>. </p> </desc> </func> <func> - <name>add_vertex(G, V, Label) -> vertex()</name> - <name>add_vertex(G, V) -> vertex()</name> - <name>add_vertex(G) -> vertex()</name> + <name name="add_vertex" arity="1"/> + <name name="add_vertex" arity="2"/> + <name name="add_vertex" arity="3"/> <fsummary>Add or modify a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p><c>add_vertex/3</c> creates (or modifies) the vertex <c>V</c> - of the digraph <c>G</c>, using <c>Label</c> as the (new) + <p><c>add_vertex/3</c> creates (or modifies) the vertex <c><anno>V</anno></c> + of the digraph <c><anno>G</anno></c>, using <c><anno>Label</anno></c> as the (new) <seealso marker="#label">label</seealso> of the - vertex. Returns <c>V</c>. + vertex. Returns <c><anno>V</anno></c>. </p> - <p><c>add_vertex(G, V)</c> is equivalent to - <c>add_vertex(G, V, [])</c>. + <p><c>add_vertex(<anno>G</anno>, <anno>V</anno>)</c> is equivalent to + <c>add_vertex(<anno>G</anno>, <anno>V</anno>, [])</c>. </p> <p><c>add_vertex/1</c> creates a vertex using the empty list as label, and returns the created vertex. The created vertex @@ -157,304 +169,227 @@ </desc> </func> <func> - <name>del_edge(G, E) -> true</name> + <name name="del_edge" arity="2"/> <fsummary>Delete an edge from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - </type> <desc> - <p>Deletes the edge <c>E</c> from the digraph <c>G</c>. + <p>Deletes the edge <c><anno>E</anno></c> from the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>del_edges(G, Edges) -> true</name> + <name name="del_edges" arity="2"/> <fsummary>Delete edges from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Edges = [edge()]</v> - </type> <desc> - <p>Deletes the edges in the list <c>Edges</c> from the digraph - <c>G</c>. + <p>Deletes the edges in the list <c><anno>Edges</anno></c> from the digraph + <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>del_path(G, V1, V2) -> true</name> + <name name="del_path" arity="3"/> <fsummary>Delete paths from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - </type> <desc> - <p>Deletes edges from the digraph <c>G</c> until there are no + <p>Deletes edges from the digraph <c><anno>G</anno></c> until there are no <seealso marker="#path">paths</seealso> from the vertex - <c>V1</c> to the vertex <c>V2</c>. + <c><anno>V1</anno></c> to the vertex <c><anno>V2</anno></c>. </p> <p>A sketch of the procedure employed: Find an arbitrary <seealso marker="#simple_path">simple path</seealso> - v[1], v[2], ..., v[k] from <c>V1</c> to - <c>V2</c> in <c>G</c>. Remove all edges of - <c>G</c> <seealso marker="#emanate">emanating</seealso> from v[i] + v[1], v[2], ..., v[k] from <c><anno>V1</anno></c> to + <c><anno>V2</anno></c> in <c><anno>G</anno></c>. Remove all edges of + <c><anno>G</anno></c> <seealso marker="#emanate">emanating</seealso> from v[i] and <seealso marker="#incident">incident</seealso> to v[i+1] for 1 <= i < k (including multiple - edges). Repeat until there is no path between <c>V1</c> and - <c>V2</c>. + edges). Repeat until there is no path between <c><anno>V1</anno></c> and + <c><anno>V2</anno></c>. </p> </desc> </func> <func> - <name>del_vertex(G, V) -> true</name> + <name name="del_vertex" arity="2"/> <fsummary>Delete a vertex from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - </type> <desc> - <p>Deletes the vertex <c>V</c> from the digraph <c>G</c>. Any + <p>Deletes the vertex <c><anno>V</anno></c> from the digraph <c><anno>G</anno></c>. Any edges <seealso marker="#emanate">emanating</seealso> from - <c>V</c> or <seealso marker="#incident">incident</seealso> - on <c>V</c> are also deleted. + <c><anno>V</anno></c> or <seealso marker="#incident">incident</seealso> + on <c><anno>V</anno></c> are also deleted. </p> </desc> </func> <func> - <name>del_vertices(G, Vertices) -> true</name> + <name name="del_vertices" arity="2"/> <fsummary>Delete vertices from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Deletes the vertices in the list <c>Vertices</c> from the - digraph <c>G</c>. + <p>Deletes the vertices in the list <c><anno>Vertices</anno></c> from the + digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>delete(G) -> true</name> + <name name="delete" arity="1"/> <fsummary>Delete a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Deletes the digraph <c>G</c>. This call is important - because digraphs are implemented with <c>Ets</c>. There is - no garbage collection of <c>Ets</c> tables. The digraph + <p>Deletes the digraph <c><anno>G</anno></c>. This call is important + because digraphs are implemented with <c>ETS</c>. There is + no garbage collection of <c>ETS</c> tables. The digraph will, however, be deleted if the process that created the digraph terminates. </p> </desc> </func> <func> - <name>edge(G, E) -> {E, V1, V2, Label} | false</name> + <name name="edge" arity="2"/> <fsummary>Return the vertices and the label of an edge of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - <v>V1 = V2 = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p>Returns <c>{E, V1, V2, Label}</c> where - <c>Label</c> is the <seealso marker="#label">label</seealso> + <p>Returns <c>{<anno>E</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>}</c> where + <c><anno>Label</anno></c> is the <seealso marker="#label">label</seealso> of the edge - <c>E</c> <seealso marker="#emanate">emanating</seealso> from - <c>V1</c> and <seealso marker="#incident">incident</seealso> on - <c>V2</c> of the digraph <c>G</c>. - If there is no edge <c>E</c> of the - digraph <c>G</c>, then <c>false</c> is returned. + <c><anno>E</anno></c> <seealso marker="#emanate">emanating</seealso> from + <c><anno>V1</anno></c> and <seealso marker="#incident">incident</seealso> on + <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. + If there is no edge <c><anno>E</anno></c> of the + digraph <c><anno>G</anno></c>, then <c>false</c> is returned. </p> </desc> </func> <func> - <name>edges(G) -> Edges</name> + <name name="edges" arity="1"/> <fsummary>Return all edges of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Edges = [edge()]</v> - </type> <desc> - <p>Returns a list of all edges of the digraph <c>G</c>, in + <p>Returns a list of all edges of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>edges(G, V) -> Edges</name> + <name name="edges" arity="2"/> <fsummary>Return the edges emanating from or incident on a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#emanate">emanating</seealso> from - or <seealso marker="#incident">incident</seealso> on <c>V</c> - of the digraph <c>G</c>, in some unspecified order.</p> + or <seealso marker="#incident">incident</seealso> on <c><anno>V</anno></c> + of the digraph <c><anno>G</anno></c>, in some unspecified order.</p> </desc> </func> <func> - <name>get_cycle(G, V) -> Vertices | false</name> + <name name="get_cycle" arity="2"/> <fsummary>Find one cycle in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>If there is a <seealso marker="#simple_cycle">simple cycle</seealso> of length two or more through the vertex - <c>V</c>, then the cycle is returned as a list - <c>[V, ..., V]</c> of vertices, otherwise if there + <c><anno>V</anno></c>, then the cycle is returned as a list + <c>[<anno>V</anno>, ..., <anno>V</anno>]</c> of vertices, otherwise if there is a <seealso marker="#loop">loop</seealso> through - <c>V</c>, then the loop is returned as a list <c>[V]</c>. If - there are no cycles through <c>V</c>, then <c>false</c> is + <c><anno>V</anno></c>, then the loop is returned as a list <c>[<anno>V</anno>]</c>. If + there are no cycles through <c><anno>V</anno></c>, then <c>false</c> is returned. </p> <p><c>get_path/3</c> is used for finding a simple cycle - through <c>V</c>. + through <c><anno>V</anno></c>. </p> </desc> </func> <func> - <name>get_path(G, V1, V2) -> Vertices | false</name> + <name name="get_path" arity="3"/> <fsummary>Find one path in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find a <seealso marker="#simple_path">simple path</seealso> from - the vertex <c>V1</c> to the vertex - <c>V2</c> of the digraph <c>G</c>. Returns the path as a - list <c>[V1, ..., V2]</c> of vertices, or - <c>false</c> if no simple path from <c>V1</c> to <c>V2</c> + the vertex <c><anno>V1</anno></c> to the vertex + <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. Returns the path as a + list <c>[<anno>V1</anno>, ..., <anno>V2</anno>]</c> of vertices, or + <c>false</c> if no simple path from <c><anno>V1</anno></c> to <c><anno>V2</anno></c> of length one or more exists. </p> - <p>The digraph <c>G</c> is traversed in a depth-first manner, + <p>The digraph <c><anno>G</anno></c> is traversed in a depth-first manner, and the first path found is returned. </p> </desc> </func> <func> - <name>get_short_cycle(G, V) -> Vertices | false</name> + <name name="get_short_cycle" arity="2"/> <fsummary>Find one short cycle in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find an as short as possible <seealso marker="#simple_cycle">simple cycle</seealso> through - the vertex <c>V</c> of the digraph <c>G</c>. Returns the cycle - as a list <c>[V, ..., V]</c> of vertices, or - <c>false</c> if no simple cycle through <c>V</c> exists. + the vertex <c><anno>V</anno></c> of the digraph <c>G</c>. Returns the cycle + as a list <c>[<anno>V</anno>, ..., <anno>V</anno>]</c> of vertices, or + <c>false</c> if no simple cycle through <c><anno>V</anno></c> exists. Note that a <seealso marker="#loop">loop</seealso> through - <c>V</c> is returned as the list <c>[V, V]</c>. + <c><anno>V</anno></c> is returned as the list <c>[<anno>V</anno>, <anno>V</anno>]</c>. </p> <p><c>get_short_path/3</c> is used for finding a simple cycle - through <c>V</c>. + through <c><anno>V</anno></c>. </p> </desc> </func> <func> - <name>get_short_path(G, V1, V2) -> Vertices | false</name> + <name name="get_short_path" arity="3"/> <fsummary>Find one short path in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find an as short as possible <seealso marker="#simple_path">simple path</seealso> from - the vertex <c>V1</c> to the vertex <c>V2</c> of the digraph <c>G</c>. - Returns the path as a list <c>[V1, ..., V2]</c> of - vertices, or <c>false</c> if no simple path from <c>V1</c> - to <c>V2</c> of length one or more exists. + the vertex <c><anno>V1</anno></c> to the vertex <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. + Returns the path as a list <c>[<anno>V1</anno>, ..., <anno>V2</anno>]</c> of + vertices, or <c>false</c> if no simple path from <c><anno>V1</anno></c> + to <c><anno>V2</anno></c> of length one or more exists. </p> - <p>The digraph <c>G</c> is traversed in a breadth-first + <p>The digraph <c><anno>G</anno></c> is traversed in a breadth-first manner, and the first path found is returned. </p> </desc> </func> <func> - <name>in_degree(G, V) -> integer()</name> + <name name="in_degree" arity="2"/> <fsummary>Return the in-degree of a vertex of a digraph.</fsummary> - <type> - <v>G= digraph()</v> - <v>V = vertex()</v> - </type> <desc> <p>Returns the <seealso marker="#in_degree">in-degree</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>in_edges(G, V) -> Edges</name> + <name name="in_edges" arity="2"/> <fsummary>Return all edges incident on a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#incident">incident</seealso> on - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>in_neighbours(G, V) -> Vertices</name> + <name name="in_neighbours" arity="2"/> <fsummary>Return all in-neighbours of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a list of all <seealso marker="#in_neighbour">in-neighbours</seealso> of - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>info(G) -> InfoList</name> + <name name="info" arity="1"/> <fsummary>Return information about a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>InfoList = [{cyclicity, Cyclicity}, {memory, NoWords}, {protection, Protection}]</v> - <v>Cyclicity = cyclic | acyclic</v> - <v>Protection = protected | private</v> - <v>NoWords = integer() >= 0</v> - </type> + <type name="d_cyclicity"/> + <type name="d_protection"/> <desc> <p>Returns a list of <c>{Tag, Value}</c> pairs describing the - digraph <c>G</c>. The following pairs are returned: + digraph <c><anno>G</anno></c>. The following pairs are returned: </p> <list type="bulleted"> <item> - <p><c>{cyclicity, Cyclicity}</c>, where <c>Cyclicity</c> + <p><c>{cyclicity, <anno>Cyclicity</anno>}</c>, where <c><anno>Cyclicity</anno></c> is <c>cyclic</c> or <c>acyclic</c>, according to the options given to <c>new</c>.</p> </item> <item> - <p><c>{memory, NoWords}</c>, where <c>NoWords</c> is - the number of words allocated to the <c>ets</c> tables.</p> + <p><c>{memory, <anno>NoWords</anno>}</c>, where <c><anno>NoWords</anno></c> is + the number of words allocated to the <c>ETS</c> tables.</p> </item> <item> - <p><c>{protection, Protection}</c>, where <c>Protection</c> + <p><c>{protection, <anno>Protection</anno>}</c>, where <c><anno>Protection</anno></c> is <c>protected</c> or <c>private</c>, according to the options given to <c>new</c>.</p> </item> @@ -462,7 +397,7 @@ </desc> </func> <func> - <name>new() -> digraph()</name> + <name name="new" arity="0"/> <fsummary>Return a protected empty digraph, where cycles are allowed.</fsummary> <desc> <p>Equivalent to <c>new([])</c>. @@ -470,15 +405,16 @@ </desc> </func> <func> - <name>new(Type) -> digraph()</name> + <name name="new" arity="1"/> <fsummary>Create a new empty digraph.</fsummary> - <type> - <v>Type = [cyclic | acyclic | private | protected]</v> - </type> + <type variable="Type"/> + <type name="d_type"/> + <type name="d_cyclicity"/> + <type name="d_protection"/> <desc> <p>Returns an <seealso marker="#empty_digraph">empty digraph</seealso> with - properties according to the options in <c>Type</c>:</p> + properties according to the options in <c><anno>Type</anno></c>:</p> <taglist> <tag><c>cyclic</c></tag> <item>Allow <seealso marker="#cycle">cycles</seealso> in the @@ -491,101 +427,72 @@ <item>The digraph can be read and modified by the creating process only.</item> </taglist> - <p>If an unrecognized type option <c>T</c> is given or <c>Type</c> + <p>If an unrecognized type option <c>T</c> is given or <c><anno>Type</anno></c> is not a proper list, there will be a <c>badarg</c> exception. </p> </desc> </func> <func> - <name>no_edges(G) -> integer() >= 0</name> + <name name="no_edges" arity="1"/> <fsummary>Return the number of edges of the a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Returns the number of edges of the digraph <c>G</c>. + <p>Returns the number of edges of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>no_vertices(G) -> integer() >= 0</name> + <name name="no_vertices" arity="1"/> <fsummary>Return the number of vertices of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Returns the number of vertices of the digraph <c>G</c>. + <p>Returns the number of vertices of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>out_degree(G, V) -> integer()</name> + <name name="out_degree" arity="2"/> <fsummary>Return the out-degree of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - </type> <desc> <p>Returns the <seealso marker="#out_degree">out-degree</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>out_edges(G, V) -> Edges</name> + <name name="out_edges" arity="2"/> <fsummary>Return all edges emanating from a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#emanate">emanating</seealso> from - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>out_neighbours(G, V) -> Vertices</name> + <name name="out_neighbours" arity="2"/> <fsummary>Return all out-neighbours of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a list of all <seealso marker="#out_neighbour">out-neighbours</seealso> of - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>vertex(G, V) -> {V, Label} | false</name> + <name name="vertex" arity="2"/> <fsummary>Return the label of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p>Returns <c>{V, Label}</c> where <c>Label</c> is the + <p>Returns <c>{<anno>V</anno>, <anno>Label</anno>}</c> where <c><anno>Label</anno></c> is the <seealso marker="#label">label</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>, or <c>false</c> if there - is no vertex <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, or <c>false</c> if there + is no vertex <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>vertices(G) -> Vertices</name> + <name name="vertices" arity="1"/> <fsummary>Return all vertices of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns a list of all vertices of the digraph <c>G</c>, in + <p>Returns a list of all vertices of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml index 4b137456b3..e44632bfd2 100644 --- a/lib/stdlib/doc/src/digraph_utils.xml +++ b/lib/stdlib/doc/src/digraph_utils.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -119,49 +119,43 @@ considering all edges undirected.</p> </description> + <datatypes> + <datatype> + <name><marker id="type-digraph">digraph()</marker></name> + <desc><p>A digraph as returned by <c>digraph:new/0,1</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>arborescence_root(Digraph) -> no | {yes, Root}</name> + <name name="arborescence_root" arity="1"/> <fsummary>Check if a digraph is an arborescence.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Root = vertex()</v> - </type> <desc> - - <p>Returns <c>{yes, Root}</c> if <c>Root</c> is + <p>Returns <c>{yes, <anno>Root</anno>}</c> if <c><anno>Root</anno></c> is the <seealso marker="#root">root</seealso> of the arborescence - <c>Digraph</c>, <c>no</c> otherwise. + <c><anno>Digraph</anno></c>, <c>no</c> otherwise. </p> </desc> </func> <func> - <name>components(Digraph) -> [Component]</name> + <name name="components" arity="1"/> <fsummary>Return the components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Component = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#components">connected components</seealso>. Each component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - <c>Digraph</c> occurs in exactly one component. + <c><anno>Digraph</anno></c> occurs in exactly one component. </p> </desc> </func> <func> - <name>condensation(Digraph) -> CondensedDigraph</name> + <name name="condensation" arity="1"/> <fsummary>Return a condensed graph of a digraph.</fsummary> - <type> - <v>Digraph = CondensedDigraph = digraph()</v> - </type> <desc> <p>Creates a digraph where the vertices are the <seealso marker="#strong_components">strongly connected - components</seealso> of <c>Digraph</c> as returned by + components</seealso> of <c><anno>Digraph</anno></c> as returned by <c>strong_components/1</c>. If X and Y are strongly connected components, and there exist vertices x and y in X and Y respectively such that there is an @@ -169,7 +163,7 @@ and <seealso marker="#incident">incident</seealso> on y, then an edge emanating from X and incident on Y is created. </p> - <p>The created digraph has the same type as <c>Digraph</c>. + <p>The created digraph has the same type as <c><anno>Digraph</anno></c>. All vertices and edges have the default <seealso marker="#label">label</seealso> <c>[]</c>. </p> @@ -181,12 +175,8 @@ </desc> </func> <func> - <name>cyclic_strong_components(Digraph) -> [StrongComponent]</name> + <name name="cyclic_strong_components" arity="1"/> <fsummary>Return the cyclic strong components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>StrongComponent = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly connected components</seealso>. @@ -194,67 +184,50 @@ by its vertices. The order of the vertices and the order of the components are arbitrary. Only vertices that are included in some <seealso marker="#cycle">cycle</seealso> in - <c>Digraph</c> are returned, otherwise the returned list is + <c><anno>Digraph</anno></c> are returned, otherwise the returned list is equal to that returned by <c>strong_components/1</c>. </p> </desc> </func> <func> - <name>is_acyclic(Digraph) -> bool()</name> + <name name="is_acyclic" arity="1"/> <fsummary>Check if a digraph is acyclic.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is <seealso marker="#acyclic_digraph">acyclic</seealso>.</p> + <c><anno>Digraph</anno></c> is <seealso marker="#acyclic_digraph">acyclic</seealso>.</p> </desc> </func> <func> - <name>is_arborescence(Digraph) -> bool()</name> + <name name="is_arborescence" arity="1"/> <fsummary>Check if a digraph is an arborescence.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is + <c><anno>Digraph</anno></c> is an <seealso marker="#arborescence">arborescence</seealso>.</p> </desc> </func> <func> - <name>is_tree(Digraph) -> bool()</name> + <name name="is_tree" arity="1"/> <fsummary>Check if a digraph is a tree.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is + <c><anno>Digraph</anno></c> is a <seealso marker="#tree">tree</seealso>.</p> </desc> </func> <func> - <name>loop_vertices(Digraph) -> Vertices</name> + <name name="loop_vertices" arity="1"/> <fsummary>Return the vertices of a digraph included in some loop.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns a list of all vertices of <c>Digraph</c> that are + <p>Returns a list of all vertices of <c><anno>Digraph</anno></c> that are included in some <seealso marker="#loop">loop</seealso>.</p> </desc> </func> <func> - <name>postorder(Digraph) -> Vertices</name> + <name name="postorder" arity="1"/> <fsummary>Return the vertices of a digraph in post-order.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns all vertices of the digraph <c>Digraph</c>. The + <p>Returns all vertices of the digraph <c><anno>Digraph</anno></c>. The order is given by a <seealso marker="#depth_first_traversal">depth-first traversal</seealso> of the digraph, collecting visited @@ -266,14 +239,10 @@ </desc> </func> <func> - <name>preorder(Digraph) -> Vertices</name> + <name name="preorder" arity="1"/> <fsummary>Return the vertices of a digraph in pre-order.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns all vertices of the digraph <c>Digraph</c>. The + <p>Returns all vertices of the digraph <c><anno>Digraph</anno></c>. The order is given by a <seealso marker="#depth_first_traversal">depth-first traversal</seealso> of the digraph, collecting visited @@ -281,119 +250,94 @@ </desc> </func> <func> - <name>reachable(Vertices, Digraph) -> Vertices</name> + <name name="reachable" arity="2"/> <fsummary>Return the vertices reachable from some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a <seealso marker="#path">path</seealso> in <c>Digraph</c> from some - vertex of <c>Vertices</c> to the vertex. In particular, + a <seealso marker="#path">path</seealso> in <c><anno>Digraph</anno></c> from some + vertex of <c><anno>Vertices</anno></c> to the vertex. In particular, since paths may have length zero, the vertices of - <c>Vertices</c> are included in the returned list. + <c><anno>Vertices</anno></c> are included in the returned list. </p> </desc> </func> <func> - <name>reachable_neighbours(Vertices, Digraph) -> Vertices</name> + <name name="reachable_neighbours" arity="2"/> <fsummary>Return the neighbours reachable from some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a <seealso marker="#path">path</seealso> in <c>Digraph</c> of length - one or more from some vertex of <c>Vertices</c> to the + a <seealso marker="#path">path</seealso> in <c><anno>Digraph</anno></c> of length + one or more from some vertex of <c><anno>Vertices</anno></c> to the vertex. As a consequence, only those vertices - of <c>Vertices</c> that are included in + of <c><anno>Vertices</anno></c> that are included in some <seealso marker="#cycle">cycle</seealso> are returned. </p> </desc> </func> <func> - <name>reaching(Vertices, Digraph) -> Vertices</name> + <name name="reaching" arity="2"/> <fsummary>Return the vertices that reach some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a <seealso marker="#path">path</seealso> from the vertex to some - vertex of <c>Vertices</c>. In particular, since paths may have - length zero, the vertices of <c>Vertices</c> are included in + vertex of <c><anno>Vertices</anno></c>. In particular, since paths may have + length zero, the vertices of <c><anno>Vertices</anno></c> are included in the returned list. </p> </desc> </func> <func> - <name>reaching_neighbours(Vertices, Digraph) -> Vertices</name> + <name name="reaching_neighbours" arity="2"/> <fsummary>Return the neighbours that reach some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a <seealso marker="#path">path</seealso> of length one or more - from the vertex to some vertex of <c>Vertices</c>. As a consequence, - only those vertices of <c>Vertices</c> that are included in + from the vertex to some vertex of <c><anno>Vertices</anno></c>. As a consequence, + only those vertices of <c><anno>Vertices</anno></c> that are included in some <seealso marker="#cycle">cycle</seealso> are returned. </p> </desc> </func> <func> - <name>strong_components(Digraph) -> [StrongComponent]</name> + <name name="strong_components" arity="1"/> <fsummary>Return the strong components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>StrongComponent = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly connected components</seealso>. Each strongly component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - <c>Digraph</c> occurs in exactly one strong component. + <c><anno>Digraph</anno></c> occurs in exactly one strong component. </p> </desc> </func> <func> - <name>subgraph(Digraph, Vertices [, Options]) -> Subgraph</name> + <name name="subgraph" arity="2"/> + <name name="subgraph" arity="3"/> <fsummary>Return a subgraph of a digraph.</fsummary> - <type> - <v>Digraph = Subgraph = digraph()</v> - <v>Options = [{type, SubgraphType}, {keep_labels, bool()}]</v> - <v>SubgraphType = inherit | type()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Creates a maximal <seealso marker="#subgraph">subgraph</seealso> of <c>Digraph</c> having - as vertices those vertices of <c>Digraph</c> that are - mentioned in <c>Vertices</c>. + as vertices those vertices of <c><anno>Digraph</anno></c> that are + mentioned in <c><anno>Vertices</anno></c>. </p> <p>If the value of the option <c>type</c> is <c>inherit</c>, - which is the default, then the type of <c>Digraph</c> is used + which is the default, then the type of <c><anno>Digraph</anno></c> is used for the subgraph as well. Otherwise the option value of <c>type</c> is used as argument to <c>digraph:new/1</c>. </p> <p>If the value of the option <c>keep_labels</c> is <c>true</c>, which is the default, then the <seealso marker="#label">labels</seealso> of vertices and edges - of <c>Digraph</c> are used for the subgraph as well. If the value + of <c><anno>Digraph</anno></c> are used for the subgraph as well. If the value is <c>false</c>, then the default label, <c>[]</c>, is used for the subgraph's vertices and edges. </p> - <p><c>subgraph(Digraph, Vertices)</c> is equivalent to - <c>subgraph(Digraph, Vertices, [])</c>. + <p><c>subgraph(<anno>Digraph</anno>, <anno>Vertices</anno>)</c> is equivalent to + <c>subgraph(<anno>Digraph</anno>, <anno>Vertices</anno>, [])</c>. </p> <p>There will be a <c>badarg</c> exception if any of the arguments are invalid. @@ -401,16 +345,12 @@ </desc> </func> <func> - <name>topsort(Digraph) -> Vertices | false</name> + <name name="topsort" arity="1"/> <fsummary>Return a topological sorting of the vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a <seealso marker="#topsort">topological ordering</seealso> of the vertices of the digraph - <c>Digraph</c> if such an ordering exists, <c>false</c> + <c><anno>Digraph</anno></c> if such an ordering exists, <c>false</c> otherwise. For each vertex in the returned list, there are no <seealso marker="#out_neighbour">out-neighbours</seealso> that occur earlier in the list.</p> diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index e6b48b270a..488499581f 100644 --- a/lib/stdlib/doc/src/epp.xml +++ b/lib/stdlib/doc/src/epp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,65 +38,61 @@ by <c>compile</c> to preprocess macros and include files before the actual parsing takes place.</p> </description> + <datatypes> + <datatype> + <name name="macros"></name> + </datatype> + <datatype> + <name name="epp_handle"></name> + <desc><p>Handle to the epp server.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>open(FileName, IncludePath) -> {ok,Epp} | {error, ErrorDescriptor}</name> - <name>open(FileName, IncludePath, PredefMacros) -> {ok,Epp} | {error, ErrorDescriptor}</name> + <name name="open" arity="2"/> + <name name="open" arity="3"/> <fsummary>Open a file for preprocessing</fsummary> - <type> - <v>FileName = atom() | string()</v> - <v>IncludePath = [DirectoryName]</v> - <v>DirectoryName = atom() | string()</v> - <v>PredefMacros = [{atom(),term()}]</v> - <v>Epp = pid() -- handle to the epp server</v> - <v>ErrorDescriptor = term()</v> - </type> <desc> <p>Opens a file for preprocessing.</p> </desc> </func> <func> - <name>close(Epp) -> ok</name> + <name name="close" arity="1"/> <fsummary>Close the preprocessing of the file associated with <c>Epp</c></fsummary> - <type> - <v>Epp = pid() -- handle to the epp server</v> - </type> <desc> <p>Closes the preprocessing of a file.</p> </desc> </func> <func> - <name>parse_erl_form(Epp) -> {ok, AbsForm} | {eof, Line} | {error, ErrorInfo}</name> + <name name="parse_erl_form" arity="1"/> <fsummary>Return the next Erlang form from the opened Erlang source file</fsummary> - <type> - <v>Epp = pid()</v> - <v>AbsForm = term()</v> - <v>Line = integer()</v> - <v>ErrorInfo = see separate description below.</v> - </type> <desc> <p>Returns the next Erlang form from the opened Erlang source file. - The tuple <c>{eof, Line}</c> is returned at end-of-file. The first + The tuple <c>{eof, <anno>Line</anno>}</c> is returned at end-of-file. The first form corresponds to an implicit attribute <c>-file(File,1).</c>, where <c>File</c> is the name of the file.</p> </desc> </func> <func> - <name>parse_file(FileName,IncludePath,PredefMacro) -> {ok,[Form]} | {error,OpenError}</name> - <fsummary>Preprocesse and parse an Erlang source file</fsummary> - <type> - <v>FileName = atom() | string()</v> - <v>IncludePath = [DirectoryName]</v> - <v>DirectoryName = atom() | string()</v> - <v>PredefMacros = [{atom(),term()}]</v> - <v>Form = term() -- same as returned by erl_parse:parse_form</v> - </type> + <name name="parse_file" arity="3"/> + <fsummary>Preprocess and parse an Erlang source file</fsummary> <desc> <p>Preprocesses and parses an Erlang source file. - Note that the tuple <c>{eof, Line}</c> returned at end-of-file is + Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned at end-of-file is included as a "form".</p> </desc> </func> + <func> + <name name="format_error" arity="1"/> + <fsummary>Format an error descriptor</fsummary> + <desc> + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns + a string which + describes the error or warning. This function is usually + called implicitly when processing an <c>ErrorInfo</c> + structure (see below).</p> + </desc> + </func> </funcs> <section> diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml index 62af23c5eb..c95494bf25 100644 --- a/lib/stdlib/doc/src/erl_eval.xml +++ b/lib/stdlib/doc/src/erl_eval.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,63 +36,108 @@ <description> <p>This module provides an interpreter for Erlang expressions. The expressions are in the abstract syntax as returned by - <c>erl_parse</c>, the Erlang parser, or a call to - <c>io:parse_erl_exprs/2</c>.</p> + <seealso marker="erl_parse"><c>erl_parse</c></seealso>, + the Erlang parser, or <seealso marker="io"> + <c>io</c></seealso>.</p> </description> + <datatypes> + <datatype> + <name name="bindings"/> + </datatype> + <datatype> + <name name="binding_struct"/> + <desc>A binding structure.</desc> + </datatype> + <datatype> + <name name="expression"/> + </datatype> + <datatype> + <name name="expressions"/> + <desc><p>As returned by <seealso marker="erl_parse#parse_exprs/1"> + <c>erl_parse:parse_exprs/1</c></seealso> or + <seealso marker="io#parse_erl_exprs/2"> + <c>io:parse_erl_exprs/2</c></seealso>.</p></desc> + </datatype> + <datatype> + <name name="expression_list"/> + </datatype> + <datatype> + <name name="func_spec"/> + </datatype> + <datatype> + <name name="lfun_eval_handler"/> + </datatype> + <datatype> + <name name="lfun_value_handler"/> + </datatype> + <datatype> + <name name="local_function_handler"/> + <desc><p>Further described + <seealso marker="#local_function_handler">below.</seealso></p> + </desc> + </datatype> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="nlfun_handler"/> + </datatype> + <datatype> + <name name="non_local_function_handler"/> + <desc><p>Further described + <seealso marker="#non_local_function_handler">below.</seealso></p> + </desc> + </datatype> + <datatype> + <name name="value"/> + </datatype> + </datatypes> <funcs> <func> - <name>exprs(Expressions, Bindings) -> {value, Value, NewBindings}</name> - <name>exprs(Expressions, Bindings, LocalFunctionHandler) -> {value, Value, NewBindings}</name> - <name>exprs(Expressions, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {value, Value, NewBindings}</name> + <name name="exprs" arity="2"/> + <name name="exprs" arity="3"/> + <name name="exprs" arity="4"/> <fsummary>Evaluate expressions</fsummary> - <type> - <v>Expressions = as returned by erl_parse or io:parse_erl_exprs/2</v> - <v>Bindings = as returned by bindings/1</v> - <v>LocalFunctionHandler = {value, Func} | {eval, Func} | none</v> - <v>NonlocalFunctionHandler = {value, Func} | none</v> - </type> <desc> - <p>Evaluates <c>Expressions</c> with the set of bindings - <c>Bindings</c>, where <c>Expressions</c> is a sequence of + <p>Evaluates <c><anno>Expressions</anno></c> with the set of bindings + <c><anno>Bindings</anno></c>, where <c><anno>Expressions</anno></c> + is a sequence of expressions (in abstract syntax) of a type which may be - returned by <c>io:parse_erl_exprs/2</c>. See below for an + returned by <seealso marker="io#parse_erl_exprs/2"> + <c>io:parse_erl_exprs/2</c></seealso>. See below for an explanation of how and when to use the arguments - <c>LocalFunctionHandler</c> and <c>NonlocalFunctionHandler</c>. + <c><anno>LocalFunctionHandler</anno></c> and + <c><anno>NonLocalFunctionHandler</anno></c>. </p> - <p>Returns <c>{value, Value, NewBindings}</c></p> + <p>Returns <c>{value, <anno>Value</anno>, <anno>NewBindings</anno>}</c> + </p> </desc> </func> <func> - <name>expr(Expression, Bindings) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler, ReturnFormat) -> { value, Value, NewBindings } | Value</name> + <name name="expr" arity="2"/> + <name name="expr" arity="3"/> + <name name="expr" arity="4"/> + <name name="expr" arity="5"/> <fsummary>Evaluate expression</fsummary> - <type> - <v>Expression = as returned by io:parse_erl_form/2, for example</v> - <v>Bindings = as returned by bindings/1</v> - <v>LocalFunctionHandler = {value, Func} | {eval, Func} | none</v> - <v>NonlocalFunctionHandler = {value, Func} | none</v> - <v>ReturnFormat = value | none</v> - </type> <desc> - <p>Evaluates <c>Expression</c> with the set of bindings - <c>Bindings</c>. <c>Expression</c> is an expression (in - abstract syntax) of a type which may be returned by - <c>io:parse_erl_form/2</c>. See below for an explanation of + <p>Evaluates <c><anno>Expression</anno></c> with the set of bindings + <c><anno>Bindings</anno></c>. <c><anno>Expression</anno></c> + is an expression in + abstract syntax. See below for an explanation of how and when to use the arguments - <c>LocalFunctionHandler</c> and - <c>NonlocalFunctionHandler</c>. + <c><anno>LocalFunctionHandler</anno></c> and + <c><anno>NonLocalFunctionHandler</anno></c>. </p> - <p>Returns <c>{value, Value, NewBindings}</c> by default. But - if the <c>ReturnFormat</c> is <c>value</c> only the <c>Value</c> - is returned.</p> + <p>Returns <c>{value, <anno>Value</anno>, + <anno>NewBindings</anno>}</c> by default. But if the + <c><anno>ReturnFormat</anno></c> is <c>value</c> only + the <c><anno>Value</anno></c> is returned.</p> </desc> </func> <func> - <name>expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings}</name> - <name>expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> {ValueList, NewBindings}</name> - <name>expr_list(ExpressionList, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {ValueList, NewBindings}</name> + <name name="expr_list" arity="2"/> + <name name="expr_list" arity="3"/> + <name name="expr_list" arity="4"/> <fsummary>Evaluate a list of expressions</fsummary> <desc> <p>Evaluates a list of expressions in parallel, using the same @@ -100,18 +145,19 @@ merge the bindings returned from each evaluation. This function is useful in the <c>LocalFunctionHandler</c>. See below. </p> - <p>Returns <c>{ValueList, NewBindings}</c>.</p> + <p>Returns <c>{<anno>ValueList</anno>, <anno>NewBindings</anno>}</c>. + </p> </desc> </func> <func> - <name>new_bindings() -> BindingStruct</name> + <name name="new_bindings" arity="0"/> <fsummary>Return a bindings structure</fsummary> <desc> <p>Returns an empty binding structure.</p> </desc> </func> <func> - <name>bindings(BindingStruct) -> Bindings</name> + <name name="bindings" arity="1"/> <fsummary>Return bindings</fsummary> <desc> <p>Returns the list of bindings contained in the binding @@ -119,25 +165,28 @@ </desc> </func> <func> - <name>binding(Name, BindingStruct) -> Binding</name> + <name name="binding" arity="2"/> <fsummary>Return bindings</fsummary> <desc> - <p>Returns the binding of <c>Name</c> in <c>BindingStruct</c>.</p> + <p>Returns the binding of <c><anno>Name</anno></c> + in <c><anno>BindingStruct</anno></c>.</p> </desc> </func> <func> - <name>add_binding(Name, Value, Bindings) -> BindingStruct</name> + <name name="add_binding" arity="3"/> <fsummary>Add a binding</fsummary> <desc> - <p>Adds the binding <c>Name = Value</c> to <c>Bindings</c>. + <p>Adds the binding <c><anno>Name</anno> = <anno>Value</anno></c> + to <c><anno>BindingStruct</anno></c>. Returns an updated binding structure.</p> </desc> </func> <func> - <name>del_binding(Name, Bindings) -> BindingStruct</name> + <name name="del_binding" arity="2"/> <fsummary>Delete a binding</fsummary> <desc> - <p>Removes the binding of <c>Name</c> in <c>Bindings</c>. + <p>Removes the binding of <c><anno>Name</anno></c> + in <c><anno>BindingStruct</anno></c>. Returns an updated binding structure.</p> </desc> </func> @@ -145,7 +194,8 @@ <section> <title>Local Function Handler</title> - <p>During evaluation of a function, no calls can be made to local + <p><marker id="local_function_handler"></marker> + During evaluation of a function, no calls can be made to local functions. An undefined function error would be generated. However, the optional argument <c>LocalFunctionHandler</c> may be used to define a function @@ -191,7 +241,8 @@ Func(Name, Arguments, Bindings) </code> <section> <title>Non-local Function Handler</title> - <p>The optional argument <c>NonlocalFunctionHandler</c> may be + <p><marker id="non_local_function_handler"></marker> + The optional argument <c>NonlocalFunctionHandler</c> may be used to define a function which is called in the following cases: a functional object (fun) is called; a built-in function is called; a function is called using the M:F syntax, where M diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml index c93248493f..8ead438b31 100644 --- a/lib/stdlib/doc/src/erl_expand_records.xml +++ b/lib/stdlib/doc/src/erl_expand_records.xml @@ -39,12 +39,8 @@ </description> <funcs> <func> - <name>module(AbsForms, CompileOptions) -> AbsForms</name> + <name name="module" arity="2"/> <fsummary>Expand all records in a module</fsummary> - <type> - <v>AbsForms = [term()]</v> - <v>CompileOptions = [term()]</v> - </type> <desc> <p>Expands all records in a module. The returned module has no references to records, neither attributes nor code.</p> diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml index cfb18ec131..18cc2460f9 100644 --- a/lib/stdlib/doc/src/erl_id_trans.xml +++ b/lib/stdlib/doc/src/erl_id_trans.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2010</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -48,8 +48,8 @@ <name>parse_transform(Forms, Options) -> Forms</name> <fsummary>Transform Erlang forms</fsummary> <type> - <v>Forms = [erlang_form()]</v> - <v>Options = [compiler_options()]</v> + <v>Forms = [<seealso marker="erl_parse#type-abstract_form">erl_parse:abstract_form()</seealso>]</v> + <v>Options = [<seealso marker="compile#type-option">compile:option()</seealso>]</v> </type> <desc> <p>Performs an identity transformation on Erlang forms, as an example.</p> diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml index 732d77c3ae..b8d5ad73b3 100644 --- a/lib/stdlib/doc/src/erl_internal.xml +++ b/lib/stdlib/doc/src/erl_internal.xml @@ -42,112 +42,75 @@ </description> <funcs> <func> - <name>bif(Name, Arity) -> bool()</name> + <name name="bif" arity="2"/> <fsummary>Test for an Erlang BIF</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is an Erlang BIF + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is an Erlang BIF which is automatically recognized by the compiler, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>guard_bif(Name, Arity) -> bool()</name> + <name name="guard_bif" arity="2"/> <fsummary>Test for an Erlang BIF allowed in guards</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is an Erlang BIF + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is an Erlang BIF which is allowed in guards, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>type_test(Name, Arity) -> bool()</name> + <name name="type_test" arity="2"/> <fsummary>Test for a valid type test</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is a valid Erlang + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is a valid Erlang type test, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>arith_op(OpName, Arity) -> bool()</name> + <name name="arith_op" arity="2"/> <fsummary>Test for an arithmetic operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is an arithmetic + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is an arithmetic operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>bool_op(OpName, Arity) -> bool()</name> + <name name="bool_op" arity="2"/> <fsummary>Test for a Boolean operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a Boolean + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a Boolean operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>comp_op(OpName, Arity) -> bool()</name> + <name name="comp_op" arity="2"/> <fsummary>Test for a comparison operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a comparison + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a comparison operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>list_op(OpName, Arity) -> bool()</name> + <name name="list_op" arity="2"/> <fsummary>Test for a list operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a list + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a list operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>send_op(OpName, Arity) -> bool()</name> + <name name="send_op" arity="2"/> <fsummary>Test for a send operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a send + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a send operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>op_type(OpName, Arity) -> Type</name> + <name name="op_type" arity="2"/> <fsummary>Return operator type</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - <v>Type = arith | bool | comp | list | send</v> - </type> <desc> - <p>Returns the <c>Type</c> of operator that <c>OpName/Arity</c> + <p>Returns the <c><anno>Type</anno></c> of operator that <c><anno>OpName</anno>/<anno>Arity</anno></c> belongs to, or generates a <c>function_clause</c> error if it is not an operator at all.</p> diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml index 8639d678fa..b7fbdd8799 100644 --- a/lib/stdlib/doc/src/erl_lint.xml +++ b/lib/stdlib/doc/src/erl_lint.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,29 +60,30 @@ functions separately unless you have written your own Erlang compiler.</p> </description> + <datatypes> + <datatype> + <name name="error_info"/> + </datatype> + <datatype> + <name name="error_description"/> + </datatype> + </datatypes> <funcs> <func> - <name>module(AbsForms) -> {ok,Warnings} | {error,Errors,Warnings}</name> - <name>module(AbsForms, FileName) -> {ok,Warnings} | {error,Errors,Warnings}</name> - <name>module(AbsForms, FileName, CompileOptions) -> {ok,Warnings} | {error,Errors,Warnings}</name> + <name name="module" arity="1"/> + <name name="module" arity="2"/> + <name name="module" arity="3"/> <fsummary>Check a module for errors</fsummary> - <type> - <v>AbsForms = [term()]</v> - <v>FileName = FileName2 = atom() | string()</v> - <v>Warnings = Errors = [{Filename2,[ErrorInfo]}]</v> - <v>ErrorInfo = see separate description below.</v> - <v>CompileOptions = [term()]</v> - </type> <desc> <p>This function checks all the forms in a module for errors. It returns: </p> <taglist> - <tag><c>{ok,Warnings}</c></tag> + <tag><c>{ok,<anno>Warnings</anno>}</c></tag> <item> <p>There were no errors in the module.</p> </item> - <tag><c>{error,Errors,Warnings}</c></tag> + <tag><c>{error,<anno>Errors</anno>,<anno>Warnings</anno>}</c></tag> <item> <p>There were errors in the module.</p> </item> @@ -93,14 +94,14 @@ elements of <c>Options</c> that control the warnings are only described in <seealso marker="compiler:compile#erl_lint_options">compile(3)</seealso>. </p> - <p>The <c>AbsForms</c> of a module which comes from a file + <p>The <c><anno>AbsForms</anno></c> of a module which comes from a file that is read through <c>epp</c>, the Erlang pre-processor, can come from many files. This means that any references to errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso>). The warnings and errors returned have the following format: </p> <code type="none"> - [{FileName2,[ErrorInfo]}] </code> + [{<anno>FileName2</anno>,[<anno>ErrorInfo</anno>]}] </code> <p>The errors and warnings are listed in the order in which they are encountered in the forms. This means that the errors from one file may be split into different entries in @@ -108,27 +109,20 @@ </desc> </func> <func> - <name>is_guard_test(Expr) -> bool()</name> + <name name="is_guard_test" arity="1"/> <fsummary>Test for a guard test</fsummary> - <type> - <v>Expr = term()</v> - </type> <desc> - <p>This function tests if <c>Expr</c> is a legal guard test. - <c>Expr</c> is an Erlang term representing the abstract form + <p>This function tests if <c><anno>Expr</anno></c> is a legal guard test. + <c><anno>Expr</anno></c> is an Erlang term representing the abstract form for the expression. <c>erl_parse:parse_exprs(Tokens)</c> can - be used to generate a list of <c>Expr</c>.</p> + be used to generate a list of <c><anno>Expr</anno></c>.</p> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> - <p>Takes an <c>ErrorDescriptor</c> and returns a string which + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns a string which describes the error or warning. This function is usually called implicitly when processing an <c>ErrorInfo</c> structure (see below).</p> diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml index 18b592deea..bafc2e0746 100644 --- a/lib/stdlib/doc/src/erl_parse.xml +++ b/lib/stdlib/doc/src/erl_parse.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,31 +36,51 @@ <description> <p>This module is the basic Erlang parser which converts tokens into the abstract form of either forms (i.e., top-level constructs), - expressions, or terms. The Abstract Format is described in the ERTS - User's Guide. + expressions, or terms. The Abstract Format is described in the + <seealso marker="erts:absform">ERTS User's Guide</seealso>. Note that a token list must end with the <em>dot</em> token in order to be acceptable to the parse functions (see <seealso marker="erl_scan">erl_scan(3)</seealso>).</p> </description> + <datatypes> + <datatype> + <name name="abstract_clause"></name> + <desc><p>Parse tree for Erlang clause.</p> + </desc> + </datatype> + <datatype> + <name name="abstract_expr"></name> + <desc><p>Parse tree for Erlang expression.</p> + </desc> + </datatype> + <datatype> + <name name="abstract_form"></name> + <desc><p>Parse tree for Erlang form.</p> + </desc> + </datatype> + <datatype> + <name name="error_description"></name> + </datatype> + <datatype> + <name name="error_info"></name> + </datatype> + <datatype> + <name name="token"></name> + </datatype> + </datatypes> <funcs> <func> - <name>parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo}</name> + <name name="parse_form" arity="1"/> <fsummary>Parse an Erlang form</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>AbsForm = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a form. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a form. It returns:</p> <taglist> - <tag><c>{ok, AbsForm}</c></tag> + <tag><c>{ok, <anno>AbsForm</anno>}</c></tag> <item> - <p>The parsing was successful. <c>AbsForm</c> is the + <p>The parsing was successful. <c><anno>AbsForm</anno></c> is the abstract form of the parsed form.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -68,25 +88,18 @@ </desc> </func> <func> - <name>parse_exprs(Tokens) -> {ok, Expr_list} | {error, ErrorInfo}</name> + <name name="parse_exprs" arity="1"/> <fsummary>Parse Erlang expressions</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>Expr_list = [AbsExpr]</v> - <v>AbsExpr = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a list of expressions. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a list of expressions. It returns:</p> <taglist> - <tag><c>{ok, Expr_list}</c></tag> + <tag><c>{ok, <anno>ExprList</anno>}</c></tag> <item> - <p>The parsing was successful. <c>Expr_list</c> is a + <p>The parsing was successful. <c><anno>ExprList</anno></c> is a list of the abstract forms of the parsed expressions.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -94,21 +107,15 @@ </desc> </func> <func> - <name>parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo}</name> + <name name="parse_term" arity="1"/> <fsummary>Parse an Erlang term</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>Term = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a term. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a term. It returns:</p> <taglist> - <tag><c>{ok, Term}</c></tag> + <tag><c>{ok, <anno>Term</anno>}</c></tag> <item> - <p>The parsing was successful. <c>Term</c> is + <p>The parsing was successful. <c><anno>Term</anno></c> is the Erlang term corresponding to the token list.</p> </item> <tag><c>{error, ErrorInfo}</c></tag> @@ -122,7 +129,8 @@ <name>format_error(ErrorDescriptor) -> Chars</name> <fsummary>Format an error descriptor</fsummary> <type> - <v>ErrorDescriptor = errordesc()</v> + <v>ErrorDescriptor = <seealso + marker="#type-error_info">error_description()</seealso></v> <v>Chars = [char() | Chars]</v> </type> <desc> @@ -133,44 +141,32 @@ </desc> </func> <func> - <name>tokens(AbsTerm) -> Tokens</name> - <name>tokens(AbsTerm, MoreTokens) -> Tokens</name> + <name name="tokens" arity="1"/> + <name name="tokens" arity="2"/> <fsummary>Generate a list of tokens for an expression</fsummary> - <type> - <v>Tokens = MoreTokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>AbsTerm = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> <p>This function generates a list of tokens representing the abstract - form <c>AbsTerm</c> of an expression. Optionally, it appends - <c>Moretokens</c>.</p> + form <c><anno>AbsTerm</anno></c> of an expression. Optionally, it + appends <c><anno>MoreTokens</anno></c>.</p> </desc> </func> <func> - <name>normalise(AbsTerm) -> Data</name> + <name name="normalise" arity="1"/> <fsummary>Convert abstract form to an Erlang term</fsummary> - <type> - <v>AbsTerm = Data = term()</v> - </type> <desc> - <p>Converts the abstract form <c>AbsTerm</c> of a term into a + <p>Converts the abstract form <c><anno>AbsTerm</anno></c> of a + term into a conventional Erlang data structure (i.e., the term itself). This is the inverse of <c>abstract/1</c>.</p> </desc> </func> <func> - <name>abstract(Data) -> AbsTerm</name> + <name name="abstract" arity="1"/> <fsummary>Convert an Erlang term into an abstract form</fsummary> - <type> - <v>Data = AbsTerm = term()</v> - </type> <desc> - <p>Converts the Erlang data structure <c>Data</c> into an - abstract form of type <c>AbsTerm</c>. This is the inverse of - <c>normalise/1</c>.</p> + <p>Converts the Erlang data structure <c><anno>Data</anno></c> into an + abstract form of type <c><anno>AbsTerm</anno></c>. + This is the inverse of <c>normalise/1</c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 1fdda48893..57b5828bcd 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -43,93 +43,82 @@ <p>All functions can have an optional argument which specifies a hook that is called if an attempt is made to print an unknown form.</p> </description> + <datatypes> + <datatype> + <name name="hook_function"/> + <desc> + <p>The optional argument <marker id="hook_function"> + <c>HookFunction</c></marker>, shown in the functions described below, + defines a function which is called when an unknown form occurs where there + should be a valid expression.</p> + + <p>If <c>HookFunction</c> is equal to <c>none</c> there is no hook + function.</p> + + <p>The called hook function should return a (possibly deep) list + of characters. <seealso marker="#expr/4"><c>expr/4</c></seealso> + is useful in a hook. + </p> + <p>If <c><anno>CurrentIndentation</anno></c> is negative, there will be no line + breaks and only a space is used as a separator.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>form(Form) -> DeepCharList</name> - <name>form(Form, HookFunction) -> DeepCharList</name> + <name name="form" arity="1"/> + <name name="form" arity="2"/> <fsummary>Pretty print a form</fsummary> - <type> - <v>Form = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>Pretty prints a - <c>Form</c> which is an abstract form of a type which is - returned by <c>erl_parse:parse_form</c>.</p> + <c><anno>Form</anno></c> which is an abstract form of a type which is + returned by <seealso marker="erl_parse#parse_form/1"> + <c>erl_parse:parse_form/1</c></seealso>.</p> </desc> </func> <func> - <name>attribute(Attribute) -> DeepCharList</name> - <name>attribute(Attribute, HookFunction) -> DeepCharList</name> + <name name="attribute" arity="1"/> + <name name="attribute" arity="2"/> <fsummary>Pretty print an attribute</fsummary> - <type> - <v>Attribute = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the attribute - <c>Attribute</c>.</p> + <c><anno>Attribute</anno></c>.</p> </desc> </func> <func> - <name>function(Function) -> DeepCharList</name> - <name>function(Function, HookFunction) -> DeepCharList</name> + <name name="function" arity="1"/> + <name name="function" arity="2"/> <fsummary>Pretty print a function</fsummary> - <type> - <v>Function = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the function - <c>Function</c>.</p> + <c><anno>Function</anno></c>.</p> </desc> </func> <func> - <name>guard(Guard) -> DeepCharList</name> - <name>guard(Guard, HookFunction) -> DeepCharList</name> + <name name="guard" arity="1"/> + <name name="guard" arity="2"/> <fsummary>Pretty print a guard</fsummary> - <type> - <v>Form = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the guard test - <c>Guard</c>.</p> + <c><anno>Guard</anno></c>.</p> </desc> </func> <func> - <name>exprs(Expressions) -> DeepCharList</name> - <name>exprs(Expressions, HookFunction) -> DeepCharList</name> - <name>exprs(Expressions, Indent, HookFunction) -> DeepCharList</name> + <name name="exprs" arity="1"/> + <name name="exprs" arity="2"/> + <name name="exprs" arity="3"/> <fsummary>Pretty print <c>Expressions</c></fsummary> - <type> - <v>Expressions = term()</v> - <v>HookFunction = see separate description below.</v> - <v>Indent = integer()</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the sequence of - expressions in <c>Expressions</c>.</p> + expressions in <c><anno>Expressions</anno></c>.</p> </desc> </func> <func> - <name>expr(Expression) -> DeepCharList</name> - <name>expr(Expression, HookFunction) -> DeepCharList</name> - <name>expr(Expression, Indent, HookFunction) -> DeepCharList</name> - <name>expr(Expression, Indent, Precedence, HookFunction) ->-> DeepCharList</name> + <name name="expr" arity="1"/> + <name name="expr" arity="2"/> + <name name="expr" arity="3"/> + <name name="expr" arity="4"/> <fsummary>Pretty print one <c>Expression</c></fsummary> - <type> - <v>Expression = term()</v> - <v>HookFunction = see separate description below.</v> - <v>Indent = integer()</v> - <v>Precedence = </v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>This function prints one expression. It is useful for implementing hooks (see below).</p> </desc> @@ -137,32 +126,6 @@ </funcs> <section> - <title>Unknown Expression Hooks</title> - <p>The optional argument <c>HookFunction</c>, shown in the functions described above, defines a function which is called when an unknown form occurs where there - should be a valid expression. It can have the following formats:</p> - <taglist> - <tag><c>Function</c></tag> - <item> - <p>The hook function is called by:</p> - <code type="none"> -Function(Expr, - CurrentIndentation, - CurrentPrecedence, - HookFunction) </code> - </item> - <tag><c>none</c></tag> - <item> - <p>There is no hook function</p> - </item> - </taglist> - <p>The called hook function should return a (possibly deep) list - of characters. <c>expr/4</c> is useful in a hook. - </p> - <p>If <c>CurrentIndentation</c> is negative, there will be no line - breaks and only a space is used as a separator.</p> - </section> - - <section> <title>Bugs</title> <p>It should be possible to have hook functions for unknown forms at places other than expressions.</p> diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml index 1199c34f0f..54240dea19 100644 --- a/lib/stdlib/doc/src/erl_scan.xml +++ b/lib/stdlib/doc/src/erl_scan.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,56 +37,96 @@ <p>This module contains functions for tokenizing characters into Erlang tokens.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -category() = atom() -column() = integer() > 0 -line() = integer() -location() = line() | {line(), column()} -reserved_word_fun() -> fun(atom()) -> bool() -set_attribute_fun() -> fun(term()) -> term() -symbol() = atom() | float() | integer() | string() -token() = {category(), attributes()} | {category(), attributes(), symbol()} -attributes() = line() | list() | tuple()</code> - </section> + <datatypes> + <datatype> + <name name="attribute_info"></name> + </datatype> + <datatype> + <name name="attributes"></name> + </datatype> + <datatype> + <name name="attributes_data"></name> + </datatype> + <datatype> + <name name="category"></name> + </datatype> + <datatype> + <name name="column"></name> + </datatype> + <datatype> + <name name="error_description"></name> + </datatype> + <datatype> + <name name="error_info"></name> + </datatype> + <datatype> + <name name="info_line"></name> + </datatype> + <datatype> + <name name="info_location"></name> + </datatype> + <datatype> + <name name="line"></name> + </datatype> + <datatype> + <name name="location"></name> + </datatype> + <datatype> + <name name="option"></name> + </datatype> + <datatype> + <name name="options"></name> + </datatype> + <datatype> + <name name="symbol"></name> + </datatype> + <datatype> + <name name="resword_fun"></name> + </datatype> + <datatype> + <name name="token"></name> + </datatype> + <datatype> + <name name="token_info"></name> + </datatype> + <datatype> + <name name="tokens"></name> + </datatype> + <datatype> + <name name="tokens_result"></name> + </datatype> + </datatypes> <funcs> <func> - <name>string(String) -> Return</name> - <name>string(String, StartLocation) -> Return</name> - <name>string(String, StartLocation, Options) -> Return</name> + <name name="string" arity="1"/> + <name name="string" arity="2"/> + <name name="string" arity="3"/> <fsummary>Scan a string and return the Erlang tokens</fsummary> - <type> - <v>String = string()</v> - <v>Return = {ok, Tokens, EndLocation} | Error</v> - <v>Tokens = [token()]</v> - <v>Error = {error, ErrorInfo, EndLocation}</v> - <v>StartLocation = EndLocation = location()</v> - <v>Options = Option | [Option]</v> - <v>Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return - | text</v> - </type> <desc> - <p>Takes the list of characters <c>String</c> and tries to - scan (tokenize) them. Returns <c>{ok, Tokens, EndLocation}</c>, - where <c>Tokens</c> are the Erlang tokens from - <c>String</c>. <c>EndLocation</c> is the first location - after the last token.</p> - <p><c>{error, ErrorInfo, EndLocation}</c> is returned if an - error occurs. <c>EndLocation</c> is the first location after + <p>Takes the list of characters <c><anno>String</anno></c> and tries to + scan (tokenize) them. Returns <c>{ok, <anno>Tokens</anno>, + <anno>EndLocation</anno>}</c>, + where <c><anno>Tokens</anno></c> are the Erlang tokens from + <c><anno>String</anno></c>. <c><anno>EndLocation</anno></c> + is the first location after the last token.</p> + <p><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLocation</anno>}</c> + is returned if an error occurs. + <c><anno>ErrorLocation</anno></c> is the first location after the erroneous token.</p> - <p><c>string(String)</c> is equivalent to - <c>string(String, 1)</c>, and <c>string(String, - StartLocation)</c> is equivalent to <c>string(String, - StartLocation, [])</c>.</p> - <p><c>StartLocation</c> indicates the initial location when - scanning starts. If <c>StartLocation</c> is a line - <c>attributes()</c> as well as <c>EndLocation</c> and - <c>ErrorLocation</c> will be lines. If - <c>StartLocation</c> is a pair of a line and a column + <p><c>string(<anno>String</anno>)</c> is equivalent to + <c>string(<anno>String</anno>, 1)</c>, and + <c>string(<anno>String</anno>, + <anno>StartLocation</anno>)</c> is equivalent to + <c>string(<anno>String</anno>, + <anno>StartLocation</anno>, [])</c>.</p> + <p><c><anno>StartLocation</anno></c> indicates the initial location + when scanning starts. If <c><anno>StartLocation</anno></c> is a line + <c>attributes()</c> as well as <c><anno>EndLocation</anno></c> and + <c><anno>ErrorLocation</anno></c> will be lines. If + <c><anno>StartLocation</anno></c> is a pair of a line and a column <c>attributes()</c> takes the form of an opaque compound - data type, and <c>EndLocation</c> and <c>ErrorLocation</c> + data type, and <c><anno>EndLocation</anno></c> and + <c><anno>ErrorLocation</anno></c> will be pairs of a line and a column. The <em>token attributes</em> contain information about the column and the line where the token begins, as well as the text of the @@ -134,148 +174,130 @@ attributes() = line() | list() | tuple()</code> </desc> </func> <func> - <name>tokens(Continuation, CharSpec, StartLocation) -> Return</name> - <name>tokens(Continuation, CharSpec, StartLocation, Options) -> Return</name> + <name name="tokens" arity="3"/> + <name name="tokens" arity="4"/> + <type name="char_spec"/> + <type name="return_cont"/> + <type_desc name="return_cont">An opaque continuation</type_desc> <fsummary>Re-entrant scanner</fsummary> - <type> - <v>Continuation = [] | Continuation1</v> - <v>Return = {done, Result, LeftOverChars} | {more, Continuation1}</v> - <v>LeftOverChars = CharSpec</v> - <v>CharSpec = string() | eof</v> - <v>Continuation1 = tuple()</v> - <v>Result = {ok, Tokens, EndLocation} | {eof, EndLocation} | Error</v> - <v>Tokens = [token()]</v> - <v>Error = {error, ErrorInfo, EndLocation}</v> - <v>StartLocation = EndLocation = location()</v> - <v>Options = Option | [Option]</v> - <v>Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return - | text</v> - </type> <desc> <p>This is the re-entrant scanner which scans characters until a <em>dot</em> ('.' followed by a white space) or <c>eof</c> has been reached. It returns:</p> <taglist> - <tag><c>{done, Result, LeftOverChars}</c></tag> + <tag><c>{done, <anno>Result</anno>, <anno>LeftOverChars</anno>}</c> + </tag> <item> <p>This return indicates that there is sufficient input - data to get a result. <c>Result</c> is:</p> + data to get a result. <c><anno>Result</anno></c> is:</p> <taglist> - <tag><c>{ok, Tokens, EndLocation}</c></tag> + <tag><c>{ok, Tokens, EndLocation}</c> + </tag> <item> - <p>The scanning was successful. <c>Tokens</c> is the - list of tokens including <em>dot</em>.</p> + <p>The scanning was successful. <c>Tokens</c> + is the list of tokens including <em>dot</em>.</p> </item> <tag><c>{eof, EndLocation}</c></tag> <item> <p>End of file was encountered before any more tokens.</p> </item> - <tag><c>{error, ErrorInfo, EndLocation}</c></tag> + <tag><c>{error, ErrorInfo, EndLocation}</c> + </tag> <item> - <p>An error occurred. <c>LeftOverChars</c> is the remaining - characters of the input data, + <p>An error occurred. <c><anno>LeftOverChars</anno></c> + is the remaining characters of the input data, starting from <c>EndLocation</c>.</p> </item> </taglist> </item> - <tag><c>{more, Continuation1}</c></tag> + <tag><c>{more, <anno>Continuation1</anno>}</c></tag> <item> <p>More data is required for building a term. - <c>Continuation1</c> must be passed in a new call to + <c><anno>Continuation1</anno></c> must be passed in a new call to <c>tokens/3,4</c> when more data is available.</p> </item> </taglist> - <p>The <c>CharSpec</c> <c>eof</c> signals end of file. - <c>LeftOverChars</c> will then take the value <c>eof</c> as - well.</p> - <p><c>tokens(Continuation, CharSpec, StartLocation)</c> is - equivalent to <c>tokens(Continuation, CharSpec, - StartLocation, [])</c>.</p> + <p>The <c><anno>CharSpec</anno></c> <c>eof</c> signals end of file. + <c><anno>LeftOverChars</anno></c> will then take the value <c>eof</c> + as well.</p> + <p><c>tokens(<anno>Continuation</anno>, <anno>CharSpec</anno>, + <anno>StartLocation</anno>)</c> is equivalent to + <c>tokens(<anno>Continuation</anno>, <anno>CharSpec</anno>, + <anno>StartLocation</anno>, [])</c>.</p> <p>See <seealso marker="#string/3">string/3</seealso> for a description of the various options.</p> </desc> </func> <func> - <name>reserved_word(Atom) -> bool()</name> + <name name="reserved_word" arity="1"/> <fsummary>Test for a reserved word</fsummary> - <type> - <v>Atom = atom()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Atom</c> is an Erlang reserved - word, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Atom</anno></c> is an Erlang + reserved word, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>token_info(Token) -> TokenInfo</name> + <name name="token_info" arity="1"/> <fsummary>Return information about a token</fsummary> - <type> - <v>Token = token()</v> - <v>TokenInfo = [TokenInfoTuple]</v> - <v>TokenInfoTuple = {TokenItem, Info}</v> - <v>TokenItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - <c>Token</c>. The order of the <c>TokenInfoTuple</c>s is not - defined. The following <c>TokenItem</c>s are returned: - <c>category</c>, <c>column</c>, <c>length</c>, - <c>line</c>, <c>symbol</c>, and <c>text</c>. See <seealso + <c><anno>Token</anno></c>. The order of the + <c><anno>TokenInfoTuple</anno></c>s is not + defined. See <seealso marker="#token_info/2">token_info/2</seealso> for information about specific - <c>TokenInfoTuple</c>s.</p> + <c><anno>TokenInfoTuple</anno></c>s.</p> <p>Note that if <c>token_info(Token, TokenItem)</c> returns - <c>undefined</c> for some <c>TokenItem</c> in the list above, the - item is not included in <c>TokenInfo</c>.</p> + <c>undefined</c> for some <c>TokenItem</c>, the + item is not included in <c><anno>TokenInfo</anno></c>.</p> </desc> </func> <func> - <name>token_info(Token, TokenItemSpec) -> TokenInfo</name> + <name name="token_info" arity="2" clause_i="1"/> + <name name="token_info" arity="2" clause_i="2"/> + <type name="token_item"/> + <type name="attribute_item"/> <fsummary>Return information about a token</fsummary> - <type> - <v>Token = token()</v> - <v>TokenItemSpec = TokenItem | [TokenItem]</v> - <v>TokenInfo = TokenInfoTuple | undefined | [TokenInfoTuple]</v> - <v>TokenInfoTuple = {TokenItem, Info}</v> - <v>TokenItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - <c>Token</c>. If <c>TokenItemSpec</c> is a single - <c>TokenItem</c>, the returned value is the corresponding + <c><anno>Token</anno></c>. If one single + <c><anno>TokenItem</anno></c> is given the returned value is + the corresponding <c>TokenInfoTuple</c>, or <c>undefined</c> if the - <c>TokenItem</c> has no value. If <c>TokenItemSpec</c> is a - list of - <c>TokenItem</c>, the result is a list of - <c>TokenInfoTuple</c>. The <c>TokenInfoTuple</c>s will - appear with the corresponding - <c>TokenItem</c>s in the same order as the <c>TokenItem</c>s - appeared in the list of <c>TokenItem</c>s. <c>TokenItem</c>s - with no value are not included in the list of - <c>TokenInfoTuple</c>.</p> - <p>The following <c>TokenInfoTuple</c>s with corresponding - <c>TokenItem</c>s are valid:</p> + <c>TokenItem</c> has no value. If a list of + <c><anno>TokenItem</anno></c>s is given the result is a list of + <c><anno>TokenInfoTuple</anno></c>. The + <c><anno>TokenInfoTuple</anno></c>s will + appear with the corresponding <c><anno>TokenItem</anno></c>s in + the same order as the <c><anno>TokenItem</anno></c>s + appear in the list of <c>TokenItem</c>s. + <c><anno>TokenItem</anno></c>s with no value are not included + in the list of <c><anno>TokenInfoTuple</anno></c>.</p> + <p>The following <c><anno>TokenInfoTuple</anno></c>s with corresponding + <c><anno>TokenItem</anno></c>s are valid:</p> <taglist> - <tag><c>{category, category()}</c></tag> + <tag><c>{category, <seealso marker="#type-category"> + category()</seealso>}</c></tag> <item><p>The category of the token.</p> </item> - <tag><c>{column, column()}</c></tag> + <tag><c>{column, <seealso marker="#type-column"> + column()</seealso>}</c></tag> <item><p>The column where the token begins.</p> </item> <tag><c>{length, integer() > 0}</c></tag> <item><p>The length of the token's text.</p> </item> - <tag><c>{line, line()}</c></tag> + <tag><c>{line, <seealso marker="#type-line"> + line()</seealso>}</c></tag> <item><p>The line where the token begins.</p> </item> - <tag><c>{location, location()}</c></tag> + <tag><c>{location, <seealso marker="#type-location"> + location()</seealso>}</c></tag> <item><p>The line and column where the token begins, or just the line if the column unknown.</p> </item> - <tag><c>{symbol, symbol()}</c></tag> + <tag><c>{symbol, <seealso marker="#type-symbol"> + symbol()</seealso>}</c></tag> <item><p>The token's symbol.</p> </item> <tag><c>{text, string()}</c></tag> @@ -285,70 +307,59 @@ attributes() = line() | list() | tuple()</code> </desc> </func> <func> - <name>attributes_info(Attributes) -> AttributesInfo</name> + <name name="attributes_info" arity="1"/> <fsummary>Return information about token attributes</fsummary> - <type> - <v>Attributes = attributes()</v> - <v>AttributesInfo = [AttributeInfoTuple]</v> - <v>AttributeInfoTuple = {AttributeItem, Info}</v> - <v>AttributeItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - attributes <c>Attributes</c>. The order of the - <c>AttributeInfoTuple</c>s is not defined. The following - <c>AttributeItem</c>s are returned: - <c>column</c>, <c>length</c>, <c>line</c>, and <c>text</c>. + attributes <c><anno>Attributes</anno></c>. The order of the + <c><anno>AttributeInfoTuple</anno></c>s is not defined. See <seealso marker="#attributes_info/2">attributes_info/2</seealso> for information about specific - <c>AttributeInfoTuple</c>s.</p> + <c><anno>AttributeInfoTuple</anno></c>s.</p> <p>Note that if <c>attributes_info(Token, AttributeItem)</c> returns <c>undefined</c> for some <c>AttributeItem</c> in the list above, the item is not included in - <c>AttributesInfo</c>.</p> + <c><anno>AttributesInfo</anno></c>.</p> </desc> </func> <func> - <name>attributes_info(Attributes, AttributeItemSpec) -> AttributesInfo</name> + <name name="attributes_info" arity="2" clause_i="1"/> + <name name="attributes_info" arity="2" clause_i="2"/> <fsummary>Return information about a token attributes</fsummary> - <type> - <v>Attributes = attributes()</v> - <v>AttributeItemSpec = AttributeItem | [AttributeItem]</v> - <v>AttributesInfo = AttributeInfoTuple | undefined - | [AttributeInfoTuple]</v> - <v>AttributeInfoTuple = {AttributeItem, Info}</v> - <v>AttributeItem = atom()</v> - <v>Info = term()</v> - </type> + <type name="attribute_item"/> <desc> <p>Returns a list containing information about the token - attributes <c>Attributes</c>. If <c>AttributeItemSpec</c> is - a single <c>AttributeItem</c>, the returned value is the - corresponding <c>AttributeInfoTuple</c>, or <c>undefined</c> - if the <c>AttributeItem</c> has no value. If - <c>AttributeItemSpec</c> is a list of - <c>AttributeItem</c>, the result is a list of - <c>AttributeInfoTuple</c>. The <c>AttributeInfoTuple</c>s - will appear with the corresponding <c>AttributeItem</c>s in - the same order as the <c>AttributeItem</c>s appeared in the - list of <c>AttributeItem</c>s. <c>AttributeItem</c>s with no + attributes <c><anno>Attributes</anno></c>. If one single + <c><anno>AttributeItem</anno></c> is given the returned value is the + corresponding <c><anno>AttributeInfoTuple</anno></c>, + or <c>undefined</c> if the <c><anno>AttributeItem</anno></c> + has no value. If a list of <c><anno>AttributeItem</anno></c> + is given the result is a list of + <c><anno>AttributeInfoTuple</anno></c>. + The <c><anno>AttributeInfoTuple</anno></c>s + will appear with the corresponding <c><anno>AttributeItem</anno></c>s + in the same order as the <c><anno>AttributeItem</anno></c>s + appear in the list of <c><anno>AttributeItem</anno></c>s. + <c><anno>AttributeItem</anno></c>s with no value are not included in the list of - <c>AttributeInfoTuple</c>.</p> - <p>The following <c>AttributeInfoTuple</c>s with corresponding - <c>AttributeItem</c>s are valid:</p> + <c><anno>AttributeInfoTuple</anno></c>.</p> + <p>The following <c><anno>AttributeInfoTuple</anno></c>s with + corresponding <c><anno>AttributeItem</anno></c>s are valid:</p> <taglist> - <tag><c>{column, column()}</c></tag> + <tag><c>{column, <seealso marker="#type-column"> + column()</seealso>}</c></tag> <item><p>The column where the token begins.</p> </item> <tag><c>{length, integer() > 0}</c></tag> <item><p>The length of the token's text.</p> </item> - <tag><c>{line, line()}</c></tag> + <tag><c>{line, <seealso marker="#type-line"> + line()</seealso>}</c></tag> <item><p>The line where the token begins.</p> </item> - <tag><c>{location, location()}</c></tag> + <tag><c>{location, <seealso marker="#type-location"> + location()</seealso>}</c></tag> <item><p>The line and column where the token begins, or just the line if the column unknown.</p> </item> @@ -359,29 +370,22 @@ attributes() = line() | list() | tuple()</code> </desc> </func> <func> - <name>set_attribute(AttributeItem, Attributes, SetAttributeFun) -> AttributesInfo</name> + <name name="set_attribute" arity="3"/> <fsummary>Set a token attribute value</fsummary> - <type> - <v>AttributeItem = line</v> - <v>Attributes = attributes()</v> - <v>SetAttributeFun = set_attribute_fun()</v> - </type> <desc> <p>Sets the value of the <c>line</c> attribute of the token - attributes <c>Attributes</c>.</p> - <p>The <c>SetAttributeFun</c> is called with the value of + attributes <c><anno>Attributes</anno></c>.</p> + <p>The <c><anno>SetAttributeFun</anno></c> is called with the value of the <c>line</c> attribute, and is to return the new value of the <c>line</c> attribute.</p> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> string()</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - </type> <desc> - <p>Takes an <c>ErrorDescriptor</c> and returns a string which + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns + a string which describes the error or warning. This function is usually called implicitly when processing an <c>ErrorInfo</c> structure (see below).</p> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 746f94d3f4..8c952708c5 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -126,15 +126,30 @@ <em>ERTS User's Guide</em>.</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -match_spec() - a match specification, see above - -tid() - a table identifier, as returned by new/2</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-continuation">continuation()</marker></name> + <desc> + <p>Opaque continuation used by <seealso marker="#select/1"> + <c>select/1</c></seealso> and <seealso marker="#select/3"> + <c>select/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="match_spec"/> + <desc><p>A match specification, see above.</p></desc> + </datatype> + <datatype> + <name name="match_pattern"/> + </datatype> + <datatype> + <name name="tab"/> + </datatype> + <datatype> + <name name="tid"/> + <desc><p>A table identifier, as returned by new/2.</p></desc> + </datatype> + </datatypes> <funcs> <func> <name>all() -> [Tab]</name> @@ -197,37 +212,25 @@ tid() </desc> </func> <func> - <name>file2tab(Filename) -> {ok,Tab} | {error,Reason}</name> + <name name="file2tab" arity="1"/> <fsummary>Read an ETS table from a file.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>Tab = tid() | atom()</v> - <v>Reason = term()</v> - </type> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2">tab2file/2</seealso> or <seealso marker="#tab2file/3">tab2file/3</seealso> and creates the - corresponding table <c>Tab</c>.</p> - <p>Equivalent to <c>file2tab(Filename,[])</c>.</p> + corresponding table <c><anno>Tab</anno></c>.</p> + <p>Equivalent to <c>file2tab(<anno>Filename</anno>, [])</c>.</p> </desc> </func> <func> - <name>file2tab(Filename,Options) -> {ok,Tab} | {error,Reason}</name> + <name name="file2tab" arity="2"/> <fsummary>Read an ETS table from a file.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>Tab = tid() | atom()</v> - <v>Options = [Option]</v> - <v>Option = {verify, bool()}</v> - <v>Reason = term()</v> - </type> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2">tab2file/2</seealso> or <seealso marker="#tab2file/3">tab2file/3</seealso> and creates the - corresponding table <c>Tab</c>.</p> - <p>The currently only supported option is <c>{verify,bool()}</c>. If + corresponding table <c><anno>Tab</anno></c>.</p> + <p>The currently only supported option is <c>{verify,boolean()}</c>. If verification is turned on (by means of specifying <c>{verify,true}</c>), the function utilizes whatever information is present in the file to assert that the @@ -271,70 +274,52 @@ tid() </desc> </func> <func> - <name>foldl(Function, Acc0, Tab) -> Acc1</name> + <name name="foldl" arity="3"/> <fsummary>Fold a function over an ETS table</fsummary> - <type> - <v>Function = fun(A, AccIn) -> AccOut</v> - <v>Tab = tid() | atom()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - </type> <desc> - <p><c>Acc0</c> is returned if the table is empty. + <p><c><anno>Acc0</anno></c> is returned if the table is empty. This function is similar to <c>lists:foldl/3</c>. The order in which the elements of the table are traversed is unspecified, except for tables of type <c>ordered_set</c>, for which they are traversed first to last.</p> - <p>If <c>Function</c> inserts objects into the table, or another + <p>If <c><anno>Function</anno></c> inserts objects into the table, or another process inserts objects into the table, those objects <em>may</em> (depending on key ordering) be included in the traversal.</p> </desc> </func> <func> - <name>foldr(Function, Acc0, Tab) -> Acc1</name> + <name name="foldr" arity="3"/> <fsummary>Fold a function over an ETS table</fsummary> - <type> - <v>Function = fun(A, AccIn) -> AccOut</v> - <v>Tab = tid() | atom()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - </type> <desc> - <p><c>Acc0</c> is returned if the table is empty. + <p><c><anno>Acc0</anno></c> is returned if the table is empty. This function is similar to <c>lists:foldr/3</c>. The order in which the elements of the table are traversed is unspecified, except for tables of type <c>ordered_set</c>, for which they are traversed last to first.</p> - <p>If <c>Function</c> inserts objects into the table, or another + <p>If <c><anno>Function</anno></c> inserts objects into the table, or another process inserts objects into the table, those objects <em>may</em> (depending on key ordering) be included in the traversal.</p> </desc> </func> <func> - <name>from_dets(Tab, DetsTab) -> true</name> + <name name="from_dets" arity="2"/> <fsummary>Fill an ETS table with objects from a Dets table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>DetsTab = atom()</v> - </type> <desc> <p>Fills an already created ETS table with the objects in the - already opened Dets table named <c>DetsTab</c>. The existing + already opened Dets table named <c><anno>DetsTab</anno></c>. The existing objects of the ETS table are kept unless overwritten.</p> <p>Throws a badarg error if any of the tables does not exist or the dets table is not open.</p> </desc> </func> <func> - <name>fun2ms(LiteralFun) -> MatchSpec</name> + <name name="fun2ms" arity="1"/> <fsummary>Pseudo function that transforms fun syntax to a match_spec.</fsummary> - <type> - <v>LiteralFun -- see below</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>Pseudo function that by means of a <c>parse_transform</c> - translates <c>LiteralFun</c> typed as parameter in the + translates <c><anno>LiteralFun</anno></c> typed as parameter in the function call to a <seealso marker="#match_spec">match_spec</seealso>. With "literal" is meant that the fun needs to textually be written @@ -342,7 +327,7 @@ tid() variable which in turn is passed to the function).</p> <p>The parse transform is implemented in the module <c>ms_transform</c> and the source <em>must</em> include the - file <c>ms_transform.hrl</c> in <c>stdlib</c> for this + file <c>ms_transform.hrl</c> in STDLIB for this pseudo function to work. Failing to include the hrl file in the source will result in a runtime error, not a compile time ditto. The include file is easiest included by adding @@ -422,20 +407,17 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>i() -> ok</name> + <name name="i" arity="0"/> <fsummary>Display information about all ETS tables on tty.</fsummary> <desc> <p>Displays information about all ETS tables on tty.</p> </desc> </func> <func> - <name>i(Tab) -> ok</name> + <name name="i" arity="1"/> <fsummary>Browse an ETS table on tty.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> - <p>Browses the table <c>Tab</c> on tty.</p> + <p>Browses the table <c><anno>Tab</anno></c> on tty.</p> </desc> </func> <func> @@ -454,7 +436,7 @@ Error: fun containing local Erlang function calls correct type, this function fails with reason <c>badarg</c>.</p> <list type="bulleted"> - <item><c>Item=memory, Value=int()</c> <br></br> + <item><c>Item=memory, Value=integer()</c> <br></br> The number of words allocated to the table.</item> <item><c>Item=owner, Value=pid()</c> <br></br> @@ -466,7 +448,7 @@ Error: fun containing local Erlang function calls <item><c>Item=name, Value=atom()</c> <br></br> The name of the table.</item> - <item><c>Item=size, Value=int()</c> <br></br> + <item><c>Item=size, Value=integer()</c> <br></br> The number of objects inserted in the table.</item> <item><c>Item=node, Value=atom()</c> <br></br> @@ -479,7 +461,7 @@ Error: fun containing local Erlang function calls <item><c>Item=type, Value=set|ordered_set|bag|duplicate_bag</c> <br></br> The table type.</item> - <item><c>Item=keypos, Value=int()</c> <br></br> + <item><c>Item=keypos, Value=integer()</c> <br></br> The key position.</item> <item><c>Item=protection, Value=public|protected|private</c> <br></br> @@ -536,25 +518,19 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>init_table(Name, InitFun) -> true</name> + <name name="init_table" arity="2"/> <fsummary>Replace all objects of an ETS table.</fsummary> - <type> - <v>Name = atom()</v> - <v>InitFun = fun(Arg) -> Res</v> - <v>Arg = read | close</v> - <v>Res = end_of_input | {[object()], InitFun} | term()</v> - </type> <desc> - <p>Replaces the existing objects of the table <c>Tab</c> with - objects created by calling the input function <c>InitFun</c>, + <p>Replaces the existing objects of the table <c><anno>Tab</anno></c> with + objects created by calling the input function <c><anno>InitFun</anno></c>, see below. This function is provided for compatibility with the <c>dets</c> module, it is not more efficient than filling a table by using <c>ets:insert/2</c>. </p> <p>When called with the argument <c>read</c> the function - <c>InitFun</c> is assumed to return <c>end_of_input</c> when - there is no more input, or <c>{Objects, Fun}</c>, where - <c>Objects</c> is a list of objects and <c>Fun</c> is a new + <c><anno>InitFun</anno></c> is assumed to return <c>end_of_input</c> when + there is no more input, or <c>{<anno>Objects</anno>, Fun}</c>, where + <c><anno>Objects</anno></c> is a list of objects and <c>Fun</c> is a new input function. Any other value Value is returned as an error <c>{error, {init_fun, Value}}</c>. Each input function will be called exactly once, and should an error occur, the last @@ -594,7 +570,7 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>insert_new(Tab, ObjectOrObjects) -> bool()</name> + <name>insert_new(Tab, ObjectOrObjects) -> boolean()</name> <fsummary>Insert an object into an ETS table if the key is not already present.</fsummary> <type> <v>Tab = tid() | atom()</v> @@ -615,7 +591,7 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>is_compiled_ms(Term) -> bool()</name> + <name>is_compiled_ms(Term) -> boolean()</name> <fsummary>Checks if an Erlang term is the result of ets:match_spec_compile</fsummary> <type> <v>Term = term()</v> @@ -708,7 +684,7 @@ ets:is_compiled_ms(Broken).</code> <type> <v>Tab = tid() | atom()</v> <v>Key = term()</v> - <v>Pos = int()</v> + <v>Pos = integer()</v> <v>Elem = term() | [term()]</v> </type> <desc> @@ -802,15 +778,11 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_delete(Tab, Pattern) -> true</name> + <name name="match_delete" arity="2"/> <fsummary>Delete all objects which match a given pattern from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = tuple()</v> - </type> <desc> - <p>Deletes all objects which match the pattern <c>Pattern</c> - from the table <c>Tab</c>. See <c>match/2</c> for a + <p>Deletes all objects which match the pattern <c><anno>Pattern</anno></c> + from the table <c><anno>Tab</anno></c>. See <c>match/2</c> for a description of patterns.</p> </desc> </func> @@ -957,8 +929,8 @@ ets:select(Table,MatchSpec),</code> <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v> <v> Type = set | ordered_set | bag | duplicate_bag</v> <v> Access = public | protected | private</v> - <v> Tweaks = {write_concurrency,bool()} | {read_concurrency,bool()} | compressed</v> - <v> Pos = int()</v> + <v> Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed</v> + <v> Pos = integer()</v> <v> HeirData = term()</v> </type> <desc> @@ -1050,7 +1022,7 @@ ets:select(Table,MatchSpec),</code> </item> <item> <marker id="new_2_write_concurrency"></marker> - <p><c>{write_concurrency,bool()}</c> + <p><c>{write_concurrency,boolean()}</c> Performance tuning. Default is <c>false</c>, in which case an operation that mutates (writes to) the table will obtain exclusive access, blocking any concurrent access of the same table until finished. @@ -1074,7 +1046,7 @@ ets:select(Table,MatchSpec),</code> </item> <item> <marker id="new_2_read_concurrency"></marker> - <p><c>{read_concurrency,bool()}</c> + <p><c>{read_concurrency,boolean()}</c> Performance tuning. Default is <c>false</c>. When set to <c>true</c>, the table is optimized for concurrent read operations. When this option is enabled on a runtime system with @@ -1160,12 +1132,8 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>repair_continuation(Continuation, MatchSpec) -> Continuation</name> + <name name="repair_continuation" arity="2"/> <fsummary>Repair a continuation from ets:select/1 or ets:select/3 that has passed through external representation</fsummary> - <type> - <v>Continuation = term()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>This function can be used to restore an opaque continuation returned by <c>ets:select/3</c> or <c>ets:select/1</c> if the @@ -1551,7 +1519,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <fsummary>Return all objects in a given slot of an ETS table.</fsummary> <type> <v>Tab = tid() | atom()</v> - <v>I = int()</v> + <v>I = integer()</v> <v>Object = tuple()</v> </type> <desc> @@ -1572,32 +1540,19 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>tab2file(Tab, Filename) -> ok | {error,Reason}</name> + <name name="tab2file" arity="2"/> <fsummary>Dump an ETS table to a file.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Filename = string() | atom()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Dumps the table <c>Tab</c> to the file <c>Filename</c>.</p> - <p>Equivalent to <c>tab2file(Tab, Filename,[])</c></p> + <p>Dumps the table <c><anno>Tab</anno></c> to the file <c><anno>Filename</anno></c>.</p> + <p>Equivalent to <c>tab2file(<anno>Tab</anno>, <anno>Filename</anno>,[])</c></p> </desc> </func> <func> - <name>tab2file(Tab, Filename, Options) -> ok | {error,Reason}</name> + <name name="tab2file" arity="3"/> <fsummary>Dump an ETS table to a file.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Filename = string() | atom()</v> - <v>Options = [Option]</v> - <v>Option = {extended_info, [ExtInfo]}</v> - <v>ExtInfo = object_count | md5sum</v> - <v>Reason = term()</v> - </type> <desc> - <p>Dumps the table <c>Tab</c> to the file <c>Filename</c>.</p> + <p>Dumps the table <c><anno>Tab</anno></c> to the file <c><anno>Filename</anno></c>.</p> <p>When dumping the table, certain information about the table is dumped to a header at the beginning of the dump. This information contains data about the table type, @@ -1634,26 +1589,15 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>tab2list(Tab) -> [Object]</name> + <name name="tab2list" arity="1"/> <fsummary>Return a list of all objects in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> - </type> <desc> - <p>Returns a list of all objects in the table <c>Tab</c>.</p> + <p>Returns a list of all objects in the table <c><anno>Tab</anno></c>.</p> </desc> </func> <func> - <name>tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}</name> + <name name="tabfile_info" arity="1"/> <fsummary>Return a list of all objects in an ETS table.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>TableInfo = [InfoItem]</v> - <v>InfoItem = {InfoTag, term()}</v> - <v>InfoTag = name | type | protection | named_table | keypos | size | extended_info | version</v> - <v>Reason = term()</v> - </type> <desc> <p>Returns information about the table dumped to file by <seealso marker="#tab2file/2">tab2file/2</seealso> or @@ -1699,7 +1643,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> one or more of the atoms <c>object_count</c> and <c>md5sum</c>.</item> <tag>version</tag> - <item>A tuple <c>{Major,Minor}</c> containing the major and + <item>A tuple <c>{<anno>Major</anno>,<anno>Minor</anno>}</c> containing the major and minor version of the file format for ets table dumps. This version field was added beginning with stdlib-1.5.1, files dumped with older versions will return <c>{0,0}</c> in this @@ -1712,20 +1656,11 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>table(Tab [, Options]) -> QueryHandle</name> + <name name="table" arity="1"/> + <name name="table" arity="2"/> <fsummary>Return a QLC query handle.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>QueryHandle = - a query handle, see qlc(3) -</v> - <v>Options = [Option] | Option</v> - <v>Option = {n_objects, NObjects} | {traverse, TraverseMethod}</v> - <v>NObjects = default | integer() > 0</v> - <v>TraverseMethod = first_next | last_prev | select | {select, MatchSpec}</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> - <p> <marker id="qlc_table"></marker> -Returns a QLC (Query List + <p><marker id="qlc_table"></marker>Returns a QLC (Query List Comprehension) query handle. The module <c>qlc</c> implements a query language aimed mainly at Mnesia but ETS tables, Dets tables, and lists are also recognized by QLC as sources of @@ -1760,7 +1695,7 @@ Returns a QLC (Query List that matches all objects.</p> </item> <item> - <p><c>{select, MatchSpec}</c>. As for <c>select</c> + <p><c>{select, <anno>MatchSpec</anno>}</c>. As for <c>select</c> the table is traversed by calling <c>ets:select/3</c> and <c>ets:select/1</c>. The difference is that the match_spec is explicitly given. This is how to state @@ -1788,41 +1723,31 @@ true</pre> </desc> </func> <func> - <name>test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors}</name> + <name name="test_ms" arity="2"/> <fsummary>Test a match_spec for use in ets:select/2.</fsummary> - <type> - <v>Tuple = tuple()</v> - <v>MatchSpec = match_spec()</v> - <v>Result = term()</v> - <v>Errors = [{warning|error, string()}]</v> - </type> <desc> <p>This function is a utility to test a <seealso marker="#match_spec">match_spec</seealso> used in calls to <c>ets:select/2</c>. The function both tests - <c>MatchSpec</c> for "syntactic" correctness and runs the - match_spec against the object <c>Tuple</c>. If the match_spec - contains errors, the tuple <c>{error, Errors}</c> is returned - where <c>Errors</c> is a list of natural language + <c><anno>MatchSpec</anno></c> for "syntactic" correctness and runs the + match_spec against the object <c><anno>Tuple</anno></c>. If the match_spec + contains errors, the tuple <c>{error, <anno>Errors</anno>}</c> is returned + where <c><anno>Errors</anno></c> is a list of natural language descriptions of what was wrong with the match_spec. If the match_spec is syntactically OK, the function returns - <c>{ok,Term}</c> where <c>Term</c> is what would have been + <c>{ok,<anno>Result</anno>}</c> where <c><anno>Result</anno></c> is what would have been the result in a real <c>ets:select/2</c> call or <c>false</c> - if the match_spec does not match the object <c>Tuple</c>.</p> + if the match_spec does not match the object <c><anno>Tuple</anno></c>.</p> <p>This is a useful debugging and test tool, especially when writing complicated <c>ets:select/2</c> calls.</p> </desc> </func> <func> - <name>to_dets(Tab, DetsTab) -> DetsTab</name> + <name name="to_dets" arity="2"/> <fsummary>Fill a Dets table with objects from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>DetsTab = atom()</v> - </type> <desc> <p>Fills an already created/opened Dets table with the objects - in the already opened ETS table named <c>Tab</c>. The Dets + in the already opened ETS table named <c><anno>Tab</anno></c>. The Dets table is emptied before the objects are inserted.</p> </desc> </func> @@ -1835,7 +1760,7 @@ true</pre> <v>Tab = tid() | atom()</v> <v>Key = term()</v> <v>UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}</v> - <v>Pos = Incr = Threshold = SetValue = Result = int()</v> + <v>Pos = Incr = Threshold = SetValue = Result = integer()</v> </type> <desc> <p>This function provides an efficient way to update one or more @@ -1897,7 +1822,7 @@ true</pre> <type> <v>Tab = tid() | atom()</v> <v>Key = Value = term()</v> - <v>Pos = int()</v> + <v>Pos = integer()</v> </type> <desc> <p>This function provides an efficient way to update one or more diff --git a/lib/stdlib/doc/src/file_sorter.xml b/lib/stdlib/doc/src/file_sorter.xml index ccb32659a0..a6b3633066 100644 --- a/lib/stdlib/doc/src/file_sorter.xml +++ b/lib/stdlib/doc/src/file_sorter.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -89,7 +89,7 @@ considerably. The <c>keysort</c>, <c>keymerge</c> and <c>keycheck</c> functions do not accept ordering functions. </item> - <item><c>{unique, bool()}</c>. When sorting or merging files, + <item><c>{unique, boolean()}</c>. When sorting or merging files, only the first of a sequence of terms that compare equal (<c>==</c>) is output if this option is set to <c>true</c>. The default value is <c>false</c> which implies that all terms that @@ -112,7 +112,7 @@ overwritten. Temporary files are deleted unless some uncaught EXIT signal occurs. </item> - <item><c>{compressed, bool()}</c>. Temporary files and the + <item><c>{compressed, boolean()}</c>. Temporary files and the output file may be compressed. The default value <c>false</c> implies that written files are not compressed. Regardless of the value of the <c>compressed</c> @@ -128,39 +128,6 @@ merged at a time. This option should rarely be needed. </item> </list> - <p>To summarize, here is the syntax of the options:</p> - <list type="bulleted"> - <item> - <p><c>Options = [Option] | Option</c></p> - </item> - <item> - <p><c>Option = {header, HeaderLength} | {format, Format} | {order, Order} | {unique, bool()} | {tmpdir, TempDirectory} | {compressed, bool()} | {size, Size} | {no_files, NoFiles}</c></p> - </item> - <item> - <p><c>HeaderLength = int() > 0</c></p> - </item> - <item> - <p><c>Format = binary_term | term | binary | FormatFun</c></p> - </item> - <item> - <p><c>FormatFun = fun(Binary) -> Term</c></p> - </item> - <item> - <p><c>Order = ascending | descending | OrderFun</c></p> - </item> - <item> - <p><c>OrderFun = fun(Term, Term) -> bool()</c></p> - </item> - <item> - <p><c>TempDirectory = "" | file_name()</c></p> - </item> - <item> - <p><c>Size = int() >= 0</c></p> - </item> - <item> - <p><c>NoFiles = int() > 1</c></p> - </item> - </list> <p>As an alternative to sorting files, a function of one argument can be given as input. When called with the argument <c>read</c> the function is assumed to return <c>end_of_input</c> or @@ -234,8 +201,8 @@ output(L) -> occurs are:</p> <list type="bulleted"> <item> - <p><c>bad_object</c>, <c>{bad_object, FileName}</c>. - Applying the format function failed for some binary, + <p><c>bad_object</c>, <c>{bad_object, FileName}</c>. + Applying the format function failed for some binary, or the key(s) could not be extracted from some term.</p> </item> <item> @@ -243,148 +210,181 @@ output(L) -> to read some term.</p> </item> <item> - <p><c>{file_error, FileName, Reason2}</c>. See - <c>file(3)</c> for an explanation of <c>Reason2</c>.</p> + <p><c>{file_error, FileName, file:posix()}</c>. See + <c>file(3)</c> for an explanation of <c>file:posix()</c>.</p> </item> <item> - <p><c>{premature_eof, FileName}</c>. End-of-file was + <p><c>{premature_eof, FileName}</c>. End-of-file was encountered inside some binary term.</p> </item> </list> - <p><em>Types</em></p> - <pre> -Binary = binary() -FileName = file_name() -FileNames = [FileName] -ICommand = read | close -IReply = end_of_input | {end_of_input, Value} | {[Object], Infun} | InputReply -Infun = fun(ICommand) -> IReply -Input = FileNames | Infun -InputReply = Term -KeyPos = int() > 0 | [int() > 0] -OCommand = {value, Value} | [Object] | close -OReply = Outfun | OutputReply -Object = Term | Binary -Outfun = fun(OCommand) -> OReply -Output = FileName | Outfun -OutputReply = Term -Term = term() -Value = Term</pre> </description> + + <datatypes> + <datatype> + <name name="file_name"/><br/> + </datatype> + <datatype> + <name name="file_names"/><br/> + </datatype> + <datatype> + <name name="i_command"/><br/> + </datatype> + <datatype> + <name name="i_reply"/><br/> + </datatype> + <datatype> + <name name="infun"/><br/> + </datatype> + <datatype> + <name name="input"/><br/> + </datatype> + <datatype> + <name name="input_reply"/><br/> + </datatype> + <datatype> + <name name="o_command"/><br/> + </datatype> + <datatype> + <name name="o_reply"/><br/> + </datatype> + <datatype> + <name name="object"/><br/> + </datatype> + <datatype> + <name name="outfun"/><br/> + </datatype> + <datatype> + <name name="output"/><br/> + </datatype> + <datatype> + <name name="output_reply"/><br/> + </datatype> + <datatype> + <name name="value"/><br/> + </datatype> + <datatype> + <name name="options"/><br/> + </datatype> + <datatype> + <name name="option"/><br/> + </datatype> + <datatype> + <name name="format"/><br/> + </datatype> + <datatype> + <name name="format_fun"/><br/> + </datatype> + <datatype> + <name name="header_length"/><br/> + </datatype> + <datatype> + <name name="key_pos"/><br/> + </datatype> + <datatype> + <name name="no_files"/><br/> + </datatype> + <datatype> + <name name="order"/><br/> + </datatype> + <datatype> + <name name="order_fun"/><br/> + </datatype> + <datatype> + <name name="size"/><br/> + </datatype> + <datatype> + <name name="tmp_directory"/><br/> + </datatype> + <datatype> + <name name="reason"/><br/> + </datatype> + </datatypes> + <funcs> <func> - <name>sort(FileName) -> Reply</name> - <name>sort(Input, Output) -> Reply</name> - <name>sort(Input, Output, Options) -> Reply</name> + <name name="sort" arity="1"/> <fsummary>Sort terms on files.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | InputReply | OutputReply</v> - </type> <desc> - <p>Sorts terms on files. - </p> - <p><c>sort(FileName)</c> is equivalent to - <c>sort([FileName], FileName)</c>. - </p> - <p><c>sort(Input, Output)</c> is equivalent to - <c>sort(Input, Output, [])</c>. - </p> - <p></p> + <p>Sorts terms on files. <c>sort(FileName)</c> is equivalent + to <c>sort([FileName], FileName)</c>.</p> + </desc> + </func> + <func> + <name name="sort" arity="2"/> + <name name="sort" arity="3"/> + <fsummary>Sort terms on files.</fsummary> + <desc> + <p>Sorts terms on files. <c>sort(Input, Output)</c> is + equivalent to <c>sort(Input, Output, [])</c>.</p> + </desc> + </func> + <func> + <name name="keysort" arity="2"/> + <fsummary>Sort terms on files by key.</fsummary> + <desc> + <p>Sorts tuples on files. <c>keysort(N, FileName)</c> is + equivalent to <c>keysort(N, [FileName], FileName)</c>.</p> </desc> </func> <func> - <name>keysort(KeyPos, FileName) -> Reply</name> - <name>keysort(KeyPos, Input, Output) -> Reply</name> - <name>keysort(KeyPos, Input, Output, Options) -> Reply</name> + <name name="keysort" arity="3"/> + <name name="keysort" arity="4"/> <fsummary>Sort terms on files by key.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | InputReply | OutputReply</v> - </type> <desc> <p>Sorts tuples on files. The sort is performed on the - element(s) mentioned in <c>KeyPos</c>. If two tuples - compare equal (<c>==</c>) on one element, next element according to - <c>KeyPos</c> is compared. The sort is stable. - </p> - <p><c>keysort(N, FileName)</c> is equivalent to - <c>keysort(N, [FileName], FileName)</c>. - </p> + element(s) mentioned in <c><anno>KeyPos</anno></c>. If two + tuples compare equal (<c>==</c>) on one element, next + element according to <c><anno>KeyPos</anno></c> + is compared. The sort is stable.</p> <p><c>keysort(N, Input, Output)</c> is equivalent to - <c>keysort(N, Input, Output, [])</c>. - </p> - <p></p> + <c>keysort(N, Input, Output, [])</c>.</p> </desc> </func> <func> - <name>merge(FileNames, Output) -> Reply</name> - <name>merge(FileNames, Output, Options) -> Reply</name> + <name name="merge" arity="2"/> + <name name="merge" arity="3"/> <fsummary>Merge terms on files.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | OutputReply</v> - </type> <desc> <p>Merges terms on files. Each input file is assumed to be - sorted. - </p> + sorted.</p> <p><c>merge(FileNames, Output)</c> is equivalent to - <c>merge(FileNames, Output, [])</c>. - </p> + <c>merge(FileNames, Output, [])</c>.</p> </desc> </func> <func> - <name>keymerge(KeyPos, FileNames, Output) -> Reply</name> - <name>keymerge(KeyPos, FileNames, Output, Options) -> Reply</name> + <name name="keymerge" arity="3"/> + <name name="keymerge" arity="4"/> <fsummary>Merge terms on files by key.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | OutputReply</v> - </type> <desc> <p>Merges tuples on files. Each input file is assumed to be - sorted on key(s). - </p> + sorted on key(s).</p> <p><c>keymerge(KeyPos, FileNames, Output)</c> is equivalent - to <c>keymerge(KeyPos, FileNames, Output, [])</c>. - </p> - <p></p> + to <c>keymerge(KeyPos, FileNames, Output, [])</c>.</p> </desc> </func> <func> - <name>check(FileName) -> Reply</name> - <name>check(FileNames, Options) -> Reply</name> + <name name="check" arity="1"/> + <name name="check" arity="2"/> <fsummary>Check whether terms on files are sorted.</fsummary> - <type> - <v>Reply = {ok, [Result]} | {error, Reason}</v> - <v>Result = {FileName, TermPosition, Term}</v> - <v>TermPosition = int() > 1</v> - </type> <desc> <p>Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. - </p> + file has position 1.</p> <p><c>check(FileName)</c> is equivalent to - <c>check([FileName], [])</c>. - </p> + <c>check([FileName], [])</c>.</p> </desc> </func> <func> - <name>keycheck(KeyPos, FileName) -> CheckReply</name> - <name>keycheck(KeyPos, FileNames, Options) -> Reply</name> + <name name="keycheck" arity="2"/> + <name name="keycheck" arity="3"/> <fsummary>Check whether terms on files are sorted by key.</fsummary> - <type> - <v>Reply = {ok, [Result]} | {error, Reason}</v> - <v>Result = {FileName, TermPosition, Term}</v> - <v>TermPosition = int() > 1</v> - </type> <desc> <p>Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. - </p> + file has position 1.</p> <p><c>keycheck(KeyPos, FileName)</c> is equivalent - to <c>keycheck(KeyPos, [FileName], [])</c>. - </p> - <p></p> + to <c>keycheck(KeyPos, [FileName], [])</c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index fab68ae77c..f3079c7337 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -41,62 +41,46 @@ <p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -filename() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). -dirname() = filename()</code> - </section> + <datatypes> + <datatype> + <name name="filename"/> + </datatype> + <datatype> + <name name="dirname"/> + </datatype> + </datatypes> <funcs> <func> - <name>ensure_dir(Name) -> ok | {error, Reason}</name> + <name name="ensure_dir" arity="1"/> <fsummary>Ensure that all parent directories for a file or directory exist.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - <v>Reason = posix() -- see file(3)</v> - </type> <desc> <p>The <c>ensure_dir/1</c> function ensures that all parent - directories for the given file or directory name <c>Name</c> + directories for the given file or directory name <c><anno>Name</anno></c> exist, trying to create them if necessary.</p> <p>Returns <c>ok</c> if all parent directories already exist - or could be created, or <c>{error, Reason}</c> if some parent + or could be created, or <c>{error, <anno>Reason</anno>}</c> if some parent directory does not exist and could not be created for some reason.</p> </desc> </func> <func> - <name>file_size(Filename) -> integer()</name> + <name name="file_size" arity="1"/> <fsummary>Return the size in bytes of the file.</fsummary> <desc> <p>The <c>file_size</c> function returns the size of the given file.</p> </desc> </func> <func> - <name>fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut </name> + <name name="fold_files" arity="5"/> <fsummary>Fold over all files matching a regular expression.</fsummary> - <type> - <v>Dir = dirname()</v> - <v>RegExp = regular_expression_string()</v> - <v>Recursive = true|false</v> - <v>Fun = fun(F, AccIn) -> AccOut</v> - <v>AccIn = AccOut = term()</v> - </type> <desc> <p>The <c>fold_files/5</c> function folds the function - <c>Fun</c> over all (regular) files <c>F</c> in the - directory <c>Dir</c> that match the regular expression <c>RegExp</c> + <c><anno>Fun</anno></c> over all (regular) files <c><anno>F</anno></c> in the + directory <c><anno>Dir</anno></c> that match the regular expression <c><anno>RegExp</anno></c> (see the <seealso marker="re">re</seealso> module for a description of the allowed regular expressions). - If <c>Recursive</c> is true all sub-directories to <c>Dir</c> + If <c><anno>Recursive</anno></c> is true all sub-directories to <c>Dir</c> are processed. The regular expression matching is done on just the filename without the directory part.</p> @@ -114,44 +98,32 @@ dirname() = filename()</code> </desc> </func> <func> - <name>is_dir(Name) -> true | false</name> + <name name="is_dir" arity="1"/> <fsummary>Test whether Name refer to a directory or not</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> - <p>The <c>is_dir/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_dir/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a directory, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_file(Name) -> true | false</name> + <name name="is_file" arity="1"/> <fsummary>Test whether Name refer to a file or directory.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> - <p>The <c>is_file/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_file/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a file or a directory, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_regular(Name) -> true | false</name> + <name name="is_regular" arity="1"/> <fsummary>Test whether Name refer to a (regular) file.</fsummary> - <type> - <v>Name = filename()</v> - </type> <desc> - <p>The <c>is_regular/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_regular/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a file (regular file), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>last_modified(Name) -> {{Year,Month,Day},{Hour,Min,Sec}} | 0</name> + <name name="last_modified" arity="1"/> <fsummary>Return the local date and time when a file was last modified.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> <p>The <c>last_modified/1</c> function returns the date and time the given file or directory was last modified, or 0 if the file @@ -159,14 +131,11 @@ dirname() = filename()</code> </desc> </func> <func> - <name>wildcard(Wildcard) -> list()</name> + <name name="wildcard" arity="1"/> <fsummary>Match filenames using Unix-style wildcards.</fsummary> - <type> - <v>Wildcard = filename() | dirname()</v> - </type> <desc> <p>The <c>wildcard/1</c> function returns a list of all files - that match Unix-style wildcard-string <c>Wildcard</c>.</p> + that match Unix-style wildcard-string <c><anno>Wildcard</anno></c>.</p> <p>The wildcard string looks like an ordinary filename, except that certain "wildcard characters" are interpreted in a special way. The following characters are special: @@ -226,15 +195,11 @@ dirname() = filename()</code> </desc> </func> <func> - <name>wildcard(Wildcard, Cwd) -> list()</name> + <name name="wildcard" arity="2"/> <fsummary>Match filenames using Unix-style wildcards starting at a specified directory.</fsummary> - <type> - <v>Wildcard = filename() | dirname()</v> - <v>Cwd = dirname()</v> - </type> <desc> <p>The <c>wildcard/2</c> function works like <c>wildcard/1</c>, - except that instead of the actual working directory, <c>Cwd</c> + except that instead of the actual working directory, <c><anno>Cwd</anno></c> will be used.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index cdee6e4a81..bc3a616d39 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -47,28 +47,12 @@ <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -name() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). - </code> - </section> <funcs> <func> - <name>absname(Filename) -> string()</name> + <name name="absname" arity="1"/> <fsummary>Convert a filename to an absolute name, relative the working directory</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Converts a relative <c>Filename</c> and returns an absolute + <p>Converts a relative <c><anno>Filename</anno></c> and returns an absolute name. No attempt is made to create the shortest absolute name, because this can give incorrect results on file systems which allow links.</p> @@ -95,44 +79,33 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>absname(Filename, Dir) -> string()</name> + <name name="absname" arity="2"/> <fsummary>Convert a filename to an absolute name, relative a specified directory</fsummary> - <type> - <v>Filename = name()</v> - <v>Dir = string()</v> - </type> <desc> <p>This function works like <c>absname/1</c>, except that the directory to which the file name should be made relative - is given explicitly in the <c>Dir</c> argument.</p> + is given explicitly in the <c><anno>Dir</anno></c> argument.</p> </desc> </func> <func> - <name>absname_join(Dir, Filename) -> string()</name> + <name name="absname_join" arity="2"/> <fsummary>Join an absolute directory with a relative filename</fsummary> - <type> - <v>Dir = string()</v> - <v>Filename = name()</v> - </type> <desc> <p>Joins an absolute directory with a relative filename. Similar to <c>join/2</c>, but on platforms with tight restrictions on raw filename length and no support for symbolic links (read: VxWorks), leading parent directory - components in <c>Filename</c> are matched against trailing - directory components in <c>Dir</c> so they can be removed + components in <c><anno>Filename</anno></c> are matched against trailing + directory components in <c><anno>Dir</anno></c> so they can be removed from the result - minimizing its length.</p> </desc> </func> <func> - <name>basename(Filename) -> string()</name> + <name name="basename" arity="1"/> <fsummary>Return the last component of a filename</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the last component of <c>Filename</c>, or - <c>Filename</c> itself if it does not contain any directory + <p>Returns the last component of <c><anno>Filename</anno></c>, or + <c><anno>Filename</anno></c> itself if it does not contain any directory separators.</p> <pre> 5> <input>filename:basename("foo").</input> @@ -144,14 +117,11 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>basename(Filename, Ext) -> string()</name> + <name name="basename" arity="2"/> <fsummary>Return the last component of a filename, stripped of the specified extension</fsummary> - <type> - <v>Filename = Ext = name()</v> - </type> <desc> - <p>Returns the last component of <c>Filename</c> with the - extension <c>Ext</c> stripped. This function should be used + <p>Returns the last component of <c><anno>Filename</anno></c> with the + extension <c><anno>Ext</anno></c> stripped. This function should be used to remove a specific extension which might, or might not, be there. Use <c>rootname(basename(Filename))</c> to remove an extension that exists, but you are not sure which one it is.</p> @@ -169,13 +139,10 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>dirname(Filename) -> string()</name> + <name name="dirname" arity="1"/> <fsummary>Return the directory part of a path name</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the directory part of <c>Filename</c>.</p> + <p>Returns the directory part of <c><anno>Filename</anno></c>.</p> <pre> 13> <input>filename:dirname("/usr/src/kalle.erl").</input> "/usr/src" @@ -187,13 +154,10 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>extension(Filename) -> string()</name> + <name name="extension" arity="1"/> <fsummary>Return the file extension</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the file extension of <c>Filename</c>, including + <p>Returns the file extension of <c><anno>Filename</anno></c>, including the period. Returns an empty string if there is no extension.</p> <pre> 15> <input>filename:extension("foo.erl").</input> @@ -203,11 +167,8 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>flatten(Filename) -> string()</name> + <name name="flatten" arity="1"/> <fsummary>Convert a filename to a flat string</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> <p>Converts a possibly deep list filename consisting of characters and atoms into the corresponding flat string @@ -215,14 +176,11 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>join(Components) -> string()</name> + <name name="join" arity="1"/> <fsummary>Join a list of filename components with directory separators</fsummary> - <type> - <v>Components = [string()]</v> - </type> <desc> - <p>Joins a list of file name <c>Components</c> with directory - separators. If one of the elements of <c>Components</c> + <p>Joins a list of file name <c><anno>Components</anno></c> with directory + separators. If one of the elements of <c><anno>Components</anno></c> includes an absolute path, for example <c>"/xxx"</c>, the preceding elements, if any, are removed from the result.</p> <p>The result is "normalized":</p> @@ -242,24 +200,18 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>join(Name1, Name2) -> string()</name> + <name name="join" arity="2"/> <fsummary>Join two filename components with directory separators</fsummary> - <type> - <v>Name1 = Name2 = string()</v> - </type> <desc> <p>Joins two file name components with directory separators. - Equivalent to <c>join([Name1, Name2])</c>.</p> + Equivalent to <c>join([<anno>Name1</anno>, <anno>Name2</anno>])</c>.</p> </desc> </func> <func> - <name>nativename(Path) -> string()</name> + <name name="nativename" arity="1"/> <fsummary>Return the native form of a file path</fsummary> - <type> - <v>Path = string()</v> - </type> <desc> - <p>Converts <c>Path</c> to a form accepted by the command shell + <p>Converts <c><anno>Path</anno></c> to a form accepted by the command shell and native applications on the current platform. On Windows, forward slashes is converted to backward slashes. On all platforms, the name is normalized as done by <c>join/1</c>.</p> @@ -272,7 +224,7 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>pathtype(Path) -> absolute | relative | volumerelative</name> + <name name="pathtype" arity="1"/> <fsummary>Return the type of a path</fsummary> <desc> <p>Returns the type of path, one of <c>absolute</c>, @@ -302,16 +254,13 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>rootname(Filename) -> string()</name> - <name>rootname(Filename, Ext) -> string()</name> + <name name="rootname" arity="1"/> + <name name="rootname" arity="2"/> <fsummary>Remove a filename extension</fsummary> - <type> - <v>Filename = Ext = name()</v> - </type> <desc> <p>Remove a filename extension. <c>rootname/2</c> works as <c>rootname/1</c>, except that the extension is removed only - if it is <c>Ext</c>.</p> + if it is <c><anno>Ext</anno></c>.</p> <pre> 20> <input>filename:rootname("/beam.src/kalle").</input> /beam.src/kalle" @@ -324,15 +273,11 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>split(Filename) -> Components</name> + <name name="split" arity="1"/> <fsummary>Split a filename into its path components</fsummary> - <type> - <v>Filename = name()</v> - <v>Components = [string()]</v> - </type> <desc> <p>Returns a list whose elements are the path components of - <c>Filename</c>.</p> + <c><anno>Filename</anno></c>.</p> <pre> 24> <input>filename:split("/usr/local/bin").</input> ["/","usr","local","bin"] @@ -343,47 +288,38 @@ name() = string() | atom() | DeepList | RawFilename </desc> </func> <func> - <name>find_src(Beam) -> {SourceFile, Options} | {error,{ErrorReason,Module}}</name> - <name>find_src(Beam, Rules) -> {SourceFile, Options} | {error,{ErrorReason,Module}}</name> + <name name="find_src" arity="1"/> + <name name="find_src" arity="2"/> <fsummary>Find the filename and compiler options for a module</fsummary> - <type> - <v>Beam = Module | Filename</v> - <v> Module = atom()</v> - <v> Filename = string() | atom()</v> - <v>SourceFile = string()</v> - <v>Options = [Opt]</v> - <v> Opt = {i, string()} | {outdir, string()} | {d, atom()}</v> - <v>ErrorReason = non_existing | preloaded | interpreted</v> - </type> <desc> <p>Finds the source filename and compiler options for a module. The result can be fed to <c>compile:file/2</c> in order to compile the file again.</p> - <p>The <c>Beam</c> argument, which can be a string or an atom, + <p>The <c><anno>Beam</anno></c> argument, which can be a string or an atom, specifies either the module name or the path to the source code, with or without the <c>".erl"</c> extension. In either case, the module must be known by the code server, i.e. - <c>code:which(Module)</c> must succeed.</p> - <p><c>Rules</c> describes how the source directory can be found, + <c>code:which(<anno>Module</anno>)</c> must succeed.</p> + <p><c><anno>Rules</anno></c> describes how the source directory can be found, when the object code directory is known. It is a list of - tuples <c>{BinSuffix, SourceSuffix}</c> and is interpreted + tuples <c>{<anno>BinSuffix</anno>, <anno>SourceSuffix</anno>}</c> and is interpreted as follows: If the end of the directory name where the object - is located matches <c>BinSuffix</c>, then the source code - directory has the same name, but with <c>BinSuffix</c> - replaced by <c>SourceSuffix</c>. <c>Rules</c> defaults to:</p> + is located matches <c><anno>BinSuffix</anno></c>, then the source code + directory has the same name, but with <c><anno>BinSuffix</anno></c> + replaced by <c><anno>SourceSuffix</anno></c>. <c><anno>Rules</anno></c> defaults to:</p> <code type="none"> [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}]</code> <p>If the source file is found in the resulting directory, then the function returns that location together with - <c>Options</c>. Otherwise, the next rule is tried, and so on.</p> + <c><anno>Options</anno></c>. Otherwise, the next rule is tried, and so on.</p> - <p>The function returns <c>{SourceFile, Options}</c> if it succeeds. - <c>SourceFile</c> is the absolute path to the source file - without the <c>".erl"</c> extension. <c>Options</c> include + <p>The function returns <c>{<anno>SourceFile</anno>, <anno>Options</anno>}</c> if it succeeds. + <c><anno>SourceFile</anno></c> is the absolute path to the source file + without the <c>".erl"</c> extension. <c><anno>Options</anno></c> include the options which are necessary to recompile the file with <c>compile:file/2</c>, but excludes options such as <c>report</c> or <c>verbose</c> which do not change the way - code is generated. The paths in the <c>{outdir, Path}</c> + code is generated. The paths in the <c>{outdir, <anno>Path</anno>}</c> and <c>{i, Path}</c> options are guaranteed to be absolute.</p> diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index 60d8bcbfa3..38de51322f 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -114,34 +114,32 @@ </list> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -gb_set() = a GB set</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-gb_set">gb_set()</marker></name> + <desc><p>A GB set.</p></desc> + </datatype> + <datatype> + <name name="iter"/> + <desc><p>A GB set iterator.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>add(Element, Set1) -> Set2</name> - <name>add_element(Element, Set1) -> Set2</name> + <name name="add" arity="2"/> + <name name="add_element" arity="2"/> <fsummary>Add a (possibly existing) element to a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> inserted. If <c>Element</c> is already an - element in <c>Set1</c>, nothing is changed.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted. If <c><anno>Element</anno></c> is already an + element in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> </func> <func> - <name>balance(Set1) -> Set2</name> + <name name="balance" arity="1"/> <fsummary>Rebalance tree representation of a gb_set</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Rebalances the tree representation of <c>Set1</c>. Note that + <p>Rebalances the tree representation of <c><anno>Set1</anno></c>. Note that this is rarely necessary, but may be motivated when a large number of elements have been deleted from the tree without further insertions. Rebalancing could then be forced in order @@ -150,208 +148,144 @@ gb_set() = a GB set</code> </desc> </func> <func> - <name>delete(Element, Set1) -> Set2</name> + <name name="delete" arity="2"/> <fsummary>Remove an element from a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> removed. Assumes that <c>Element</c> is present - in <c>Set1</c>.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> removed. Assumes that <c><anno>Element</anno></c> is present + in <c><anno>Set1</anno></c>.</p> </desc> </func> <func> - <name>delete_any(Element, Set1) -> Set2</name> - <name>del_element(Element, Set1) -> Set2</name> + <name name="delete_any" arity="2"/> + <name name="del_element" arity="2"/> <fsummary>Remove a (possibly non-existing) element from a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> removed. If <c>Element</c> is not an element - in <c>Set1</c>, nothing is changed.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> removed. If <c><anno>Element</anno></c> is not an element + in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> </func> <func> - <name>difference(Set1, Set2) -> Set3</name> - <name>subtract(Set1, Set2) -> Set3</name> + <name name="difference" arity="2"/> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns only the elements of <c>Set1</c> which are not also - elements of <c>Set2</c>.</p> + <p>Returns only the elements of <c><anno>Set1</anno></c> which are not also + elements of <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>empty() -> Set</name> - <name>new() -> Set</name> + <name name="empty" arity="0"/> + <name name="new" arity="0"/> <fsummary>Return an empty gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns a new empty gb_set.</p> </desc> </func> <func> - <name>filter(Pred, Set1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter gb_set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v> E = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Filters elements in <c>Set1</c> using predicate function - <c>Pred</c>.</p> + <p>Filters elements in <c><anno>Set1</anno></c> using predicate function + <c><anno>Pred</anno></c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Set) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over gb_set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v> E = term()</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Folds <c>Function</c> over every element in <c>Set</c> + <p>Folds <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>from_list(List) -> Set</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into a gb_set</fsummary> - <type> - <v>List = [term()]</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns a gb_set of the elements in <c>List</c>, where - <c>List</c> may be unordered and contain duplicates.</p> + <p>Returns a gb_set of the elements in <c><anno>List</anno></c>, where + <c><anno>List</anno></c> may be unordered and contain duplicates.</p> </desc> </func> <func> - <name>from_ordset(List) -> Set</name> + <name name="from_ordset" arity="1"/> <fsummary>Make a gb_set from an ordset list</fsummary> - <type> - <v>List = [term()]</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Turns an ordered-set list <c>List</c> into a gb_set. The list + <p>Turns an ordered-set list <c><anno>List</anno></c> into a gb_set. The list must not contain duplicates.</p> </desc> </func> <func> - <name>insert(Element, Set1) -> Set2</name> + <name name="insert" arity="2"/> <fsummary>Add a new element to a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> inserted. Assumes that <c>Element</c> is not - present in <c>Set1</c>.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted. Assumes that <c><anno>Element</anno></c> is not + present in <c><anno>Set1</anno></c>.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns the intersection of <c>Set1</c> and <c>Set2</c>.</p> + <p>Returns the intersection of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>intersection(SetList) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of gb_sets</fsummary> - <type> - <v>SetList = [gb_set()]</v> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of gb_sets.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two gb_sets are disjoint</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set1</c> and - <c>Set2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty(Set) -> bool()</name> + <name name="is_empty" arity="1"/> <fsummary>Test for empty gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> is an empty set, and + <p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_member(Element, Set) -> bool()</name> - <name>is_element(Element, Set) -> bool()</name> + <name name="is_member" arity="2"/> + <name name="is_element" arity="2"/> <fsummary>Test for membership of a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Set</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Set</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_set(Term) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for a gb_set</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> appears to be a gb_set, + <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a gb_set, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Set1</c> is - also a member of <c>Set2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c> is + also a member of <c><anno>Set2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>iterator(Set) -> Iter</name> + <name name="iterator" arity="1"/> <fsummary>Return an iterator for a gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - <v>Iter = term()</v> - </type> <desc> <p>Returns an iterator that can be used for traversing the - entries of <c>Set</c>; see <c>next/1</c>. The implementation + entries of <c><anno>Set</anno></c>; see <c>next/1</c>. The implementation of this is very efficient; traversing the whole set using <c>next/1</c> is only slightly slower than getting the list of all elements using <c>to_list/1</c> and traversing that. @@ -361,118 +295,84 @@ gb_set() = a GB set</code> </desc> </func> <func> - <name>largest(Set) -> term()</name> + <name name="largest" arity="1"/> <fsummary>Return largest element</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the largest element in <c>Set</c>. Assumes that - <c>Set</c> is nonempty.</p> + <p>Returns the largest element in <c><anno>Set</anno></c>. Assumes that + <c><anno>Set</anno></c> is nonempty.</p> </desc> </func> <func> - <name>next(Iter1) -> {Element, Iter2} | none</name> + <name name="next" arity="1"/> <fsummary>Traverse a gb_set with an iterator</fsummary> - <type> - <v>Iter1 = Iter2 = Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Iter2}</c> where <c>Element</c> is the - smallest element referred to by the iterator <c>Iter1</c>, - and <c>Iter2</c> is the new iterator to be used for + <p>Returns <c>{<anno>Element</anno>, <anno>Iter2</anno>}</c> where <c><anno>Element</anno></c> is the + smallest element referred to by the iterator <c><anno>Iter1</anno></c>, + and <c><anno>Iter2</anno></c> is the new iterator to be used for traversing the remaining elements, or the atom <c>none</c> if no elements remain.</p> </desc> </func> <func> - <name>singleton(Element) -> gb_set()</name> + <name name="singleton" arity="1"/> <fsummary>Return a gb_set with one element</fsummary> - <type> - <v>Element = term()</v> - </type> <desc> - <p>Returns a gb_set containing only the element <c>Element</c>.</p> + <p>Returns a gb_set containing only the element <c><anno>Element</anno></c>.</p> </desc> </func> <func> - <name>size(Set) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the number of elements in <c>Set</c>.</p> + <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> </desc> </func> <func> - <name>smallest(Set) -> term()</name> + <name name="smallest" arity="1"/> <fsummary>Return smallest element</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the smallest element in <c>Set</c>. Assumes that - <c>Set</c> is nonempty.</p> + <p>Returns the smallest element in <c><anno>Set</anno></c>. Assumes that + <c><anno>Set</anno></c> is nonempty.</p> </desc> </func> <func> - <name>take_largest(Set1) -> {Element, Set2}</name> + <name name="take_largest" arity="1"/> <fsummary>Extract largest element</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - <v>Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Set2}</c>, where <c>Element</c> is the - largest element in <c>Set1</c>, and <c>Set2</c> is this set - with <c>Element</c> deleted. Assumes that <c>Set1</c> is + <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where <c><anno>Element</anno></c> is the + largest element in <c><anno>Set1</anno></c>, and <c><anno>Set2</anno></c> is this set + with <c><anno>Element</anno></c> deleted. Assumes that <c><anno>Set1</anno></c> is nonempty.</p> </desc> </func> <func> - <name>take_smallest(Set1) -> {Element, Set2}</name> + <name name="take_smallest" arity="1"/> <fsummary>Extract smallest element</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - <v>Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Set2}</c>, where <c>Element</c> is the - smallest element in <c>Set1</c>, and <c>Set2</c> is this set - with <c>Element</c> deleted. Assumes that <c>Set1</c> is + <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where <c><anno>Element</anno></c> is the + smallest element in <c><anno>Set1</anno></c>, and <c><anno>Set2</anno></c> is this set + with <c><anno>Element</anno></c> deleted. Assumes that <c><anno>Set1</anno></c> is nonempty.</p> </desc> </func> <func> - <name>to_list(Set) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a gb_set into a list</fsummary> - <type> - <v>Set = gb_set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Set</c> as a list.</p> + <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns the merged (union) gb_set of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the merged (union) gb_set of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>union(SetList) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of gb_sets</fsummary> - <type> - <v>SetList = [gb_set()]</v> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns the merged (union) gb_set of the list of gb_sets.</p> </desc> diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index 94f40c28bd..65c866efbe 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -57,20 +57,22 @@ trees. Behaviour is logarithmic (as it should be).</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -gb_tree() = a GB tree</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-gb_tree">gb_tree()</marker></name> + <desc><p>A GB tree.</p></desc> + </datatype> + <datatype> + <name name="iter"/> + <desc><p>A GB tree iterator.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>balance(Tree1) -> Tree2</name> + <name name="balance" arity="1"/> <fsummary>Rebalance a tree</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Rebalances <c>Tree1</c>. Note that this is rarely necessary, + <p>Rebalances <c><anno>Tree1</anno></c>. Note that this is rarely necessary, but may be motivated when a large number of nodes have been deleted from the tree without further insertions. Rebalancing could then be forced in order to minimise lookup times, since @@ -78,139 +80,97 @@ gb_tree() = a GB tree</code> </desc> </func> <func> - <name>delete(Key, Tree1) -> Tree2</name> + <name name="delete" arity="2"/> <fsummary>Remove a node from a tree</fsummary> - <type> - <v>Key = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Removes the node with key <c>Key</c> from <c>Tree1</c>; + <p>Removes the node with key <c><anno>Key</anno></c> from <c><anno>Tree1</anno></c>; returns new tree. Assumes that the key is present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>delete_any(Key, Tree1) -> Tree2</name> + <name name="delete_any" arity="2"/> <fsummary>Remove a (possibly non-existing) node from a tree</fsummary> - <type> - <v>Key = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Removes the node with key <c>Key</c> from <c>Tree1</c> if + <p>Removes the node with key <c><anno>Key</anno></c> from <c><anno>Tree1</anno></c> if the key is present in the tree, otherwise does nothing; returns new tree.</p> </desc> </func> <func> - <name>empty() -> Tree</name> + <name name="empty" arity="0"/> <fsummary>Return an empty tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> <p>Returns a new empty tree</p> </desc> </func> <func> - <name>enter(Key, Val, Tree1) -> Tree2</name> + <name name="enter" arity="3"/> <fsummary>Insert or update key with value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Inserts <c>Key</c> with value <c>Val</c> into <c>Tree1</c> if + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c> if the key is not present in the tree, otherwise updates - <c>Key</c> to value <c>Val</c> in <c>Tree1</c>. Returns the + <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>. Returns the new tree.</p> </desc> </func> <func> - <name>from_orddict(List) -> Tree</name> + <name name="from_orddict" arity="1"/> <fsummary>Make a tree from an orddict</fsummary> - <type> - <v>List = [{Key, Val}]</v> - <v> Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Turns an ordered list <c>List</c> of key-value tuples into a + <p>Turns an ordered list <c><anno>List</anno></c> of key-value tuples into a tree. The list must not contain duplicate keys.</p> </desc> </func> <func> - <name>get(Key, Tree) -> Val</name> + <name name="get" arity="2"/> <fsummary>Look up a key in a tree, if present</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Retrieves the value stored with <c>Key</c> in <c>Tree</c>. + <p>Retrieves the value stored with <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>. Assumes that the key is present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>lookup(Key, Tree) -> {value, Val} | none</name> + <name name="lookup" arity="2"/> <fsummary>Look up a key in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Looks up <c>Key</c> in <c>Tree</c>; returns - <c>{value, Val}</c>, or <c>none</c> if <c>Key</c> is not + <p>Looks up <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>; returns + <c>{value, <anno>Val</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not present.</p> </desc> </func> <func> - <name>insert(Key, Val, Tree1) -> Tree2</name> + <name name="insert" arity="3"/> <fsummary>Insert a new key and value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Inserts <c>Key</c> with value <c>Val</c> into <c>Tree1</c>; + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is not present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>is_defined(Key, Tree) -> bool()</name> + <name name="is_defined" arity="2"/> <fsummary>Test for membership of a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Key</c> is present in <c>Tree</c>, + <p>Returns <c>true</c> if <c><anno>Key</anno></c> is present in <c><anno>Tree</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_empty(Tree) -> bool()</name> + <name name="is_empty" arity="1"/> <fsummary>Test for empty tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Tree</c> is an empty tree, and + <p>Returns <c>true</c> if <c><anno>Tree</anno></c> is an empty tree, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>iterator(Tree) -> Iter</name> + <name name="iterator" arity="1"/> <fsummary>Return an iterator for a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Iter = term()</v> - </type> <desc> <p>Returns an iterator that can be used for traversing the - entries of <c>Tree</c>; see <c>next/1</c>. The implementation + entries of <c><anno>Tree</anno></c>; see <c>next/1</c>. The implementation of this is very efficient; traversing the whole tree using <c>next/1</c> is only slightly slower than getting the list of all elements using <c>to_list/1</c> and traversing that. @@ -220,141 +180,99 @@ gb_tree() = a GB tree</code> </desc> </func> <func> - <name>keys(Tree) -> [Key]</name> + <name name="keys" arity="1"/> <fsummary>Return a list of the keys in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = term()</v> - </type> <desc> - <p>Returns the keys in <c>Tree</c> as an ordered list.</p> + <p>Returns the keys in <c><anno>Tree</anno></c> as an ordered list.</p> </desc> </func> <func> - <name>largest(Tree) -> {Key, Val}</name> + <name name="largest" arity="1"/> <fsummary>Return largest key and value</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val}</c>, where <c>Key</c> is the largest - key in <c>Tree</c>, and <c>Val</c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the largest + key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>map(Function, Tree1) -> Tree2</name> + <name name="map" arity="2"/> <fsummary>Return largest key and value</fsummary> - <type> - <v>Function = fun(K, V1) -> V2</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> - <desc><p>maps the function F(K, V1) -> V2 to all key-value pairs - of the tree Tree1 and returns a new tree Tree2 with the same set of keys - as Tree1 and the new set of values V2.</p> + <desc><p>Maps the function F(<anno>K</anno>, <anno>V1</anno>) -> <anno>V2</anno> to all key-value pairs + of the tree <c><anno>Tree1</anno></c> and returns a new tree <c><anno>Tree2</anno></c> with the same set of keys + as <c><anno>Tree1</anno></c> and the new set of values <c><anno>V2</anno></c>.</p> </desc> </func> <func> - <name>next(Iter1) -> {Key, Val, Iter2} | none</name> + <name name="next" arity="1"/> <fsummary>Traverse a tree with an iterator</fsummary> - <type> - <v>Iter1 = Iter2 = Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Iter2}</c> where <c>Key</c> is the - smallest key referred to by the iterator <c>Iter1</c>, and - <c>Iter2</c> is the new iterator to be used for + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the + smallest key referred to by the iterator <c><anno>Iter1</anno></c>, and + <c><anno>Iter2</anno></c> is the new iterator to be used for traversing the remaining nodes, or the atom <c>none</c> if no nodes remain.</p> </desc> </func> <func> - <name>size(Tree) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of nodes in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns the number of nodes in <c>Tree</c>.</p> + <p>Returns the number of nodes in <c><anno>Tree</anno></c>.</p> </desc> </func> <func> - <name>smallest(Tree) -> {Key, Val}</name> + <name name="smallest" arity="1"/> <fsummary>Return smallest key and value</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val}</c>, where <c>Key</c> is the smallest - key in <c>Tree</c>, and <c>Val</c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the smallest + key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>take_largest(Tree1) -> {Key, Val, Tree2}</name> + <name name="take_largest" arity="1"/> <fsummary>Extract largest key and value</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Tree2}</c>, where <c>Key</c> is the - largest key in <c>Tree1</c>, <c>Val</c> is the value - associated with this key, and <c>Tree2</c> is this tree with + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + largest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>take_smallest(Tree1) -> {Key, Val, Tree2}</name> + <name name="take_smallest" arity="1"/> <fsummary>Extract smallest key and value</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Tree2}</c>, where <c>Key</c> is the - smallest key in <c>Tree1</c>, <c>Val</c> is the value - associated with this key, and <c>Tree2</c> is this tree with + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + smallest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>to_list(Tree) -> [{Key, Val}]</name> + <name name="to_list" arity="1"/> <fsummary>Convert a tree into a list</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> <p>Converts a tree into an ordered list of key-value tuples.</p> </desc> </func> <func> - <name>update(Key, Val, Tree1) -> Tree2</name> + <name name="update" arity="3"/> <fsummary>Update a key to new value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Updates <c>Key</c> to value <c>Val</c> in <c>Tree1</c>; + <p>Updates <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is present in the tree.</p> </desc> </func> <func> - <name>values(Tree) -> [Val]</name> + <name name="values" arity="1"/> <fsummary>Return a list of the values in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Val = term()</v> - </type> <desc> - <p>Returns the values in <c>Tree</c> as an ordered list, sorted + <p>Returns the values in <c><anno>Tree</anno></c> as an ordered list, sorted by their corresponding keys. Duplicates are not removed.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index 2234a62ac3..24bcb419fe 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -100,6 +100,20 @@ gen_event:stop -----> Module:terminate/2 the specified event manager does not exist or if bad arguments are given.</p> </description> + <datatypes> + <datatype> + <name name="handler"/> + </datatype> + <datatype> + <name name="handler_args"/> + </datatype> + <datatype> + <name name="add_handler_ret"/> + </datatype> + <datatype> + <name name="del_handler_ret"/> + </datatype> + </datatypes> <funcs> <func> <name>start_link() -> Result</name> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index 41e3e92c59..af9c75d546 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -43,7 +43,7 @@ <p>As of R13A, data supplied to the <seealso marker="#put_chars/2">put_chars</seealso> function should be in the - <c>chardata()</c> format described below. This means that programs + <seealso marker="unicode#type-chardata"><c>unicode:chardata()</c></seealso> format. This means that programs supplying binaries to this function need to convert them to UTF-8 before trying to output the data on an <c>io_device()</c>.</p> @@ -64,76 +64,84 @@ </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -io_device() - as returned by file:open/2, a process handling IO protocols</code> - - <code type="none"> -unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint - -chardata() = charlist() | unicode_binary() + <datatypes> + <datatype> + <name name="device"/> + <desc> + <p>Either <c>standard_io</c>, <c>standard_error</c>, a + registered name, or a pid handling IO protocols (returned from + <seealso marker="file#open/2">file:open/2</seealso>).</p> + </desc> + </datatype> + <datatype> + <name name="opt_pair"/> + </datatype> + <datatype> + <name name="expand_fun"/> + </datatype> + <datatype> + <name name="encoding"/> + </datatype> + <datatype> + <name name="setopt"/> + </datatype> + <datatype> + <name name="format"/> + </datatype> + <datatype> + <name name="line"/> + </datatype> + <datatype> + <name name="prompt"/> + </datatype> + <datatype> + <name name="request_error"/> + </datatype> + <datatype> + <name name="error_description"/> + <desc><p>Whatever the I/O-server sends.</p></desc> + </datatype> + </datatypes> -charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list</code> - </section> <funcs> <func> - <name>columns() -> {ok,int()} | {error, enotsup}</name> - <name>columns(IoDevice) -> {ok,int()} | {error, enotsup}</name> + <name name="columns" arity="0"/> + <name name="columns" arity="1"/> <fsummary>Get the number of columns of a device</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> <p>Retrieves the number of columns of the - <c>IoDevice</c> (i.e. the width of a terminal). The function + <c><anno>IoDevice</anno></c> (i.e. the width of a terminal). The function only succeeds for terminal devices, for all other devices the function returns <c>{error, enotsup}</c></p> </desc> </func> <func> - <name>put_chars(IoData) -> ok</name> - <name>put_chars(IoDevice, IoData) -> ok</name> + <name name="put_chars" arity="1"/> + <name name="put_chars" arity="2"/> <fsummary>Write a list of characters</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>IoData = chardata()</v> - </type> <desc> - <p>Writes the characters of <c>IoData</c> to the io_server() - (<c>IoDevice</c>).</p> + <p>Writes the characters of <c><anno>CharData</anno></c> to the io_server() + (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>nl() -> ok</name> - <name>nl(IoDevice) -> ok</name> + <name name="nl" arity="0"/> + <name name="nl" arity="1"/> <fsummary>Write a newline</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> - <p>Writes new line to the standard output (<c>IoDevice</c>).</p> + <p>Writes new line to the standard output (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>get_chars(Prompt, Count) -> Data | eof</name> - <name>get_chars(IoDevice, Prompt, Count) -> Data | eof</name> + <name name="get_chars" arity="2"/> + <name name="get_chars" arity="3"/> <fsummary>Read a specified number of characters</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Count = int()</v> - <v>Data = [ unicode_char() ] | unicode_binary()</v> - </type> <desc> - <p>Reads <c>Count</c> characters from standard input - (<c>IoDevice</c>), prompting it with <c>Prompt</c>. It + <p>Reads <c><anno>Count</anno></c> characters from standard input + (<c><anno>IoDevice</anno></c>), prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>Data</c></tag> + <tag><c><anno>Data</anno></c></tag> <item> <p>The input characters. If the device supports Unicode, the data may represent codepoints larger than 255 (the @@ -145,7 +153,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,Reason}</c></tag> + <tag><c>{error,<anno>Reason</anno>}</c></tag> <item> <p>Other (rare) error condition, for instance <c>{error,estale}</c> if reading from an NFS file system.</p> @@ -154,19 +162,14 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>get_line(Prompt) -> Data | eof | {error,Reason}</name> - <name>get_line(IoDevice, Prompt) -> Data | eof | {error,Reason}</name> + <name name="get_line" arity="1"/> + <name name="get_line" arity="2"/> <fsummary>Read a line</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Data = [ unicode_char() ] | unicode_binary()</v> - </type> <desc> - <p>Reads a line from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. It returns:</p> + <p>Reads a line from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>Data</c></tag> + <tag><c><anno>Data</anno></c></tag> <item> <p>The characters in the line terminated by a LF (or end of file). If the device supports Unicode, @@ -179,7 +182,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,Reason}</c></tag> + <tag><c>{error,<anno>Reason</anno>}</c></tag> <item> <p>Other (rare) error condition, for instance <c>{error,estale}</c> if reading from an NFS file system.</p> @@ -188,15 +191,9 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>getopts() -> Opts</name> - <name>getopts(IoDevice) -> Opts</name> + <name name="getopts" arity="0"/> + <name name="getopts" arity="1"/> <fsummary>Get the supported options and values from an I/O-server</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Opts = [Opt]</v> - <v> Opt = {atom(),Value}</v> - <v> Value = term()</v> - </type> <desc> <p>This function requests all available options and their current values for a specific io_device(). Example:</p> <pre> @@ -216,18 +213,11 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>setopts(Opts) -> ok | {error, Reason}</name> - <name>setopts(IoDevice, Opts) -> ok | {error, Reason}</name> + <name name="setopts" arity="1"/> + <name name="setopts" arity="2"/> <fsummary>Set options</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Opts = [Opt]</v> - <v> Opt = atom() | {atom(),Value}</v> - <v> Value = term()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Set options for the io_device() (<c>IoDevice</c>).</p> + <p>Set options for the io_device() (<c><anno>IoDevice</anno></c>).</p> <p>Possible options and values vary depending on the actual io_device(). For a list of supported options and their current values @@ -236,17 +226,17 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <p>The options and values supported by the current OTP io_devices are:</p> <taglist> - <tag><c>binary, list or {binary, bool()}</c></tag> + <tag><c>binary, list or {binary, boolean()}</c></tag> <item> <p>If set in binary mode (binary or {binary,true}), the io_server() sends binary data (encoded in UTF-8) as answers to the get_line, get_chars and, if possible, get_until requests (see the I/O protocol description in STDLIB User's Guide for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected device.</p> <p>By default, all io_devices in OTP are set in list mode, but the io functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p> <p>This option is supported by the standard shell (group.erl), the 'oldshell' (user.erl) and the file I/O servers.</p> </item> - <tag><c>{echo, bool()}</c></tag> + <tag><c>{echo, boolean()}</c></tag> <item> <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (group.erl)</p> </item> - <tag><c>{expand_fun, fun()}</c></tag> + <tag><c>{expand_fun, expand_fun()}</c></tag> <item> <p>Provide a function for tab-completion (expansion) like the erlang shell. This function is called @@ -288,35 +278,24 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>write(Term) -> ok</name> - <name>write(IoDevice, Term) -> ok</name> + <name name="write" arity="1"/> + <name name="write" arity="2"/> <fsummary>Write a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Term = term()</v> - </type> <desc> - <p>Writes the term <c>Term</c> to the standard output - (<c>IoDevice</c>).</p> + <p>Writes the term <c><anno>Term</anno></c> to the standard output + (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>read(Prompt) -> Result</name> - <name>read(IoDevice, Prompt) -> Result</name> + <name name="read" arity="1"/> + <name name="read" arity="2"/> <fsummary>Read a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Result = {ok, Term} | eof | {error, ErrorInfo}</v> - <v> Term = term()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads a term <c>Term</c> from the standard input - (<c>IoDevice</c>), prompting it with <c>Prompt</c>. It + <p>Reads a term <c><anno>Term</anno></c> from the standard input + (<c><anno>IoDevice</anno></c>), prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>{ok, Term}</c></tag> + <tag><c>{ok, <anno>Term</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> @@ -324,7 +303,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> @@ -332,31 +311,22 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>read(IoDevice, Prompt, StartLine) -> Result</name> + <name name="read" arity="3"/> <fsummary>Read a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Term, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Term = term()</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads a term <c>Term</c> from <c>IoDevice</c>, prompting it - with <c>Prompt</c>. Reading starts at line number - <c>StartLine</c>. It returns:</p> + <p>Reads a term <c><anno>Term</anno></c> from <c><anno>IoDevice</anno></c>, prompting it + with <c><anno>Prompt</anno></c>. Reading starts at line number + <c><anno>StartLine</anno></c>. It returns:</p> <taglist> - <tag><c>{ok, Term, EndLine}</c></tag> + <tag><c>{ok, Term, <anno>EndLine</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, <anno>EndLine</anno>}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLine</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> @@ -364,24 +334,19 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>fwrite(Format) -></name> - <name>fwrite(Format, Data) -> ok</name> - <name>fwrite(IoDevice, Format, Data) -> ok</name> - <name>format(Format) -></name> - <name>format(Format, Data) -> ok</name> - <name>format(IoDevice, Format, Data) -> ok</name> + <name name="fwrite" arity="1"/> + <name name="fwrite" arity="2"/> + <name name="fwrite" arity="3"/> + <name name="format" arity="1"/> + <name name="format" arity="2"/> + <name name="format" arity="3"/> <fsummary>Write formatted output</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Format = atom() | string() | binary()</v> - <v>Data = [term()]</v> - </type> <desc> - <p>Writes the items in <c>Data</c> (<c>[]</c>) on the standard - output (<c>IoDevice</c>) in accordance with <c>Format</c>. - <c>Format</c> contains plain characters which are copied to + <p>Writes the items in <c><anno>Data</anno></c> (<c>[]</c>) on the standard + output (<c><anno>IoDevice</anno></c>) in accordance with <c><anno>Format</anno></c>. + <c><anno>Format</anno></c> contains plain characters which are copied to the output device, and control sequences for formatting, see - below. If <c>Format</c> is an atom or a binary, it is first + below. If <c><anno>Format</anno></c> is an atom or a binary, it is first converted to a list with the aid of <c>atom_to_list/1</c> or <c>binary_to_list/1</c>.</p> <pre> @@ -474,7 +439,7 @@ ok</pre> <item> <p>Prints the argument with the <c>string</c> syntax. The argument is, if no Unicode translation modifier is present, an - <seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is chardata(), meaning that binaries are in UTF-8. The characters + <seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters are printed without quotes. The string is first truncated by the given precision and then padded and justified to the given field width. The default precision is the field width.</p> @@ -673,23 +638,15 @@ ok </desc> </func> <func> - <name>fread(Prompt, Format) -> Result</name> - <name>fread(IoDevice, Prompt, Format) -> Result</name> + <name name="fread" arity="2"/> + <name name="fread" arity="3"/> <fsummary>Read formatted input</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Format = string()</v> - <v>Result = {ok, Terms} | eof | {error, What}</v> - <v> Terms = [term()]</v> - <v> What = term()</v> - </type> <desc> - <p>Reads characters from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Interprets the characters in - accordance with <c>Format</c>. <c>Format</c> contains control + <p>Reads characters from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. Interprets the characters in + accordance with <c><anno>Format</anno></c>. <c><anno>Format</anno></c> contains control sequences which directs the interpretation of the input.</p> - <p><c>Format</c> may contain:</p> + <p><c><anno>Format</anno></c> may contain:</p> <list type="bulleted"> <item> <p>White space characters (SPACE, TAB and NEWLINE) which @@ -803,19 +760,19 @@ Prompt> <input><Character beyond latin1 range not printable in this medium> </taglist> <p>It returns:</p> <taglist> - <tag><c>{ok, Terms}</c></tag> + <tag><c>{ok, <anno>Terms</anno>}</c></tag> <item> - <p>The read was successful and <c>Terms</c> is the list + <p>The read was successful and <c><anno>Terms</anno></c> is the list of successfully matched and read items.</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> <p>The read operation failed and the parameter - <c>What</c> gives a hint about the error.</p> + <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> </item> @@ -834,33 +791,21 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </desc> </func> <func> - <name>rows() -> {ok,int()} | {error, enotsup}</name> - <name>rows(IoDevice) -> {ok,int()} | {error, enotsup}</name> + <name name="rows" arity="0"/> + <name name="rows" arity="1"/> <fsummary>Get the number of rows of a device</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> <p>Retrieves the number of rows of the - <c>IoDevice</c> (i.e. the height of a terminal). The function + <c><anno>IoDevice</anno></c> (i.e. the height of a terminal). The function only succeeds for terminal devices, for all other devices the function returns <c>{error, enotsup}</c></p> </desc> </func> <func> - <name>scan_erl_exprs(Prompt) -></name> - <name>scan_erl_exprs(Prompt, StartLine) -> Result</name> - <name>scan_erl_exprs(IoDevice, Prompt, StartLine) -> Result</name> + <name name="scan_erl_exprs" arity="1"/> + <name name="scan_erl_exprs" arity="2"/> + <name name="scan_erl_exprs" arity="3"/> <fsummary>Read and tokenize Erlang expressions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Tokens -- see erl_scan(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> <p>Reads data from the standard input (<c>IoDevice</c>), prompting it with <c>Prompt</c>. Reading starts at line number @@ -876,7 +821,7 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> @@ -892,23 +837,14 @@ enter><input>1.0er.</input> </desc> </func> <func> - <name>scan_erl_form(Prompt) -></name> - <name>scan_erl_form(Prompt, StartLine) -> Result</name> - <name>scan_erl_form(IoDevice, Prompt, StartLine) -> Result</name> + <name name="scan_erl_form" arity="1"/> + <name name="scan_erl_form" arity="2"/> + <name name="scan_erl_form" arity="3"/> <fsummary>Read and tokenize an Erlang form</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Tokens -- see erl_scan(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized as if it were an + <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number + <c><anno>StartLine</anno></c> (1). The data is tokenized as if it were an Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final <c>'.'</c> is reached. This last token is also returned. The return values are the @@ -916,27 +852,19 @@ enter><input>1.0er.</input> </desc> </func> <func> - <name>parse_erl_exprs(Prompt) -></name> - <name>parse_erl_exprs(Prompt, StartLine) -> Result</name> - <name>parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result</name> + <name name="parse_erl_exprs" arity="1"/> + <name name="parse_erl_exprs" arity="2"/> + <name name="parse_erl_exprs" arity="3"/> + <type name="parse_ret"/> <fsummary>Read, tokenize and parse Erlang expressions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Expr_list, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Expr_list -- see erl_parse(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized and parsed as if + <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number + <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if it were a sequence of Erlang expressions until a final '.' is reached. It returns:</p> <taglist> - <tag><c>{ok, Expr_list, EndLine}</c></tag> + <tag><c>{ok, ExprList, EndLine}</c></tag> <item> <p>The parsing was successful.</p> </item> @@ -944,7 +872,7 @@ enter><input>1.0er.</input> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> @@ -960,23 +888,15 @@ enter><input>abc("hey".</input> </desc> </func> <func> - <name>parse_erl_form(Prompt) -></name> - <name>parse_erl_form(Prompt, StartLine) -> Result</name> - <name>parse_erl_form(IoDevice, Prompt, StartLine) -> Result</name> + <name name="parse_erl_form" arity="1"/> + <name name="parse_erl_form" arity="2"/> + <name name="parse_erl_form" arity="3"/> + <type name="parse_form_ret"/> <fsummary>Read, tokenize and parse an Erlang form</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, AbsForm, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> AbsForm -- see erl_parse(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized and parsed as if + <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number + <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if it were an Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final '.' is reached. It returns:</p> @@ -989,7 +909,7 @@ enter><input>abc("hey".</input> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 399f968c5f..506c1792f1 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,14 +38,22 @@ flattening deep lists.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -chars() = [char() | chars()]</code> - </section> + <datatypes> + <datatype> + <name name="chars"/> + </datatype> + <datatype> + <name name="continuation"/> + <desc><p>A continuation as returned by <seealso marker="#fread/3"><c>fread/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="depth"/> + </datatype> + </datatypes> <funcs> <func> - <name>nl() -> chars()</name> + <name name="nl" arity="0"/> <fsummary>Write a newline</fsummary> <desc> <p>Returns a character list which represents a new line @@ -53,16 +61,12 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>write(Term) -></name> - <name>write(Term, Depth) -> chars()</name> + <name name="write" arity="1"/> + <name name="write" arity="2"/> <fsummary>Write a term</fsummary> - <type> - <v>Term = term()</v> - <v>Depth = int()</v> - </type> <desc> - <p>Returns a character list which represents <c>Term</c>. The - <c>Depth</c> (-1) argument controls the depth of the + <p>Returns a character list which represents <c><anno>Term</anno></c>. The + <c><anno>Depth</anno></c> (-1) argument controls the depth of the structures written. When the specified depth is reached, everything below this level is replaced by "...". For example:</p> @@ -74,36 +78,26 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>print(Term) -></name> - <name>print(Term, Column, LineLength, Depth) -> chars()</name> + <name name="print" arity="1"/> + <name name="print" arity="4"/> <fsummary>Pretty print a term</fsummary> - <type> - <v>Term = term()</v> - <v>Column = LineLenght = Depth = int()</v> - </type> <desc> <p>Also returns a list of characters which represents - <c>Term</c>, but breaks representations which are longer than + <c><anno>Term</anno></c>, but breaks representations which are longer than one line into many lines and indents each line sensibly. It also tries to detect and output lists of printable characters - as strings. <c>Column</c> is the starting column (1), - <c>LineLength</c> the maximum line length (80), and - <c>Depth</c> (-1) the maximum print depth.</p> + as strings. <c><anno>Column</anno></c> is the starting column (1), + <c><anno>LineLength</anno></c> the maximum line length (80), and + <c><anno>Depth</anno></c> (-1) the maximum print depth.</p> </desc> </func> <func> - <name>fwrite(Format, Data) -></name> - <name>format(Format, Data) -> chars() | UnicodeList</name> + <name name="fwrite" arity="2"/> + <name name="format" arity="2"/> <fsummary>Write formatted output</fsummary> - <type> - <v>Format = atom() | string() | binary()</v> - <v>Data = [term()]</v> - <v>UnicodeList = [Unicode]</v> - <v>Unicode = int() representing valid unicode codepoint</v> - </type> <desc> - <p>Returns a character list which represents <c>Data</c> - formatted in accordance with <c>Format</c>. See + <p>Returns a character list which represents <c><anno>Data</anno></c> + formatted in accordance with <c><anno>Format</anno></c>. See <seealso marker="io#fwrite/1">io:fwrite/1,2,3</seealso> for a detailed description of the available formatting options. A fault is generated if there is an error in the format string or @@ -119,42 +113,32 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>fread(Format, String) -> Result</name> + <name name="fread" arity="2"/> <fsummary>Read formatted input</fsummary> - <type> - <v>Format = String = string()</v> - <v>Result = {ok, InputList, LeftOverChars} | {more, RestFormat, Nchars, InputStack} | {error, What}</v> - <v> InputList = chars()</v> - <v> LeftOverChars = string()</v> - <v> RestFormat = string()</v> - <v> Nchars = int()</v> - <v> InputStack = chars()</v> - <v> What = term()</v> - </type> <desc> - <p>Tries to read <c>String</c> in accordance with the control - sequences in <c>Format</c>. See + <p>Tries to read <c><anno>String</anno></c> in accordance with the control + sequences in <c><anno>Format</anno></c>. See <seealso marker="io#fread/3">io:fread/3</seealso> for a detailed description of the available formatting options. It is - assumed that <c>String</c> contains whole lines. It returns:</p> + assumed that <c><anno>String</anno></c> contains whole lines. It returns:</p> <taglist> - <tag><c>{ok, InputList, LeftOverChars}</c></tag> + <tag><c>{ok, <anno>InputList</anno>, <anno>LeftOverChars</anno>}</c></tag> <item> - <p>The string was read. <c>InputList</c> is the list of + <p>The string was read. <c><anno>InputList</anno></c> is the list of successfully matched and read items, and - <c>LeftOverChars</c> are the input characters not used.</p> + <c><anno>LeftOverChars</anno></c> are the input characters not used.</p> </item> - <tag><c>{more, RestFormat, Nchars, InputStack}</c></tag> + <tag><c>{more, <anno>RestFormat</anno>, <anno>Nchars</anno>, <anno>InputStack</anno>}</c></tag> <item> <p>The string was read, but more input is needed in order - to complete the original format string. <c>RestFormat</c> - is the remaining format string, <c>NChars</c> the number - of characters scanned, and <c>InputStack</c> is the + to complete the original format string. <c><anno>RestFormat</anno></c> + is the remaining format string, <c><anno>Nchars</anno></c> the number + of characters scanned, and <c><anno>InputStack</anno></c> is the reversed list of inputs matched up to that point.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> - <p>The read operation failed and the parameter <c>What</c> + <p>The read operation failed and the parameter <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> @@ -165,17 +149,8 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>fread(Continuation, String, Format) -> Return</name> + <name name="fread" arity="3"/> <fsummary>Re-entrant formatted reader</fsummary> - <type> - <v>Continuation = see below</v> - <v>String = Format = string()</v> - <v>Return = {done, Result, LeftOverChars} | {more, Continuation}</v> - <v> Result = {ok, InputList} | eof | {error, What}</v> - <v> InputList = chars()</v> - <v> What = term()()</v> - <v> LeftOverChars = string()</v> - </type> <desc> <p>This is the re-entrant formatted reader. The continuation of the first call to the functions must be <c>[]</c>. Refer to @@ -184,114 +159,92 @@ chars() = [char() | chars()]</code> re-entrant input scheme works.</p> <p>The function returns:</p> <taglist> - <tag><c>{done, Result, LeftOverChars}</c></tag> + <tag><c>{done, <anno>Result</anno>, <anno>LeftOverChars</anno>}</c></tag> <item> <p>The input is complete. The result is one of the following:</p> <taglist> - <tag><c>{ok, InputList}</c></tag> + <tag><c>{ok, <anno>InputList</anno>}</c></tag> <item> - <p>The string was read. <c>InputList</c> is the list of + <p>The string was read. <c><anno>InputList</anno></c> is the list of successfully matched and read items, and - <c>LeftOverChars</c> are the remaining characters.</p> + <c><anno>LeftOverChars</anno></c> are the remaining characters.</p> </item> <tag><c>eof</c></tag> <item> <p>End of file has been encountered. - <c>LeftOverChars</c> are the input characters not + <c><anno>LeftOverChars</anno></c> are the input characters not used.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> - <p>An error occurred and the parameter <c>What</c> gives + <p>An error occurred and the parameter <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> </item> - <tag><c>{more, Continuation}</c></tag> + <tag><c>{more, <anno>Continuation</anno>}</c></tag> <item> <p>More data is required to build a term. - <c>Continuation</c> must be passed to <c>fread/3</c>, + <c><anno>Continuation</anno></c> must be passed to <c>fread/3</c>, when more data becomes available.</p> </item> </taglist> </desc> </func> <func> - <name>write_atom(Atom) -> chars()</name> + <name name="write_atom" arity="1"/> <fsummary>Write an atom</fsummary> - <type> - <v>Atom = atom()</v> - </type> <desc> <p>Returns the list of characters needed to print the atom - <c>Atom</c>.</p> + <c><anno>Atom</anno></c>.</p> </desc> </func> <func> - <name>write_string(String) -> chars()</name> + <name name="write_string" arity="1"/> <fsummary>Write a string</fsummary> - <type> - <v>String = string()</v> - </type> <desc> - <p>Returns the list of characters needed to print <c>String</c> + <p>Returns the list of characters needed to print <c><anno>String</anno></c> as a string.</p> </desc> </func> <func> - <name>write_char(Integer) -> chars()</name> + <name name="write_char" arity="1"/> <fsummary>Write a character</fsummary> - <type> - <v>Integer = int()</v> - </type> <desc> <p>Returns the list of characters needed to print a character constant in the ISO-latin-1 character set.</p> </desc> </func> <func> - <name>indentation(String, StartIndent) -> int()</name> + <name name="indentation" arity="2"/> <fsummary>Indentation after printing string</fsummary> - <type> - <v>String = string()</v> - <v>StartIndent = int()</v> - </type> <desc> - <p>Returns the indentation if <c>String</c> has been printed, - starting at <c>StartIndent</c>.</p> + <p>Returns the indentation if <c><anno>String</anno></c> has been printed, + starting at <c><anno>StartIndent</anno></c>.</p> </desc> </func> <func> - <name>char_list(Term) -> bool()</name> + <name name="char_list" arity="1"/> <fsummary>Test for a list of characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a flat list of + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p> </desc> </func> <func> - <name>deep_char_list(Term) -> bool()</name> + <name name="deep_char_list" arity="1"/> <fsummary>Test for a deep list of characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a, possibly deep, list + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list of characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p> </desc> </func> <func> - <name>printable_list(Term) -> bool()</name> + <name name="printable_list" arity="1"/> <fsummary>Test for a list of printable ISO-latin-1 characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a flat list of + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of printable ISO-latin-1 characters, otherwise it returns <c>false</c>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/lib.xml b/lib/stdlib/doc/src/lib.xml index 046add48e8..19fb827cbf 100644 --- a/lib/stdlib/doc/src/lib.xml +++ b/lib/stdlib/doc/src/lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,27 +37,23 @@ </description> <funcs> <func> - <name>flush_receive() -> void()</name> + <name name="flush_receive" arity="0"/> <fsummary>Flush messages</fsummary> <desc> <p>Flushes the message buffer of the current process.</p> </desc> </func> <func> - <name>error_message(Format, Args) -> ok</name> + <name name="error_message" arity="2"/> <fsummary>Print error message</fsummary> - <type> - <v>Format = atom() | string() | binary()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Prints error message <c>Args</c> in accordance with - <c>Format</c>. Similar to <c>io:format/2</c>, see + <p>Prints error message <c><anno>Args</anno></c> in accordance with + <c><anno>Format</anno></c>. Similar to <c>io:format/2</c>, see <seealso marker="io#fwrite/1">io(3)</seealso>.</p> </desc> </func> <func> - <name>progname() -> atom()</name> + <name name="progname" arity="0"/> <fsummary>Return name of Erlang start script</fsummary> <desc> <p>Returns the name of the script that started the current @@ -65,37 +61,24 @@ </desc> </func> <func> - <name>nonl(String1) -> String2</name> + <name name="nonl" arity="1"/> <fsummary>Remove last newline</fsummary> - <type> - <v>String1 = String2 = string()</v> - </type> <desc> <p>Removes the last newline character, if any, in - <c>String1</c>.</p> + <c><anno>String1</anno></c>.</p> </desc> </func> <func> - <name>send(To, Msg)</name> + <name name="send" arity="2"/> <fsummary>Send a message</fsummary> - <type> - <v>To = pid() | Name | {Name,Node}</v> - <v> Name = Node = atom()</v> - <v>Msg = term()</v> - </type> <desc> <p>This function to makes it possible to send a message using the <c>apply/3</c> BIF.</p> </desc> </func> <func> - <name>sendw(To, Msg)</name> + <name name="sendw" arity="2"/> <fsummary>Send a message and wait for an answer</fsummary> - <type> - <v>To = pid() | Name | {Name,Node}</v> - <v> Name = Node = atom()</v> - <v>Msg = term()</v> - </type> <desc> <p>As <c>send/2</c>, but waits for an answer. It is implemented as follows:</p> diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 92c4eb4f4c..6f3ed7af98 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,58 +60,41 @@ </description> <funcs> <func> - <name>all(Pred, List) -> bool()</name> + <name name="all" arity="2"/> <fsummary>Return true if all elements in the list satisfy<c>Pred</c></fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Pred(Elem)</c> returns - <c>true</c> for all elements <c>Elem</c> in <c>List</c>, + <p>Returns <c>true</c> if <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns + <c>true</c> for all elements <c><anno>Elem</anno></c> in <c><anno>List</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>any(Pred, List) -> bool()</name> + <name name="any" arity="2"/> <fsummary>Return true if any of the elements in the list satisfies<c>Pred</c></fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Pred(Elem)</c> returns - <c>true</c> for at least one element <c>Elem</c> in - <c>List</c>.</p> + <p>Returns <c>true</c> if <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns + <c>true</c> for at least one element <c><anno>Elem</anno></c> in + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>append(ListOfLists) -> List1</name> + <name name="append" arity="1"/> <fsummary>Append a list of lists</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns a list in which all the sub-lists of - <c>ListOfLists</c> have been appended. For example:</p> + <c><anno>ListOfLists</anno></c> have been appended. For example:</p> <pre> > <input>lists:append([[1, 2, 3], [a, b], [4, 5, 6]]).</input> [1,2,3,a,b,4,5,6]</pre> </desc> </func> <func> - <name>append(List1, List2) -> List3</name> + <name name="append" arity="2"/> <fsummary>Append two lists</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns a new list <c>List3</c> which is made from - the elements of <c>List1</c> followed by the elements of - <c>List2</c>. For example:</p> + <p>Returns a new list <c><anno>List3</anno></c> which is made from + the elements of <c><anno>List1</anno></c> followed by the elements of + <c><anno>List2</anno></c>. For example:</p> <pre> > <input>lists:append("abc", "def").</input> "abcdef"</pre> @@ -119,15 +102,11 @@ </desc> </func> <func> - <name>concat(Things) -> string()</name> + <name name="concat" arity="1"/> <fsummary>Concatenate a list of atoms</fsummary> - <type> - <v>Things = [Thing]</v> - <v> Thing = atom() | integer() | float() | string()</v> - </type> <desc> <p>Concatenates the text representation of the elements - of <c>Things</c>. The elements of <c>Things</c> can be atoms, + of <c><anno>Things</anno></c>. The elements of <c><anno>Things</anno></c> can be atoms, integers, floats or strings.</p> <pre> > <input>lists:concat([doc, '/', file, '.', 3]).</input> @@ -135,87 +114,59 @@ </desc> </func> <func> - <name>delete(Elem, List1) -> List2</name> + <name name="delete" arity="2"/> <fsummary>Delete an element from a list</fsummary> - <type> - <v>Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns a copy of <c>List1</c> where the first element - matching <c>Elem</c> is deleted, if there is such an + <p>Returns a copy of <c><anno>List1</anno></c> where the first element + matching <c><anno>Elem</anno></c> is deleted, if there is such an element.</p> </desc> </func> <func> - <name>dropwhile(Pred, List1) -> List2</name> + <name name="dropwhile" arity="2"/> <fsummary>Drop elements from a list while a predicate is true</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Drops elements <c>Elem</c> from <c>List1</c> while - <c>Pred(Elem)</c> returns <c>true</c> and returns + <p>Drops elements <c><anno>Elem</anno></c> from <c><anno>List1</anno></c> while + <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c> and returns the remaining list.</p> </desc> </func> <func> - <name>duplicate(N, Elem) -> List</name> + <name name="duplicate" arity="2"/> <fsummary>Make N copies of element</fsummary> - <type> - <v>N = int()</v> - <v>Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns a list which contains N copies of the term - <c>Elem</c>. For example:</p> + <p>Returns a list which contains <c><anno>N</anno></c> copies of the term + <c><anno>Elem</anno></c>. For example:</p> <pre> > <input>lists:duplicate(5, xx).</input> [xx,xx,xx,xx,xx]</pre> </desc> </func> <func> - <name>filter(Pred, List1) -> List2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p><c>List2</c> is a list of all elements <c>Elem</c> in - <c>List1</c> for which <c>Pred(Elem)</c> returns + <p><c><anno>List2</anno></c> is a list of all elements <c><anno>Elem</anno></c> in + <c><anno>List1</anno></c> for which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>.</p> </desc> </func> <func> - <name>flatlength(DeepList) -> int()</name> + <name name="flatlength" arity="1"/> <fsummary>Length of flattened deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - </type> <desc> - <p>Equivalent to <c>length(flatten(DeepList))</c>, but more + <p>Equivalent to <c>length(flatten(<anno>DeepList</anno>))</c>, but more efficient.</p> </desc> </func> <func> - <name>flatmap(Fun, List1) -> List2</name> + <name name="flatmap" arity="2"/> <fsummary>Map and flatten in one pass</fsummary> - <type> - <v>Fun = fun(A) -> [B]</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Takes a function from <c>A</c>s to lists of <c>B</c>s, and a - list of <c>A</c>s (<c>List1</c>) and produces a list of - <c>B</c>s by applying the function to every element in - <c>List1</c> and appending the resulting lists.</p> + <p>Takes a function from <c><anno>A</anno></c>s to lists of <c><anno>B</anno></c>s, and a + list of <c><anno>A</anno></c>s (<c><anno>List1</anno></c>) and produces a list of + <c><anno>B</anno></c>s by applying the function to every element in + <c><anno>List1</anno></c> and appending the resulting lists.</p> <p>That is, <c>flatmap</c> behaves as if it had been defined as follows:</p> <code type="none"> @@ -228,43 +179,29 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>flatten(DeepList) -> List</name> + <name name="flatten" arity="1"/> <fsummary>Flatten a deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns a flattened version of <c>DeepList</c>.</p> + <p>Returns a flattened version of <c><anno>DeepList</anno></c>.</p> </desc> </func> <func> - <name>flatten(DeepList, Tail) -> List</name> + <name name="flatten" arity="2"/> <fsummary>Flatten a deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - <v>Tail = List = [term()]</v> - </type> <desc> - <p>Returns a flattened version of <c>DeepList</c> with the tail - <c>Tail</c> appended.</p> + <p>Returns a flattened version of <c><anno>DeepList</anno></c> with the tail + <c><anno>Tail</anno></c> appended.</p> </desc> </func> <func> - <name>foldl(Fun, Acc0, List) -> Acc1</name> + <name name="foldl" arity="3"/> <fsummary>Fold a function over a list</fsummary> - <type> - <v>Fun = fun(Elem, AccIn) -> AccOut</v> - <v> Elem = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Calls <c>Fun(Elem, AccIn)</c> on successive elements <c>A</c> - of <c>List</c>, starting with <c>AccIn == Acc0</c>. - <c>Fun/2</c> must return a new accumulator which is passed to + <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>, <anno>AccIn</anno>)</c> on successive elements <c>A</c> + of <c><anno>List</anno></c>, starting with <c><anno>AccIn</anno> == <anno>Acc0</anno></c>. + <c><anno>Fun</anno>/2</c> must return a new accumulator which is passed to the next call. The function returns the final value of - the accumulator. <c>Acc0</c> is returned if the list is empty. + the accumulator. <c><anno>Acc0</anno></c> is returned if the list is empty. For example:</p> <pre> > <input>lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3,4,5]).</input> @@ -274,14 +211,8 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>foldr(Fun, Acc0, List) -> Acc1</name> + <name name="foldr" arity="3"/> <fsummary>Fold a function over a list</fsummary> - <type> - <v>Fun = fun(Elem, AccIn) -> AccOut</v> - <v> Elem = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Like <c>foldl/3</c>, but the list is traversed from right to left. For example:</p> @@ -297,33 +228,23 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>foreach(Fun, List) -> void()</name> + <name name="foreach" arity="2"/> <fsummary>Apply a function to each element of a list</fsummary> - <type> - <v>Fun = fun(Elem) -> void()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Calls <c>Fun(Elem)</c> for each element <c>Elem</c> in - <c>List</c>. This function is used for its side effects and + <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> for each element <c><anno>Elem</anno></c> in + <c><anno>List</anno></c>. This function is used for its side effects and the evaluation order is defined to be the same as the order of the elements in the list.</p> </desc> </func> <func> - <name>keydelete(Key, N, TupleList1) -> TupleList2</name> + <name name="keydelete" arity="3"/> <fsummary>Delete an element from a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a tuple whose <c>N</c>th element compares equal to - <c>Key</c> is deleted, if there is such a tuple.</p> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a tuple whose <c><anno>N</anno></c>th element compares equal to + <c><anno>Key</anno></c> is deleted, if there is such a tuple.</p> </desc> </func> <func> @@ -343,19 +264,14 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymap(Fun, N, TupleList1) -> TupleList2</name> + <name name="keymap" arity="3"/> <fsummary>Map a function over a list of tuples</fsummary> - <type> - <v>Fun = fun(Term1) -> Term2</v> - <v> Term1 = Term2 = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [tuple()]</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list of tuples where, for each tuple in - <c>TupleList1</c>, the <c>N</c>th element <c>Term1</c> of the tuple + <c><anno>TupleList1</anno></c>, the <c><anno>N</anno></c>th element <c><anno>Term1</anno></c> of the tuple has been replaced with the result of calling - <c>Fun(Term1)</c>.</p> + <c><anno>Fun</anno>(<anno>Term1</anno>)</c>.</p> <p>Examples:</p> <pre> > <input>Fun = fun(Atom) -> atom_to_list(Atom) end.</input> @@ -365,7 +281,7 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymember(Key, N, TupleList) -> bool()</name> + <name>keymember(Key, N, TupleList) -> boolean()</name> <fsummary>Test for membership of a list of tuples</fsummary> <type> <v>Key = term()</v> @@ -380,37 +296,28 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymerge(N, TupleList1, TupleList2) -> TupleList3</name> + <name name="keymerge" arity="3"/> <fsummary>Merge two key-sorted lists of tuples</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = TupleList3 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns the sorted list formed by merging <c>TupleList1</c> - and <c>TupleList2</c>. The merge is performed on - the <c>N</c>th element of each tuple. Both <c>TupleList1</c> and - <c>TupleList2</c> must be key-sorted prior to evaluating this + <p>Returns the sorted list formed by merging <c><anno>TupleList1</anno></c> + and <c><anno>TupleList2</anno></c>. The merge is performed on + the <c><anno>N</anno></c>th element of each tuple. Both <c><anno>TupleList1</anno></c> and + <c><anno>TupleList2</anno></c> must be key-sorted prior to evaluating this function. When two tuples compare equal, the tuple from - <c>TupleList1</c> is picked before the tuple from - <c>TupleList2</c>.</p> + <c><anno>TupleList1</anno></c> is picked before the tuple from + <c><anno>TupleList2</anno></c>.</p> </desc> </func> <func> - <name>keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2</name> + <name name="keyreplace" arity="4"/> <fsummary>Replace an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>NewTuple = Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a <c>T</c> tuple whose <c>N</c>th element - compares equal to <c>Key</c> is replaced with - <c>NewTuple</c>, if there is such a tuple <c>T</c>.</p> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a <c>T</c> tuple whose <c><anno>N</anno></c>th element + compares equal to <c><anno>Key</anno></c> is replaced with + <c><anno>NewTuple</anno></c>, if there is such a tuple <c>T</c>.</p> </desc> </func> <func> @@ -433,95 +340,63 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keysort(N, TupleList1) -> TupleList2</name> + <name name="keysort" arity="2"/> <fsummary>Sort a list of tuples</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list containing the sorted elements of the list - <c>TupleList1</c>. Sorting is performed on the <c>N</c>th + <c><anno>TupleList1</anno></c>. Sorting is performed on the <c><anno>N</anno></c>th element of the tuples. The sort is stable.</p> </desc> </func> <func> - <name>keystore(Key, N, TupleList1, NewTuple) -> TupleList2</name> + <name name="keystore" arity="4"/> <fsummary>Store an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>NewTuple = Tuple = tuple()</v> - </type> - <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a tuple <c>T</c> whose <c>N</c>th element - compares equal to <c>Key</c> is replaced with - <c>NewTuple</c>, if there is such a tuple <c>T</c>. If there - is no such tuple <c>T</c> a copy of <c>TupleList1</c> where - [<c>NewTuple</c>] has been appended to the end is + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> + <desc> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a tuple <c>T</c> whose <c><anno>N</anno></c>th element + compares equal to <c><anno>Key</anno></c> is replaced with + <c><anno>NewTuple</anno></c>, if there is such a tuple <c>T</c>. If there + is no such tuple <c>T</c> a copy of <c><anno>TupleList1</anno></c> where + [<c><anno>NewTuple</anno></c>] has been appended to the end is returned.</p> </desc> </func> <func> - <name>keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} - | false</name> + <name name="keytake" arity="3"/> <fsummary>Extract an element from a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Searches the list of tuples <c>TupleList1</c> for a tuple - whose <c>N</c>th element compares equal to <c>Key</c>. - Returns <c>{value, Tuple, TupleList2}</c> if such a tuple is - found, otherwise <c>false</c>. <c>TupleList2</c> is a copy - of <c>TupleList1</c> where the first occurrence of - <c>Tuple</c> has been removed.</p> + <p>Searches the list of tuples <c><anno>TupleList1</anno></c> for a tuple + whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>. + Returns <c>{value, <anno>Tuple</anno>, <anno>TupleList2</anno>}</c> if such a tuple is + found, otherwise <c>false</c>. <c><anno>TupleList2</anno></c> is a copy + of <c><anno>TupleList1</anno></c> where the first occurrence of + <c><anno>Tuple</anno></c> has been removed.</p> </desc> </func> <func> - <name>last(List) -> Last</name> + <name name="last" arity="1"/> <fsummary>Return last element in a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Last = term()</v> - </type> <desc> - <p>Returns the last element in <c>List</c>.</p> + <p>Returns the last element in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, List1) -> List2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a list</fsummary> - <type> - <v>Fun = fun(A) -> B</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Takes a function from <c>A</c>s to <c>B</c>s, and a list of - <c>A</c>s and produces a list of <c>B</c>s by applying + <p>Takes a function from <c><anno>A</anno></c>s to <c><anno>B</anno></c>s, and a list of + <c><anno>A</anno></c>s and produces a list of <c><anno>B</anno></c>s by applying the function to every element in the list. This function is used to obtain the return values. The evaluation order is implementation dependent.</p> </desc> </func> <func> - <name>mapfoldl(Fun, Acc0, List1) -> {List2, Acc1}</name> + <name name="mapfoldl" arity="3"/> <fsummary>Map and fold in one pass</fsummary> - <type> - <v>Fun = fun(A, AccIn) -> {B, AccOut}</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> <p><c>mapfoldl</c> combines the operations of <c>map/2</c> and <c>foldl/3</c> into one pass. An example, summing @@ -533,35 +408,24 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>mapfoldr(Fun, Acc0, List1) -> {List2, Acc1}</name> + <name name="mapfoldr" arity="3"/> <fsummary>Map and fold in one pass</fsummary> - <type> - <v>Fun = fun(A, AccIn) -> {B, AccOut}</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> <p><c>mapfoldr</c> combines the operations of <c>map/2</c> and <c>foldr/3</c> into one pass.</p> </desc> </func> <func> - <name>max(List) -> Max</name> + <name name="max" arity="1"/> <fsummary>Return maximum element of a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Max = term()</v> - </type> <desc> - <p>Returns the first element of <c>List</c> that compares + <p>Returns the first element of <c><anno>List</anno></c> that compares greater than or equal to all other elements of - <c>List</c>.</p> + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>member(Elem, List) -> bool()</name> + <name>member(Elem, List) -> boolean()</name> <fsummary>Test for membership of a list</fsummary> <type> <v>Elem = term()</v> @@ -573,112 +437,84 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>merge(ListOfLists) -> List1</name> + <name name="merge" arity="1"/> <fsummary>Merge a list of sorted lists</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns the sorted list formed by merging all the sub-lists - of <c>ListOfLists</c>. All sub-lists must be sorted prior to + of <c><anno>ListOfLists</anno></c>. All sub-lists must be sorted prior to evaluating this function. When two elements compare equal, the element from the sub-list with the lowest position in - <c>ListOfLists</c> is picked before the other element.</p> + <c><anno>ListOfLists</anno></c> is picked before the other element.</p> </desc> </func> <func> - <name>merge(List1, List2) -> List3</name> + <name name="merge" arity="2"/> <fsummary>Merge two sorted lists</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted prior to evaluating this function. When two elements - compare equal, the element from <c>List1</c> is picked - before the element from <c>List2</c>.</p> + compare equal, the element from <c><anno>List1</anno></c> is picked + before the element from <c><anno>List2</anno></c>.</p> </desc> </func> <func> - <name>merge(Fun, List1, List2) -> List3</name> + <name name="merge" arity="3"/> <fsummary>Merge two sorted list</fsummary> - <type> - <v>Fun = fun(A, B) -> bool()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v>List3 = [A | B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c> prior to evaluating this function. <c>Fun(A, - B)</c> should return <c>true</c> if <c>A</c> compares less - than or equal to <c>B</c> in the ordering, <c>false</c> + <c><anno>Fun</anno></c> prior to evaluating this function. <c><anno>Fun</anno>(<anno>A</anno>, + <anno>B</anno>)</c> should return <c>true</c> if <c><anno>A</anno></c> compares less + than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise. When two elements compare equal, the element from - <c>List1</c> is picked before the element from - <c>List2</c>.</p> + <c><anno>List1</anno></c> is picked before the element from + <c><anno>List2</anno></c>.</p> </desc> </func> <func> - <name>merge3(List1, List2, List3) -> List4</name> + <name name="merge3" arity="3"/> <fsummary>Merge three sorted lists</fsummary> - <type> - <v>List1 = List2 = List3 = List4 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c>, - <c>List2</c> and <c>List3</c>. All of <c>List1</c>, - <c>List2</c> and <c>List3</c> must be sorted prior to + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. All of <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c> must be sorted prior to evaluating this function. When two elements compare equal, - the element from <c>List1</c>, if there is such an element, + the element from <c><anno>List1</anno></c>, if there is such an element, is picked before the other element, otherwise the element - from <c>List2</c> is picked before the element from - <c>List3</c>.</p> + from <c><anno>List2</anno></c> is picked before the element from + <c><anno>List3</anno></c>.</p> </desc> </func> <func> - <name>min(List) -> Min</name> + <name name="min" arity="1"/> <fsummary>Return minimum element of a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Min = term()</v> - </type> <desc> - <p>Returns the first element of <c>List</c> that compares + <p>Returns the first element of <c><anno>List</anno></c> that compares less than or equal to all other elements of - <c>List</c>.</p> + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>nth(N, List) -> Elem</name> + <name name="nth" arity="2"/> <fsummary>Return the Nth element of a list</fsummary> - <type> - <v>N = 1..length(List)</v> - <v>List = [term()]</v> - <v>Elem = term()</v> - </type> + <type_desc variable="N">1..length(List)</type_desc> <desc> - <p>Returns the <c>N</c>th element of <c>List</c>. For example:</p> + <p>Returns the <c><anno>N</anno></c>th element of <c><anno>List</anno></c>. For example:</p> <pre> > <input>lists:nth(3, [a, b, c, d, e]).</input> c</pre> </desc> </func> <func> - <name>nthtail(N, List1) -> Tail</name> + <name name="nthtail" arity="2"/> <fsummary>Return the Nth tail of a list</fsummary> - <type> - <v>N = 0..length(List1)</v> - <v>List1 = Tail = [term()]</v> - </type> + <type_desc variable="N">0..length(List)</type_desc> <desc> - <p>Returns the <c>N</c>th tail of <c>List</c>, that is, the sublist of - <c>List</c> starting at <c>N+1</c> and continuing up to + <p>Returns the <c><anno>N</anno></c>th tail of <c><anno>List</anno></c>, that is, the sublist of + <c><anno>List</anno></c> starting at <c><anno>N</anno>+1</c> and continuing up to the end of the list. For example:</p> <pre> > <input>lists:nthtail(3, [a, b, c, d, e]).</input> @@ -692,18 +528,13 @@ c</pre> </desc> </func> <func> - <name>partition(Pred, List) -> {Satisfying, NonSatisfying}</name> + <name name="partition" arity="2"/> <fsummary>Partition a list into two lists based on a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = Satisfying = NonSatisfying = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into two lists, where the first list - contains all elements for which <c>Pred(Elem)</c> returns + <p>Partitions <c><anno>List</anno></c> into two lists, where the first list + contains all elements for which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>, and the second list contains all elements for - which <c>Pred(Elem)</c> returns <c>false</c>.</p> + which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>false</c>.</p> <p>Examples:</p> <pre> > <input>lists:partition(fun(A) -> A rem 2 == 1 end, [1,2,3,4,5,6,7]).</input> @@ -715,24 +546,18 @@ c</pre> </desc> </func> <func> - <name>prefix(List1, List2) -> bool()</name> + <name name="prefix" arity="2"/> <fsummary>Test for list prefix</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>List1</c> is a prefix of - <c>List2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a prefix of + <c><anno>List2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>reverse(List1) -> List2</name> + <name name="reverse" arity="1"/> <fsummary>Reverse a list</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns a list with the top level elements in <c>List1</c> + <p>Returns a list with the elements in <c><anno>List1</anno></c> in reverse order.</p> </desc> </func> @@ -743,7 +568,7 @@ c</pre> <v>List1 = Tail = List2 = [term()]</v> </type> <desc> - <p>Returns a list with the top level elements in <c>List1</c> + <p>Returns a list with the elements in <c>List1</c> in reverse order, with the tail <c>Tail</c> appended. For example:</p> <pre> @@ -752,22 +577,18 @@ c</pre> </desc> </func> <func> - <name>seq(From, To) -> Seq</name> - <name>seq(From, To, Incr) -> Seq</name> + <name name="seq" arity="2"/> + <name name="seq" arity="3"/> <fsummary>Generate a sequence of integers</fsummary> - <type> - <v>From = To = Incr = int()</v> - <v>Seq = [int()]</v> - </type> <desc> - <p>Returns a sequence of integers which starts with <c>From</c> - and contains the successive results of adding <c>Incr</c> to - the previous element, until <c>To</c> has been reached or - passed (in the latter case, <c>To</c> is not an element of - the sequence). <c>Incr</c> defaults to 1.</p> - <p>Failure: If <c><![CDATA[To<From-Incr]]></c> and <c>Incr</c> - is positive, or if <c>To>From-Incr</c> and <c>Incr</c> is - negative, or if <c>Incr==0</c> and <c>From/=To</c>.</p> + <p>Returns a sequence of integers which starts with <c><anno>From</anno></c> + and contains the successive results of adding <c><anno>Incr</anno></c> to + the previous element, until <c><anno>To</anno></c> has been reached or + passed (in the latter case, <c><anno>To</anno></c> is not an element of + the sequence). <c><anno>Incr</anno></c> defaults to 1.</p> + <p>Failure: If <c><anno>To</anno><<anno>From</anno>-<anno>Incr</anno></c> and <c><anno>Incr</anno></c> + is positive, or if <c><anno>To</anno>><anno>From</anno>-<anno>Incr</anno></c> and <c><anno>Incr</anno></c> is + negative, or if <c><anno>Incr</anno>==0</c> and <c><anno>From</anno>/=<anno>To</anno></c>.</p> <p>The following equalities hold for all sequences:</p> <code type="none"> length(lists:seq(From, To)) == To-From+1 @@ -787,57 +608,41 @@ length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr</code> </desc> </func> <func> - <name>sort(List1) -> List2</name> + <name name="sort" arity="1"/> <fsummary>Sort a list</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c>.</p> + <c><anno>List1</anno></c>.</p> </desc> </func> <func> - <name>sort(Fun, List1) -> List2</name> + <name name="sort" arity="2"/> <fsummary>Sort a list</fsummary> - <type> - <v>Fun = fun(Elem1, Elem2) -> bool()</v> - <v> Elem1 = Elem2 = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c>, according to the <seealso + <c><anno>List1</anno></c>, according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c>. <c>Fun(A, B)</c> should return <c>true</c> if - <c>A</c> compares less than or equal to <c>B</c> in the + <c><anno>Fun</anno></c>. <c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> should return <c>true</c> if + <c><anno>A</anno></c> compares less than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>split(N, List1) -> {List2, List3}</name> + <name name="split" arity="2"/> <fsummary>Split a list into two lists</fsummary> - <type> - <v>N = 0..length(List1)</v> - <v>List1 = List2 = List3 = [term()]</v> - </type> + <type_desc variable="N">0..length(List1)</type_desc> <desc> - <p>Splits <c>List1</c> into <c>List2</c> and <c>List3</c>. - <c>List2</c> contains the first <c>N</c> elements and - <c>List3</c> the rest of the elements (the <c>N</c>th tail).</p> + <p>Splits <c><anno>List1</anno></c> into <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. + <c><anno>List2</anno></c> contains the first <c><anno>N</anno></c> elements and + <c><anno>List3</anno></c> the rest of the elements (the <c><anno>N</anno></c>th tail).</p> </desc> </func> <func> - <name>splitwith(Pred, List) -> {List1, List2}</name> + <name name="splitwith" arity="2"/> <fsummary>Split a list into two lists based on a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = List1 = List2 = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into two lists according to - <c>Pred</c>. <c>splitwith/2</c> behaves as if it is defined + <p>Partitions <c><anno>List</anno></c> into two lists according to + <c><anno>Pred</anno></c>. <c>splitwith/2</c> behaves as if it is defined as follows:</p> <code type="none"> splitwith(Pred, List) -> @@ -853,31 +658,23 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>sublist(List1, Len) -> List2</name> + <name name="sublist" arity="2"/> <fsummary>Return a sub-list of a certain length, starting at the first position</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - <v>Len = int()</v> - </type> <desc> - <p>Returns the sub-list of <c>List1</c> starting at position 1 - and with (max) <c>Len</c> elements. It is not an error for - <c>Len</c> to exceed the length of the list -- in that case + <p>Returns the sub-list of <c><anno>List1</anno></c> starting at position 1 + and with (max) <c><anno>Len</anno></c> elements. It is not an error for + <c><anno>Len</anno></c> to exceed the length of the list -- in that case the whole list is returned.</p> </desc> </func> <func> - <name>sublist(List1, Start, Len) -> List2</name> + <name name="sublist" arity="3"/> <fsummary>Return a sub-list starting at a given position and with a given number of elements</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - <v>Start = 1..(length(List1)+1)</v> - <v>Len = int()</v> - </type> + <type_desc variable="Start">1..(length(List1)+1)</type_desc> <desc> - <p>Returns the sub-list of <c>List1</c> starting at <c>Start</c> - and with (max) <c>Len</c> elements. It is not an error for - <c>Start+Len</c> to exceed the length of the list.</p> + <p>Returns the sub-list of <c><anno>List1</anno></c> starting at <c><anno>Start</anno></c> + and with (max) <c><anno>Len</anno></c> elements. It is not an error for + <c><anno>Start</anno>+<anno>Len</anno></c> to exceed the length of the list.</p> <pre> > <input>lists:sublist([1,2,3,4], 2, 2).</input> [2,3] @@ -888,15 +685,12 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>subtract(List1, List2) -> List3</name> + <name name="subtract" arity="2"/> <fsummary>Subtract the element in one list from another list</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns a new list <c>List3</c> which is a copy of - <c>List1</c>, subjected to the following procedure: for each - element in <c>List2</c>, its first occurrence in <c>List1</c> + <p>Returns a new list <c><anno>List3</anno></c> which is a copy of + <c><anno>List1</anno></c>, subjected to the following procedure: for each + element in <c><anno>List2</anno></c>, its first occurrence in <c><anno>List1</anno></c> is deleted. For example:</p> <pre> > <input>lists:subtract("123212", "212").</input> @@ -911,151 +705,112 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>suffix(List1, List2) -> bool()</name> + <name name="suffix" arity="2"/> <fsummary>Test for list suffix</fsummary> <desc> - <p>Returns <c>true</c> if <c>List1</c> is a suffix of - <c>List2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a suffix of + <c><anno>List2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>sum(List) -> number()</name> + <name name="sum" arity="1"/> <fsummary>Return sum of elements in a list</fsummary> - <type> - <v>List = [number()]</v> - </type> <desc> - <p>Returns the sum of the elements in <c>List</c>.</p> + <p>Returns the sum of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>takewhile(Pred, List1) -> List2</name> + <name name="takewhile" arity="2"/> <fsummary>Take elements from a list while a predicate is true</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Takes elements <c>Elem</c> from <c>List1</c> while - <c>Pred(Elem)</c> returns <c>true</c>, that is, + <p>Takes elements <c><anno>Elem</anno></c> from <c><anno>List1</anno></c> while + <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>, that is, the function returns the longest prefix of the list for which all elements satisfy the predicate.</p> </desc> </func> <func> - <name>ukeymerge(N, TupleList1, TupleList2) -> TupleList3</name> + <name name="ukeymerge" arity="3"/> <fsummary>Merge two key-sorted lists of tuples, removing duplicates</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = TupleList3 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns the sorted list formed by merging <c>TupleList1</c> - and <c>TupleList2</c>. The merge is performed on the - <c>N</c>th element of each tuple. Both <c>TupleList1</c> and - <c>TupleList2</c> must be key-sorted without duplicates + <p>Returns the sorted list formed by merging <c><anno>TupleList1</anno></c> + and <c><anno>TupleList2</anno></c>. The merge is performed on the + <c><anno>N</anno></c>th element of each tuple. Both <c><anno>TupleList1</anno></c> and + <c><anno>TupleList2</anno></c> must be key-sorted without duplicates prior to evaluating this function. When two tuples compare - equal, the tuple from <c>TupleList1</c> is picked and the - one from <c>TupleList2</c> deleted.</p> + equal, the tuple from <c><anno>TupleList1</anno></c> is picked and the + one from <c><anno>TupleList2</anno></c> deleted.</p> </desc> </func> <func> - <name>ukeysort(N, TupleList1) -> TupleList2</name> + <name name="ukeysort" arity="2"/> <fsummary>Sort a list of tuples, removing duplicates</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list containing the sorted elements of the list - <c>TupleList1</c> where all but the first tuple of the + <c><anno>TupleList1</anno></c> where all but the first tuple of the tuples comparing equal have been deleted. Sorting is - performed on the <c>N</c>th element of the tuples.</p> + performed on the <c><anno>N</anno></c>th element of the tuples.</p> </desc> </func> <func> - <name>umerge(ListOfLists) -> List1</name> + <name name="umerge" arity="1"/> <fsummary>Merge a list of sorted lists, removing duplicates</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns the sorted list formed by merging all the sub-lists - of <c>ListOfLists</c>. All sub-lists must be sorted and + of <c><anno>ListOfLists</anno></c>. All sub-lists must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from the - sub-list with the lowest position in <c>ListOfLists</c> is + sub-list with the lowest position in <c><anno>ListOfLists</anno></c> is picked and the other one deleted.</p> </desc> </func> <func> - <name>umerge(List1, List2) -> List3</name> + <name name="umerge" arity="2"/> <fsummary>Merge two sorted lists, removing duplicates</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from - <c>List1</c> is picked and the one from <c>List2</c> + <c><anno>List1</anno></c> is picked and the one from <c><anno>List2</anno></c> deleted.</p> </desc> </func> <func> - <name>umerge(Fun, List1, List2) -> List3</name> + <name name="umerge" arity="3"/> <fsummary>Merge two sorted lists, removing duplicates</fsummary> - <type> - <v>Fun = fun(A, B) -> bool()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v>List3 = [A | B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted according to the <seealso marker="#ordering_function">ordering function</seealso> <c>Fun</c> and contain no duplicates prior to evaluating - this function. <c>Fun(A, B)</c> should return <c>true</c> if - <c>A</c> compares less than or equal to <c>B</c> in the + this function. <c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> should return <c>true</c> if + <c><anno>A</anno></c> compares less than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise. When two elements compare equal, the element from - <c>List1</c> is picked and the one from <c>List2</c> + <c><anno>List1</anno></c> is picked and the one from <c><anno>List2</anno></c> deleted.</p> </desc> </func> <func> - <name>umerge3(List1, List2, List3) -> List4</name> + <name name="umerge3" arity="3"/> <fsummary>Merge three sorted lists, removing duplicates</fsummary> - <type> - <v>List1 = List2 = List3 = List4 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c>, - <c>List2</c> and <c>List3</c>. All of <c>List1</c>, - <c>List2</c> and <c>List3</c> must be sorted and contain no + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. All of <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c> must be sorted and contain no duplicates prior to evaluating this function. When two - elements compare equal, the element from <c>List1</c> is + elements compare equal, the element from <c><anno>List1</anno></c> is picked if there is such an element, otherwise the element - from <c>List2</c> is picked, and the other one deleted.</p> + from <c><anno>List2</anno></c> is picked, and the other one deleted.</p> </desc> </func> <func> - <name>unzip(List1) -> {List2, List3}</name> + <name name="unzip" arity="1"/> <fsummary>Unzip a list of two-tuples into two lists</fsummary> - <type> - <v>List1 = [{X, Y}]</v> - <v>List2 = [X]</v> - <v>List3 = [Y]</v> - <v> X = Y = term()</v> - </type> <desc> <p>"Unzips" a list of two-tuples into two lists, where the first list contains the first element of each tuple, and the second @@ -1063,15 +818,8 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>unzip3(List1) -> {List2, List3, List4}</name> + <name name="unzip3" arity="1"/> <fsummary>Unzip a list of three-tuples into three lists</fsummary> - <type> - <v>List1 = [{X, Y, Z}]</v> - <v>List2 = [X]</v> - <v>List3 = [Y]</v> - <v>List4 = [Z]</v> - <v> X = Y = Z = term()</v> - </type> <desc> <p>"Unzips" a list of three-tuples into three lists, where the first list contains the first element of each tuple, @@ -1080,44 +828,30 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>usort(List1) -> List2</name> + <name name="usort" arity="1"/> <fsummary>Sort a list, removing duplicates</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c> where all but the first element of the elements + <c><anno>List1</anno></c> where all but the first element of the elements comparing equal have been deleted.</p> </desc> </func> <func> - <name>usort(Fun, List1) -> List2</name> + <name name="usort" arity="2"/> <fsummary>Sort a list, removing duplicates</fsummary> - <type> - <v>Fun = fun(Elem1, Elem2) -> bool()</v> - <v> Elem1 = Elem2 = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list which contains the sorted elements of - <c>List1</c> where all but the first element of the elements + <c><anno>List1</anno></c> where all but the first element of the elements comparing equal according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c> have been deleted. <c>Fun(A, B)</c> should return + <c><anno>Fun</anno></c> have been deleted. <c><anno>Fun</anno>(A, B)</c> should return <c>true</c> if <c>A</c> compares less than or equal to <c>B</c> in the ordering, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>zip(List1, List2) -> List3</name> + <name name="zip" arity="2"/> <fsummary>Zip two lists into a list of two-tuples</fsummary> - <type> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [{X, Y}]</v> - <v> X = Y = term()</v> - </type> <desc> <p>"Zips" two lists of equal length into one list of two-tuples, where the first element of each tuple is taken from the first @@ -1126,15 +860,8 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zip3(List1, List2, List3) -> List4</name> + <name name="zip3" arity="3"/> <fsummary>Zip three lists into a list of three-tuples</fsummary> - <type> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [Z]</v> - <v>List3 = [{X, Y, Z}]</v> - <v> X = Y = Z = term()</v> - </type> <desc> <p>"Zips" three lists of equal length into one list of three-tuples, where the first element of each tuple is taken @@ -1145,20 +872,13 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zipwith(Combine, List1, List2) -> List3</name> + <name name="zipwith" arity="3"/> <fsummary>Zip two lists into one list according to a fun</fsummary> - <type> - <v>Combine = fun(X, Y) -> T</v> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [T]</v> - <v> X = Y = T = term()</v> - </type> <desc> <p>Combine the elements of two lists of equal length into one - list. For each pair <c>X, Y</c> of list elements from the two + list. For each pair <c><anno>X</anno>, <anno>Y</anno></c> of list elements from the two lists, the element in the result list will be - <c>Combine(X, Y)</c>.</p> + <c><anno>Combine</anno>(<anno>X</anno>, <anno>Y</anno>)</c>.</p> <p><c>zipwith(fun(X, Y) -> {X,Y} end, List1, List2)</c> is equivalent to <c>zip(List1, List2)</c>.</p> <p>Example:</p> @@ -1168,21 +888,13 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zipwith3(Combine, List1, List2, List3) -> List4</name> + <name name="zipwith3" arity="4"/> <fsummary>Zip three lists into one list according to a fun</fsummary> - <type> - <v>Combine = fun(X, Y, Z) -> T</v> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [Z]</v> - <v>List4 = [T]</v> - <v> X = Y = Z = T = term()</v> - </type> <desc> <p>Combine the elements of three lists of equal length into one - list. For each triple <c>X, Y, Z</c> of list elements from + list. For each triple <c><anno>X</anno>, <anno>Y</anno>, <anno>Z</anno></c> of list elements from the three lists, the element in the result list will be - <c>Combine(X, Y, Z)</c>.</p> + <c><anno>Combine</anno>(<anno>X</anno>, <anno>Y</anno>, <anno>Z</anno>)</c>.</p> <p><c>zipwith3(fun(X, Y, Z) -> {X,Y,Z} end, List1, List2, List3)</c> is equivalent to <c>zip3(List1, List2, List3)</c>.</p> <p>Examples:</p> <pre> diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml index f8e11339a7..f2b09b58eb 100644 --- a/lib/stdlib/doc/src/log_mf_h.xml +++ b/lib/stdlib/doc/src/log_mf_h.xml @@ -43,28 +43,27 @@ report files <c>1, 2, ....</c>. </p> </description> + <datatypes> + <datatype> + <name name="args"/> + <desc><p>Term to be sent to <seealso marker="gen_event#add_handler/3"> + gen_event:add_handler/3</seealso>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>init(Dir, MaxBytes, MaxFiles)</name> - <name>init(Dir, MaxBytes, MaxFiles, Pred) -> Args</name> + <name name="init" arity="3"/> + <name name="init" arity="4"/> <fsummary>Initiate the event handler</fsummary> - <type> - <v>Dir = string()</v> - <v>MaxBytes = integer()</v> - <v>MaxFiles = 0 < integer() < 256</v> - <v>Pred = fun(Event) -> boolean()</v> - <v>Event = term()</v> - <v>Args = args()</v> - </type> <desc> <p>Initiates the event handler. This function returns - <c>Args</c>, which should be used in a call to - <c>gen_event:add_handler(EventMgr, log_mf_h, Args)</c>. + <c><anno>Args</anno></c>, which should be used in a call to + <c>gen_event:add_handler(EventMgr, log_mf_h, <anno>Args</anno>)</c>. </p> - <p><c>Dir</c> specifies which directory to use for the log - files. <c>MaxBytes</c> specifies the size of each individual - file. <c>MaxFiles</c> specifies how many files are - used. <c>Pred</c> is a predicate function used to filter the + <p><c><anno>Dir</anno></c> specifies which directory to use for the log + files. <c><anno>MaxBytes</anno></c> specifies the size of each individual + file. <c><anno>MaxFiles</anno></c> specifies how many files are + used. <c><anno>Pred</anno></c> is a predicate function used to filter the events. If no predicate function is specified, all events are logged.</p> </desc> diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml index 02e4d6e495..518457d5d8 100644 --- a/lib/stdlib/doc/src/math.xml +++ b/lib/stdlib/doc/src/math.xml @@ -45,7 +45,7 @@ </description> <funcs> <func> - <name>pi() -> float()</name> + <name name="pi" arity="0"/> <fsummary>A useful number</fsummary> <desc> <p>A useful number.</p> diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml index ba9f89685b..f81f8bda96 100644 --- a/lib/stdlib/doc/src/ms_transform.xml +++ b/lib/stdlib/doc/src/ms_transform.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2002</year><year>2010</year> + <year>2002</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -599,12 +599,9 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </description> <funcs> <func> - <name>parse_transform(Forms,_Options) -> Forms</name> + <name name="parse_transform" arity="2"/> <fsummary>Transforms Erlang abstract format containing calls to ets/dbg:fun2ms into literal match specifications.</fsummary> - <type> - <v>Forms = Erlang abstract code format, see the erl_parse module description </v> - <v>_Options = Option list, required but not used</v> - </type> + <type_desc variable="Options">Option list, required but not used.</type_desc> <desc> <p>Implements the actual transformation at compile time. This function is called by the compiler to do the source code @@ -617,29 +614,21 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </desc> </func> <func> - <name>transform_from_shell(Dialect,Clauses,BoundEnvironment) -> term()</name> + <name name="transform_from_shell" arity="3"/> <fsummary>Used when transforming fun's created in the shell into match_specifications.</fsummary> - <type> - <v>Dialect = ets | dbg</v> - <v>Clauses = Erlang abstract form for a single fun</v> - <v>BoundEnvironment = [{atom(), term()}, ...], list of variable bindings in the shell environment</v> - </type> + <type_desc variable="BoundEnvironment">List of variable bindings in the shell environment.</type_desc> <desc> <p>Implements the actual transformation when the <c>fun2ms</c> functions are called from the shell. In this case the abstract form is for one single fun (parsed by the Erlang shell), and all imported variables should be in the key-value list passed - as <c>BoundEnvironment</c>. The result is a term, normalized, + as <c><anno>BoundEnvironment</anno></c>. The result is a term, normalized, i.e. not in abstract format.</p> </desc> </func> <func> - <name>format_error(Errcode) -> ErrMessage</name> + <name name="format_error" arity="1"/> <fsummary>Error formatting function as required by the parse_transform interface.</fsummary> - <type> - <v>Errcode = term()</v> - <v>ErrMessage = string()</v> - </type> <desc> <p>Takes an error code returned by one of the other functions in the module and creates a textual description of the diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 8cd499f960..c2676b1de5 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml index 1b8b74534b..319083d35b 100644 --- a/lib/stdlib/doc/src/orddict.xml +++ b/lib/stdlib/doc/src/orddict.xml @@ -46,176 +46,123 @@ (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ordered_dictionary() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name name="orddict"/> + <desc><p>As returned by new/0.</p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>append(Key, Value, Orddict1) -> Orddict2</name> + <name name="append" arity="3"/> <fsummary>Append a value to keys in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function appends a new <c>Value</c> to the current list - of values associated with <c>Key</c>. An exception is - generated if the initial value associated with <c>Key</c> is + <p>This function appends a new <c><anno>Value</anno></c> to the current list + of values associated with <c><anno>Key</anno></c>. An exception is + generated if the initial value associated with <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>append_list(Key, ValList, Orddict1) -> Orddict2</name> + <name name="append_list" arity="3"/> <fsummary>Append new values to keys in a dictionary</fsummary> - <type> - <v>ValList = [Value]</v> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function appends a list of values <c>ValList</c> to - the current list of values associated with <c>Key</c>. An + <p>This function appends a list of values <c><anno>ValList</anno></c> to + the current list of values associated with <c><anno>Key</anno></c>. An exception is generated if the initial value associated with - <c>Key</c> is not a list of values.</p> + <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>erase(Key, Orddict1) -> Orddict2</name> + <name name="erase" arity="2"/> <fsummary>Erase a key from a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> <p>This function erases all items with a given key from a dictionary.</p> </desc> </func> <func> - <name>fetch(Key, Orddict) -> Value</name> + <name name="fetch" arity="2"/> <fsummary>Look-up values in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function returns the value associated with <c>Key</c> - in the dictionary <c>Orddict</c>. <c>fetch</c> assumes that - the <c>Key</c> is present in the dictionary and an exception - is generated if <c>Key</c> is not in the dictionary.</p> + <p>This function returns the value associated with <c><anno>Key</anno></c> + in the dictionary <c><anno>Orddict</anno></c>. <c>fetch</c> assumes that + the <c><anno>Key</anno></c> is present in the dictionary and an exception + is generated if <c><anno>Key</anno></c> is not in the dictionary.</p> </desc> </func> <func> - <name>fetch_keys(Orddict) -> Keys</name> + <name name="fetch_keys" arity="1"/> <fsummary>Return all keys in a dictionary</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - <v>Keys = [term()]</v> - </type> <desc> <p>This function returns a list of all keys in the dictionary.</p> </desc> </func> <func> - <name>filter(Pred, Orddict1) -> Orddict2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Key, Value) -> bool()</v> - <v> Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p><c>Orddict2</c> is a dictionary of all keys and values in - <c>Orddict1</c> for which <c>Pred(Key, Value)</c> is <c>true</c>.</p> + <p><c><anno>Orddict2</anno></c> is a dictionary of all keys and values in + <c><anno>Orddict1</anno></c> for which <c><anno>Pred</anno>(<anno>Key</anno>, <anno>Value</anno>)</c> is <c>true</c>.</p> </desc> </func> <func> - <name>find(Key, Orddict) -> {ok, Value} | error</name> + <name name="find" arity="2"/> <fsummary>Search for a key in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> <p>This function searches for a key in a dictionary. Returns - <c>{ok, Value}</c> where <c>Value</c> is the value associated - with <c>Key</c>, or <c>error</c> if the key is not present in + <c>{ok, <anno>Value</anno>}</c> where <c><anno>Value</anno></c> is the value associated + with <c><anno>Key</anno></c>, or <c>error</c> if the key is not present in the dictionary.</p> </desc> </func> <func> - <name>fold(Fun, Acc0, Orddict) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value, AccIn) -> AccOut</v> - <v>Key = Value = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>Calls <c>Fun</c> on successive keys and values of - <c>Orddict</c> together with an extra argument <c>Acc</c> - (short for accumulator). <c>Fun</c> must return a new - accumulator which is passed to the next call. <c>Acc0</c> is + <p>Calls <c><anno>Fun</anno></c> on successive keys and values of + <c><anno>Orddict</anno></c> together with an extra argument <c>Acc</c> + (short for accumulator). <c><anno>Fun</anno></c> must return a new + accumulator which is passed to the next call. <c><anno>Acc0</anno></c> is returned if the list is empty. The evaluation order is undefined.</p> </desc> </func> <func> - <name>from_list(List) -> Orddict</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list of pairs to a dictionary</fsummary> - <type> - <v>List = [{Key, Value}]</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function converts the <c>Key</c> - <c>Value</c> list - <c>List</c> to a dictionary.</p> + <p>This function converts the <c><anno>Key</anno></c> - <c><anno>Value</anno></c> list + <c><anno>List</anno></c> to a dictionary.</p> </desc> </func> <func> - <name>is_key(Key, Orddict) -> bool()</name> + <name name="is_key" arity="2"/> <fsummary>Test if a key is in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function tests if <c>Key</c> is contained in - the dictionary <c>Orddict</c>.</p> + <p>This function tests if <c><anno>Key</anno></c> is contained in + the dictionary <c><anno>Orddict</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, Orddict1) -> Orddict2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value1) -> Value2</v> - <v> Key = Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p><c>map</c> calls <c>Func</c> on successive keys and values - of <c>Orddict</c> to return a new value for each key. + <p><c>map</c> calls <c><anno>Fun</anno></c> on successive keys and values + of <c><anno>Orddict1</anno></c> to return a new value for each key. The evaluation order is undefined.</p> </desc> </func> <func> - <name>merge(Fun, Orddict1, Orddict2) -> Orddict3</name> + <name name="merge" arity="3"/> <fsummary>Merge two dictionaries</fsummary> - <type> - <v>Fun = fun(Key, Value1, Value2) -> Value</v> - <v> Key = Value1 = Value2 = Value3 = term()</v> - <v>Orddict1 = Orddict2 = Orddict3 = ordered_dictionary()</v> - </type> <desc> - <p><c>merge</c> merges two dictionaries, <c>Orddict1</c> and - <c>Orddict2</c>, to create a new dictionary. All the <c>Key</c> - - <c>Value</c> pairs from both dictionaries are included in + <p><c>merge</c> merges two dictionaries, <c><anno>Orddict1</anno></c> and + <c><anno>Orddict2</anno></c>, to create a new dictionary. All the <c><anno>Key</anno></c> + - <c><anno>Value</anno></c> pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - <c>Fun</c> is called with the key and both values to return a + <c><anno>Fun</anno></c> is called with the key and both values to return a new value. <c>merge</c> could be defined as:</p> <code type="none"> merge(Fun, D1, D2) -> @@ -226,75 +173,52 @@ merge(Fun, D1, D2) -> </desc> </func> <func> - <name>new() -> ordered_dictionary()</name> + <name name="new" arity="0"/> <fsummary>Create a dictionary</fsummary> <desc> <p>This function creates a new dictionary.</p> </desc> </func> <func> - <name>size(Orddict) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in an ordered dictionary</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>Returns the number of elements in an <c>Orddict</c>.</p> + <p>Returns the number of elements in an <c><anno>Orddict</anno></c>.</p> </desc> </func> <func> - <name>store(Key, Value, Orddict1) -> Orddict2</name> + <name name="store" arity="3"/> <fsummary>Store a value in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function stores a <c>Key</c> - <c>Value</c> pair in a - dictionary. If the <c>Key</c> already exists in <c>Orddict1</c>, - the associated value is replaced by <c>Value</c>.</p> + <p>This function stores a <c><anno>Key</anno></c> - <c><anno>Value</anno></c> pair in a + dictionary. If the <c><anno>Key</anno></c> already exists in <c><anno>Orddict1</anno></c>, + the associated value is replaced by <c><anno>Value</anno></c>.</p> </desc> </func> <func> - <name>to_list(Orddict) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a dictionary to a list of pairs</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - <v>List = [{Key, Value}]</v> - </type> <desc> <p>This function converts the dictionary to a list representation.</p> </desc> </func> <func> - <name>update(Key, Fun, Orddict1) -> Orddict2</name> + <name name="update" arity="3"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Update a value in a dictionary by calling <c>Fun</c> on + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on the value to get a new value. An exception is generated if - <c>Key</c> is not present in the dictionary.</p> + <c><anno>Key</anno></c> is not present in the dictionary.</p> </desc> </func> <func> - <name>update(Key, Fun, Initial, Orddict1) -> Orddict2</name> + <name name="update" arity="4"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = Initial = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Update a value in a dictionary by calling <c>Fun</c> on - the value to get a new value. If <c>Key</c> is not present - in the dictionary then <c>Initial</c> will be stored as + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on + the value to get a new value. If <c><anno>Key</anno></c> is not present + in the dictionary then <c><anno>Initial</anno></c> will be stored as the first value. For example <c>append/3</c> could be defined as:</p> <code type="none"> @@ -303,17 +227,12 @@ append(Key, Val, D) -> </desc> </func> <func> - <name>update_counter(Key, Increment, Orddict1) -> Orddict2</name> + <name name="update_counter" arity="3"/> <fsummary>Increment a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Increment = number()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Add <c>Increment</c> to the value associated with <c>Key</c> - and store this value. If <c>Key</c> is not present in - the dictionary then <c>Increment</c> will be stored as + <p>Add <c><anno>Increment</anno></c> to the value associated with <c><anno>Key</anno></c> + and store this value. If <c><anno>Key</anno></c> is not present in + the dictionary then <c><anno>Increment</anno></c> will be stored as the first value.</p> <p>This could be defined as:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml index a20ab2d879..1e26fc2022 100644 --- a/lib/stdlib/doc/src/ordsets.xml +++ b/lib/stdlib/doc/src/ordsets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,202 +45,141 @@ different if and only if they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ordered_set() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name name="ordset" n_vars="1"/> + <desc><p>As returned by new/0.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>new() -> Ordset</name> + <name name="new" arity="0"/> <fsummary>Return an empty set</fsummary> - <type> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns a new empty ordered set.</p> </desc> </func> <func> - <name>is_set(Ordset) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for an <c>Ordset</c></fsummary> - <type> - <v>Ordset = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Ordset</c> is an ordered set of + <p>Returns <c>true</c> if <c><anno>Ordset</anno></c> is an ordered set of elements, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>size(Ordset) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a set</fsummary> - <type> - <v>Ordset = term()</v> - </type> <desc> - <p>Returns the number of elements in <c>Ordset</c>.</p> + <p>Returns the number of elements in <c><anno>Ordset</anno></c>.</p> </desc> </func> <func> - <name>to_list(Ordset) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert an <c>Ordset</c>into a list</fsummary> - <type> - <v>Ordset = ordered_set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Ordset</c> as a list.</p> + <p>Returns the elements of <c><anno>Ordset</anno></c> as a list.</p> </desc> </func> <func> - <name>from_list(List) -> Ordset</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into an <c>Ordset</c></fsummary> - <type> - <v>List = [term()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Returns an ordered set of the elements in <c>List</c>.</p> + <p>Returns an ordered set of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>is_element(Element, Ordset) -> bool()</name> + <name name="is_element" arity="2"/> <fsummary>Test for membership of an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Ordset</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Ordset</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>add_element(Element, Ordset1) -> Ordset2</name> + <name name="add_element" arity="2"/> <fsummary>Add an element to an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns a new ordered set formed from <c>Ordset1</c> with - <c>Element</c> inserted.</p> + <p>Returns a new ordered set formed from <c><anno>Ordset1</anno></c> with + <c><anno>Element</anno></c> inserted.</p> </desc> </func> <func> - <name>del_element(Element, Ordset1) -> Ordset2</name> + <name name="del_element" arity="2"/> <fsummary>Remove an element from an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>Ordset1</c>, but with <c>Element</c> removed.</p> + <p>Returns <c><anno>Ordset1</anno></c>, but with <c><anno>Element</anno></c> removed.</p> </desc> </func> <func> - <name>union(Ordset1, Ordset2) -> Ordset3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns the merged (union) set of <c>Ordset1</c> and - <c>Ordset2</c>.</p> + <p>Returns the merged (union) set of <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>union(OrdsetList) -> Ordset</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of <c>Ordsets</c></fsummary> - <type> - <v>OrdsetList = [ordered_set()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns the merged (union) set of the list of sets.</p> </desc> </func> <func> - <name>intersection(Ordset1, Ordset2) -> Ordset3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns the intersection of <c>Ordset1</c> and - <c>Ordset2</c>.</p> + <p>Returns the intersection of <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>intersection(OrdsetList) -> Ordset</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of <c>Ordsets</c></fsummary> - <type> - <v>OrdsetList = [ordered_set()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of sets.</p> </desc> </func> <func> - <name>is_disjoint(Ordset1, Ordset2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two <c>Ordsets</c> are disjoint</fsummary> - <type> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Ordset1</c> and - <c>Ordset2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>subtract(Ordset1, Ordset2) -> Ordset3</name> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns only the elements of <c>Ordset1</c> which are not - also elements of <c>Ordset2</c>.</p> + <p>Returns only the elements of <c><anno>Ordset1</anno></c> which are not + also elements of <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>is_subset(Ordset1, Ordset2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Ordset</c>1 is - also a member of <c>Ordset2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Ordset1</anno></c> is + also a member of <c><anno>Ordset2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Ordset) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Fold <c>Function</c> over every element in <c>Ordset</c> + <p>Fold <c><anno>Function</anno></c> over every element in <c><anno>Ordset</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>filter(Pred, Ordset1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v>Set1 = Set2 = ordered_set()</v> - </type> <desc> - <p>Filter elements in <c>Set1</c> with boolean function - <c>Fun</c>.</p> + <p>Filter elements in <c><anno>Ordset1</anno></c> with boolean function + <c><anno>Pred</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml index b174d4f7d4..c56db8c6e6 100644 --- a/lib/stdlib/doc/src/pg.xml +++ b/lib/stdlib/doc/src/pg.xml @@ -51,77 +51,56 @@ </description> <funcs> <func> - <name>create(PgName) -> ok | {error, Reason}</name> + <name name="create" arity="1"/> <fsummary>Create an empty group</fsummary> - <type> - <v>PgName = term()</v> - <v>Reason = already_created | term()</v> - </type> <desc> - <p>Creates an empty group named <c>PgName</c> on the current + <p>Creates an empty group named <c><anno>PgName</anno></c> on the current node.</p> </desc> </func> <func> - <name>create(PgName, Node) -> ok | {error, Reason}</name> + <name name="create" arity="2"/> <fsummary>Create an empty group on another node</fsummary> - <type> - <v>PgName = term()</v> - <v>Node = node()</v> - <v>Reason = already_created | term()</v> - </type> <desc> - <p>Creates an empty group named <c>PgName</c> on the node - <c>Node</c>.</p> + <p>Creates an empty group named <c><anno>PgName</anno></c> on the node + <c><anno>Node</anno></c>.</p> </desc> </func> <func> - <name>join(PgName, Pid) -> Members</name> + <name name="join" arity="2"/> <fsummary>Join a pid to a process group</fsummary> - <type> - <v>PgName = term()</v> - <v>Pid = pid()</v> - <v>Members = [pid()]</v> - </type> <desc> - <p>Joins the pid <c>Pid</c> to the process group <c>PgName</c>. + <p>Joins the pid <c><anno>Pid</anno></c> to the process group + <c><anno>PgName</anno></c>. Returns a list of all old members of the group.</p> </desc> </func> <func> - <name>send(PgName, Msg) -> void()</name> + <name name="send" arity="2"/> <fsummary>Send a message to all members of a process group</fsummary> - <type> - <v>PgName = Msg = term()</v> - </type> <desc> <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to - all members of the process group <c>PgName</c>.</p> - <p>Failure: <c>{badarg, {PgName, Msg}}</c> if <c>PgName</c> is + all members of the process group <c><anno>PgName</anno></c>.</p> + <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> + if <c><anno>PgName</anno></c> is not a process group (a globally registered name).</p> </desc> </func> <func> - <name>esend(PgName, Msg) -> void()</name> + <name name="esend" arity="2"/> <fsummary>Send a message to all members of a process group, except ourselves</fsummary> - <type> - <v>PgName = Msg = term()</v> - </type> <desc> <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to - all members of the process group <c>PgName</c>, except + all members of the process group <c><anno>PgName</anno></c>, except ourselves.</p> - <p>Failure: <c>{badarg, {PgName, Msg}}</c> if <c>PgName</c> is + <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> + if <c><anno>PgName</anno></c> is not a process group (a globally registered name).</p> </desc> </func> <func> - <name>members(PgName) -> Members</name> + <name name="members" arity="1"/> <fsummary>Return a list of all members of a process group</fsummary> - <type> - <v>PgName = term()</v> - <v>Members = [pid()]</v> - </type> <desc> <p>Returns a list of all members of the process group <c>PgName</c>.</p> diff --git a/lib/stdlib/doc/src/pool.xml b/lib/stdlib/doc/src/pool.xml index 2b890352eb..39a79e5dc5 100644 --- a/lib/stdlib/doc/src/pool.xml +++ b/lib/stdlib/doc/src/pool.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,23 +48,19 @@ </description> <funcs> <func> - <name>start(Name) -></name> - <name>start(Name, Args) -> Nodes</name> + <name name="start" arity="1"/> + <name name="start" arity="2"/> <fsummary>>Start a new pool</fsummary> - <type> - <v>Name = atom()</v> - <v>Args = string()</v> - <v>Nodes = [node()]</v> - </type> <desc> <p>Starts a new pool. The file <c>.hosts.erlang</c> is read to find host names where the pool nodes can be started. See section <seealso marker="#files">Files</seealso> below. The start-up procedure fails if the file is not found.</p> <p>The slave nodes are started with <c>slave:start/2,3</c>, - passing along <c>Name</c> and, if provided, <c>Args</c>. - <c>Name</c> is used as the first part of the node names, - <c>Args</c> is used to specify command line arguments. See + passing along <c><anno>Name</anno></c> and, if provided, + <c><anno>Args</anno></c>. + <c><anno>Name</anno></c> is used as the first part of the node names, + <c><anno>Args</anno></c> is used to specify command line arguments. See <seealso marker="slave#start/2">slave(3)</seealso>.</p> <p>Access rights must be set so that all nodes in the pool have the authority to access each other.</p> @@ -73,59 +69,45 @@ </desc> </func> <func> - <name>attach(Node) -> already_attached | attached</name> + <name name="attach" arity="1"/> <fsummary>Ensure that a pool master is running</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> <p>This function ensures that a pool master is running and - includes <c>Node</c> in the pool master's pool of nodes.</p> + includes <c><anno>Node</anno></c> in the pool master's pool of nodes.</p> </desc> </func> <func> - <name>stop() -> stopped</name> + <name name="stop" arity="0"/> <fsummary>Stop the pool and kill all the slave nodes</fsummary> <desc> <p>Stops the pool and kills all the slave nodes.</p> </desc> </func> <func> - <name>get_nodes() -> Nodes</name> + <name name="get_nodes" arity="0"/> <fsummary>Return a list of the current member nodes of the pool</fsummary> - <type> - <v>Nodes = [node()]</v> - </type> <desc> <p>Returns a list of the current member nodes of the pool.</p> </desc> </func> <func> - <name>pspawn(Mod, Fun, Args) -> pid()</name> + <name name="pspawn" arity="3"/> <fsummary>Spawn a process on the pool node with expected lowest future load</fsummary> - <type> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Spawns a process on the pool node which is expected to have the lowest future load.</p> </desc> </func> <func> - <name>pspawn_link(Mod, Fun, Args) -> pid()</name> + <name name="pspawn_link" arity="3"/> <fsummary>Spawn and link to a process on the pool node with expected lowest future load</fsummary> - <type> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Spawn links a process on the pool node which is expected to have the lowest future load.</p> </desc> </func> <func> - <name>get_node() -> node()</name> + <name name="get_node" arity="0"/> <fsummary>Return the node with the expected lowest future load</fsummary> <desc> <p>Returns the node with the expected lowest future load.</p> diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index 791001cb52..abc17c4a91 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,19 +60,33 @@ information regarding other processes which terminate as a result of this process terminating.</p> </description> + <datatypes> + <datatype> + <name name="spawn_option"/> + <desc> + <p>See <seealso marker="erts:erlang#spawn_opt/4"> + erlang:spawn_opt/2,3,4,5</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="priority_level"/> + </datatype> + <datatype> + <name name="dict_or_pid"/> + </datatype> + </datatypes> <funcs> <func> - <name>spawn(Fun) -> pid()</name> - <name>spawn(Node, Fun) -> pid()</name> - <name>spawn(Module, Function, Args) -> pid()</name> - <name>spawn(Node, Module, Function, Args) -> pid()</name> + <name name="spawn" arity="1"/> + <name name="spawn" arity="2"/> + <name name="spawn" arity="3"/> + <name name="spawn" arity="4"/> <fsummary>Spawn a new process.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -80,17 +94,16 @@ </desc> </func> <func> - <name>spawn_link(Fun) -> pid()</name> - <name>spawn_link(Node, Fun) -> pid()</name> - <name>spawn_link(Module, Function, Args) -> pid()</name> - <name>spawn_link(Node, Module, Function, Args) -> pid()</name> + <name name="spawn_link" arity="1"/> + <name name="spawn_link" arity="2"/> + <name name="spawn_link" arity="3"/> + <name name="spawn_link" arity="4"/> <fsummary>Spawn and link to a new process.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -99,18 +112,17 @@ </desc> </func> <func> - <name>spawn_opt(Fun, SpawnOpts) -> pid()</name> - <name>spawn_opt(Node, Fun, SpawnOpts) -> pid()</name> - <name>spawn_opt(Module, Function, Args, SpawnOpts) -> pid()</name> - <name>spawn_opt(Node, Module, Func, Args, SpawnOpts) -> pid()</name> + <name name="spawn_opt" arity="2"/> + <name name="spawn_opt" arity="3"/> + <name name="spawn_opt" arity="4"/> + <name name="spawn_opt" arity="5"/> <fsummary>Spawn a new process with given options.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>SpawnOpts -- see erlang:spawn_opt/2,3,4,5</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> + <type variable="SpawnOpts"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -124,20 +136,13 @@ </desc> </func> <func> - <name>start(Module, Function, Args) -> Ret</name> - <name>start(Module, Function, Args, Time) -> Ret</name> - <name>start(Module, Function, Args, Time, SpawnOpts) -> Ret</name> - <name>start_link(Module, Function, Args) -> Ret</name> - <name>start_link(Module, Function, Args, Time) -> Ret</name> - <name>start_link(Module, Function, Args, Time, SpawnOpts) -> Ret</name> + <name name="start" arity="3"/> + <name name="start" arity="4"/> + <name name="start" arity="5"/> + <name name="start_link" arity="3"/> + <name name="start_link" arity="4"/> + <name name="start_link" arity="5"/> <fsummary>Start a new process synchronously.</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Time = int() >= 0 | infinity</v> - <v>SpawnOpts -- see erlang:spawn_opt/2,3,4,5</v> - <v>Ret = term() | {error, Reason}</v> - </type> <desc> <p>Starts a new process synchronously. Spawns the process and waits for it to start. When the process has started, it @@ -148,13 +153,13 @@ function. At this time, <c>Ret</c> is returned.</p> <p>If the <c>start_link/3,4,5</c> function is used and the process crashes before it has called <c>init_ack/1,2</c>, - <c>{error, Reason}</c> is returned if the calling process + <c>{error, <anno>Reason</anno>}</c> is returned if the calling process traps exits.</p> - <p>If <c>Time</c> is specified as an integer, this function - waits for <c>Time</c> milliseconds for the new process to call + <p>If <c><anno>Time</anno></c> is specified as an integer, this function + waits for <c><anno>Time</anno></c> milliseconds for the new process to call <c>init_ack</c>, or <c>{error, timeout}</c> is returned, and the process is killed.</p> - <p>The <c>SpawnOpts</c> argument, if given, will be passed + <p>The <c><anno>SpawnOpts</anno></c> argument, if given, will be passed as the last argument to the <c>spawn_opt/2,3,4,5</c> BIF.</p> <note> <p>Using the spawn option <c>monitor</c> is currently not @@ -164,17 +169,13 @@ </desc> </func> <func> - <name>init_ack(Parent, Ret) -> void()</name> - <name>init_ack(Ret) -> void()</name> + <name name="init_ack" arity="1"/> + <name name="init_ack" arity="2"/> <fsummary>Used by a process when it has started.</fsummary> - <type> - <v>Parent = pid()</v> - <v>Ret = term()</v> - </type> <desc> <p>This function must used by a process that has been started by a <seealso marker="#start/3">start[_link]/3,4,5</seealso> - function. It tells <c>Parent</c> that the process has + function. It tells <c><anno>Parent</anno></c> that the process has initialized itself, has started, or has failed to initialize itself.</p> <p>The <c>init_ack/1</c> function uses the parent value @@ -205,40 +206,30 @@ init(Parent) -> </desc> </func> <func> - <name>format(CrashReport) -> string()</name> + <name name="format" arity="1"/> <fsummary>Format a crash report.</fsummary> - <type> - <v>CrashReport = term()</v> - </type> <desc> <p>This function can be used by a user defined event handler to format a crash report. The crash report is sent using - <c>error_logger:error_report(crash_report, CrashReport)</c>. + <c>error_logger:error_report(crash_report, <anno>CrashReport</anno>)</c>. That is, the event to be handled is of the format - <c>{error_report, GL, {Pid, crash_report, CrashReport}}</c> + <c>{error_report, GL, {Pid, crash_report, <anno>CrashReport</anno>}}</c> where <c>GL</c> is the group leader pid of the process <c>Pid</c> which sent the crash report.</p> </desc> </func> <func> - <name>initial_call(Process) -> {Module,Function,Args} | false</name> + <name name="initial_call" arity="1"/> <fsummary>Extract the initial call of a <c>proc_lib</c>spawned process.</fsummary> - <type> - <v>Process = pid() | {X,Y,Z} | ProcInfo</v> - <v> X = Y = Z = int()</v> - <v> ProcInfo = term()</v> - <v>Module = Function = atom()</v> - <v>Args = [atom()]</v> - </type> <desc> <p>Extracts the initial call of a process that was started using one of the spawn or start functions described above. - <c>Process</c> can either be a pid, an integer tuple (from + <c><anno>Process</anno></c> can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process <c>Pid</c> fetched through an <c>erlang:process_info(Pid)</c> function call.</p> - <note><p>The list <c>Args</c> no longer contains the actual arguments, + <note><p>The list <c><anno>Args</anno></c> no longer contains the actual arguments, but the same number of atoms as the number of arguments; the first atom is always <c>'Argument__1'</c>, the second <c>'Argument__2'</c>, and so on. The reason is that the argument list could waste a significant @@ -256,23 +247,15 @@ init(Parent) -> </desc> </func> <func> - <name>translate_initial_call(Process) -> {Module,Function,Arity} | Fun</name> + <name name="translate_initial_call" arity="1"/> <fsummary>Extract and translate the initial call of a <c>proc_lib</c>spawned process.</fsummary> - <type> - <v>Process = pid() | {X,Y,Z} | ProcInfo</v> - <v> X = Y = Z = int()</v> - <v> ProcInfo = term()</v> - <v>Module = Function = atom()</v> - <v>Arity = int()</v> - <v>Fun = fun() -> void()</v> - </type> <desc> <p>This function is used by the <c>c:i/0</c> and <c>c:regs/0</c> functions in order to present process information.</p> <p>Extracts the initial call of a process that was started using one of the spawn or start functions described above, - and translates it to more useful information. <c>Process</c> + and translates it to more useful information. <c><anno>Process</anno></c> can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process <c>Pid</c> fetched through an <c>erlang:process_info(Pid)</c> @@ -280,15 +263,15 @@ init(Parent) -> <p>If the initial call is to one of the system defined behaviors such as <c>gen_server</c> or <c>gen_event</c>, it is translated to more useful information. If a <c>gen_server</c> - is spawned, the returned <c>Module</c> is the name of - the callback module and <c>Function</c> is <c>init</c> + is spawned, the returned <c><anno>Module</anno></c> is the name of + the callback module and <c><anno>Function</anno></c> is <c>init</c> (the function that initiates the new server).</p> <p>A <c>supervisor</c> and a <c>supervisor_bridge</c> are also <c>gen_server</c> processes. In order to return information that this process is a supervisor and the name of the - call-back module, <c>Module</c> is <c>supervisor</c> and - <c>Function</c> is the name of the supervisor callback - module. <c>Arity</c> is <c>1</c> since the <c>init/1</c> + call-back module, <c><anno>Module</anno></c> is <c>supervisor</c> and + <c><anno>Function</anno></c> is the name of the supervisor callback + module. <c><anno>Arity</anno></c> is <c>1</c> since the <c>init/1</c> function is called initially in the callback module.</p> <p>By default, <c>{proc_lib,init_p,5}</c> is returned if no information about the initial call can be found. It is @@ -297,12 +280,8 @@ init(Parent) -> </desc> </func> <func> - <name>hibernate(Module, Function, Args)</name> + <name name="hibernate" arity="3"/> <fsummary>Hibernate a process until a message is sent to it</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>This function does the same as (and does call) the BIF <seealso marker="erts:erlang#erlang:hibernate/3">hibernate/3</seealso>, diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index 9f1c5b24ad..225c5e97eb 100644 --- a/lib/stdlib/doc/src/proplists.xml +++ b/lib/stdlib/doc/src/proplists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2002</year><year>2010</year> + <year>2002</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,14 +48,15 @@ words, numbers are compared literally rather than by value, so that, for instance, <c>1</c> and <c>1.0</c> are different keys.</p> </description> + <datatypes> + <datatype> + <name name="property"/> + </datatype> + </datatypes> <funcs> <func> - <name>append_values(Key, List) -> List</name> + <name name="append_values" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Similar to <c>get_all_values/2</c>, but each value is wrapped in a list unless it is already itself a list, and the @@ -65,11 +66,8 @@ </desc> </func> <func> - <name>compact(List) -> List</name> + <name name="compact" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> <p>Minimizes the representation of all entries in the list. This is equivalent to <c><![CDATA[[property(P) || P <- List]]]></c>.</p> @@ -77,33 +75,24 @@ </desc> </func> <func> - <name>delete(Key, List) -> List</name> + <name name="delete" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Deletes all entries associated with <c>Key</c> from - <c>List</c>.</p> + <p>Deletes all entries associated with <c><anno>Key</anno></c> from + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>expand(Expansions, List) -> List</name> + <name name="expand" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Expansions = [{Property,[term()]}]</v> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Expands particular properties to corresponding sets of - properties (or other terms). For each pair <c>{Property, Expansion}</c> in <c>Expansions</c>, if <c>E</c> is - the first entry in <c>List</c> with the same key as - <c>Property</c>, and <c>E</c> and <c>Property</c> + properties (or other terms). For each pair <c>{<anno>Property</anno>, <anno>Expansion</anno>}</c> in <c><anno>Expansions</anno></c>, if <c>E</c> is + the first entry in <c><anno>List</anno></c> with the same key as + <c><anno>Property</anno></c>, and <c>E</c> and <c><anno>Property</anno></c> have equivalent normal forms, then <c>E</c> is replaced with - the terms in <c>Expansion</c>, and any following entries with - the same key are deleted from <c>List</c>.</p> + the terms in <c><anno>Expansion</anno></c>, and any following entries with + the same key are deleted from <c><anno>List</anno></c>.</p> <p>For example, the following expressions all return <c>[fie, bar, baz, fum]</c>:</p> <code type="none"> expand([{foo, [bar, baz]}], @@ -120,103 +109,75 @@ <p>Note that if the original property term is to be preserved in the result when expanded, it must be included in the expansion list. The inserted terms are not expanded recursively. If - <c>Expansions</c> contains more than one property with the same + <c><anno>Expansions</anno></c> contains more than one property with the same key, only the first occurrence is used.</p> <p>See also: <c>normalize/2</c>.</p> </desc> </func> <func> - <name>get_all_values(Key, List) -> [term()]</name> + <name name="get_all_values" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Similar to <c>get_value/2</c>, but returns the list of values for <em>all</em> entries <c>{Key, Value}</c> in - <c>List</c>. If no such entry exists, the result is the empty + <c><anno>List</anno></c>. If no such entry exists, the result is the empty list.</p> <p>See also: <c>get_value/2</c>.</p> </desc> </func> <func> - <name>get_bool(Key, List) -> bool()</name> + <name name="get_bool" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Returns the value of a boolean key/value option. If - <c>lookup(Key, List)</c> would yield <c>{Key, true}</c>, + <c>lookup(<anno>Key</anno>, <anno>List</anno>)</c> would yield <c>{<anno>Key</anno>, true}</c>, this function returns <c>true</c>; otherwise <c>false</c> is returned.</p> <p>See also: <c>get_value/2</c>, <c>lookup/2</c>.</p> </desc> </func> <func> - <name>get_keys(List) -> [term()]</name> + <name name="get_keys" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> - <p>Returns an unordered list of the keys used in <c>List</c>, + <p>Returns an unordered list of the keys used in <c><anno>List</anno></c>, not containing duplicates.</p> </desc> </func> <func> - <name>get_value(Key, List) -> term()</name> + <name name="get_value" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Equivalent to <c>get_value(Key, List, undefined)</c>.</p> + <p>Equivalent to <c>get_value(<anno>Key</anno>, <anno>List</anno>, undefined)</c>.</p> </desc> </func> <func> - <name>get_value(Key, List, Default) -> term()</name> + <name name="get_value" arity="3"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Default = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Returns the value of a simple key/value property in - <c>List</c>. If <c>lookup(Key, List)</c> would yield - <c>{Key, Value}</c>, this function returns the corresponding - <c>Value</c>, otherwise <c>Default</c> is returned.</p> + <c><anno>List</anno></c>. If <c>lookup(<anno>Key</anno>, <anno>List</anno>)</c> would yield + <c>{<anno>Key</anno>, Value}</c>, this function returns the corresponding + <c>Value</c>, otherwise <c><anno>Default</anno></c> is returned.</p> <p>See also: <c>get_all_values/2</c>, <c>get_bool/2</c>, <c>get_value/2</c>, <c>lookup/2</c>.</p> </desc> </func> <func> - <name>is_defined(Key, List) -> bool()</name> + <name name="is_defined" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>List</c> contains at least - one entry associated with <c>Key</c>, otherwise + <p>Returns <c>true</c> if <c><anno>List</anno></c> contains at least + one entry associated with <c><anno>Key</anno></c>, otherwise <c>false</c> is returned.</p> </desc> </func> <func> - <name>lookup(Key, List) -> none | tuple()</name> + <name name="lookup" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the first entry associated with <c>Key</c> in - <c>List</c>, if one exists, otherwise returns + <p>Returns the first entry associated with <c><anno>Key</anno></c> in + <c><anno>List</anno></c>, if one exists, otherwise returns <c>none</c>. For an atom <c>A</c> in the list, the tuple <c>{A, true}</c> is the entry associated with <c>A</c>.</p> <p>See also: <c>get_bool/2</c>, <c>get_value/2</c>, @@ -224,34 +185,20 @@ </desc> </func> <func> - <name>lookup_all(Key, List) -> [tuple()]</name> + <name name="lookup_all" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the list of all entries associated with <c>Key</c> - in <c>List</c>. If no such entry exists, the result is the + <p>Returns the list of all entries associated with <c><anno>Key</anno></c> + in <c><anno>List</anno></c>. If no such entry exists, the result is the empty list.</p> <p>See also: <c>lookup/2</c>.</p> </desc> </func> <func> - <name>normalize(List, Stages) -> List</name> + <name name="normalize" arity="2"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - <v>Stages = [Operation]</v> - <v>Operation = {aliases, Aliases} | {negations, Negations} | {expand, Expansions}</v> - <v>Aliases = [{Key, Key}]</v> - <v>Negations = [{Key, Key}]</v> - <v>Key = term()</v> - <v>Expansions = [{Property, [term()]}]</v> - <v>Property = atom() | tuple()</v> - </type> <desc> - <p>Passes <c>List</c> through a sequence of + <p>Passes <c><anno>List</anno></c> through a sequence of substitution/expansion stages. For an <c>aliases</c> operation, the function <c>substitute_aliases/2</c> is applied using the given list of aliases; for a <c>negations</c> operation, @@ -270,51 +217,37 @@ </desc> </func> <func> - <name>property(Property) -> Property</name> + <name name="property" arity="1"/> <fsummary></fsummary> - <type> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Creates a normal form (minimal) representation of a property. If - <c>Property</c> is <c>{Key, true}</c> where <c>Key</c> is + <c><anno>Property</anno></c> is <c>{Key, true}</c> where <c>Key</c> is an atom, this returns <c>Key</c>, otherwise the whole term - <c>Property</c> is returned.</p> + <c><anno>Property</anno></c> is returned.</p> <p>See also: <c>property/2</c>.</p> </desc> </func> <func> - <name>property(Key, Value) -> Property</name> + <name name="property" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Value = term()</v> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Creates a normal form (minimal) representation of a simple - key/value property. Returns <c>Key</c> if <c>Value</c> is - <c>true</c> and <c>Key</c> is an atom, otherwise a tuple - <c>{Key, Value}</c> is returned.</p> + key/value property. Returns <c><anno>Key</anno></c> if <c><anno>Value</anno></c> is + <c>true</c> and <c><anno>Key</anno></c> is an atom, otherwise a tuple + <c>{<anno>Key</anno>, <anno>Value</anno>}</c> is returned.</p> <p>See also: <c>property/1</c>.</p> </desc> </func> <func> - <name>split(List, Keys) -> {Lists, Rest}</name> + <name name="split" arity="2"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - <v>Keys = [term()]</v> - <v>Lists = [[term()]]</v> - <v>Rest = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into a list of sublists and a - remainder. <c>Lists</c> contains one sublist for each key in - <c>Keys</c>, in the corresponding order. The relative order of + <p>Partitions <c><anno>List</anno></c> into a list of sublists and a + remainder. <c><anno>Lists</anno></c> contains one sublist for each key in + <c><anno>Keys</anno></c>, in the corresponding order. The relative order of the elements in each sublist is preserved from the original - <c>List</c>. <c>Rest</c> contains the elements in - <c>List</c> that are not associated with any of the given keys, + <c><anno>List</anno></c>. <c><anno>Rest</anno></c> contains the elements in + <c><anno>List</anno></c> that are not associated with any of the given keys, also with their original relative order preserved.</p> <p>Example: split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</p> @@ -323,19 +256,14 @@ </desc> </func> <func> - <name>substitute_aliases(Aliases, List) -> List</name> + <name name="substitute_aliases" arity="2"/> <fsummary></fsummary> - <type> - <v>Aliases = [{Key, Key}]</v> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Substitutes keys of properties. For each entry in - <c>List</c>, if it is associated with some key <c>K1</c> - such that <c>{K1, K2}</c> occurs in <c>Aliases</c>, the - key of the entry is changed to <c>Key2</c>. If the same - <c>K1</c> occurs more than once in <c>Aliases</c>, only + <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + such that <c>{K1, K2}</c> occurs in <c><anno>Aliases</anno></c>, the + key of the entry is changed to <c>K2</c>. If the same + <c>K1</c> occurs more than once in <c><anno>Aliases</anno></c>, only the first occurrence is used.</p> <p>Example: <c>substitute_aliases([{color, colour}], L)</c> will replace all tuples <c>{color, ...}</c> in <c>L</c> @@ -345,24 +273,19 @@ </desc> </func> <func> - <name>substitute_negations(Negations, List) -> List</name> + <name name="substitute_negations" arity="2"/> <fsummary></fsummary> - <type> - <v>Negations = [{Key, Key}]</v> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Substitutes keys of boolean-valued properties and simultaneously negates their values. For each entry in - <c>List</c>, if it is associated with some key <c>K1</c> - such that <c>{K1, K2}</c> occurs in <c>Negations</c>, then + <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + such that <c>{K1, K2}</c> occurs in <c><anno>Negations</anno></c>, then if the entry was <c>{K1, true}</c> it will be replaced with <c>{K2, false}</c>, otherwise it will be replaced with <c>{K2, true}</c>, thus changing the name of the option and simultaneously negating the value given by <c>get_bool(List)</c>. If the same <c>K1</c> occurs more - than once in <c>Negations</c>, only the first occurrence is + than once in <c><anno>Negations</anno></c>, only the first occurrence is used.</p> <p>Example: <c>substitute_negations([{no_foo, foo}], L)</c> will replace any atom <c>no_foo</c> or tuple @@ -374,13 +297,10 @@ </desc> </func> <func> - <name>unfold(List) -> List</name> + <name name="unfold" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> - <p>Unfolds all occurrences of atoms in <c>List</c> to tuples + <p>Unfolds all occurrences of atoms in <c><anno>List</anno></c> to tuples <c>{Atom, true}</c>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/qlc.xml b/lib/stdlib/doc/src/qlc.xml index da24ee9914..6a45ade447 100644 --- a/lib/stdlib/doc/src/qlc.xml +++ b/lib/stdlib/doc/src/qlc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,7 +45,9 @@ tables</em>. Typical QLC tables are ETS, Dets, and Mnesia tables. There is also support for user defined tables, see the <seealso marker="#implementing_a_qlc_table">Implementing a QLC - table</seealso> section. A <em>query</em> is stated using + table</seealso> section. <marker + id="query_list_comprehension"></marker> + A <em>query</em> is stated using <em>Query List Comprehensions</em> (QLCs). The answers to a query are determined by data in QLC tables that fulfill the constraints expressed by the QLCs of the query. QLCs are similar @@ -55,10 +57,11 @@ fact, in the absence of optimizations and options such as <c>cache</c> and <c>unique</c> (see below), every QLC free of QLC tables evaluates to the same list of answers as the - identical ordinary list comprehension. </p> + identical ordinary list comprehension.</p> <p>While ordinary list comprehensions evaluate to lists, calling - <seealso marker="#q">qlc:q/1,2</seealso> returns a <em>Query + <seealso marker="#q">qlc:q/1,2</seealso> returns a <marker + id="query_handle"></marker><em> Query Handle</em>. To obtain all the answers to a query, <seealso marker="#eval">qlc:eval/1,2</seealso> should be called with the query handle as first argument. Query handles are essentially @@ -69,7 +72,8 @@ Code replacement is described in the <seealso marker="doc/reference_manual:code_loading">Erlang Reference Manual</seealso>. The list of answers can also be traversed in - chunks by use of a <em>Query Cursor</em>. Query cursors are + chunks by use of a <marker + id="query_cursor"></marker><em>Query Cursor</em>. Query cursors are created by calling <seealso marker="#cursor">qlc:cursor/1,2</seealso> with a query handle as first argument. Query cursors are essentially Erlang processes. @@ -226,75 +230,6 @@ </section> - <section><title>Common data types</title> - - <list type="bulleted"> - <item><p><c>QueryCursor = {qlc_cursor, term()}</c></p> - </item> - <item><p><c>QueryHandle = {qlc_handle, term()}</c></p> - </item> - <item><p><c>QueryHandleOrList = QueryHandle | list()</c></p> - </item> - <item><p><c>Answers = [Answer]</c></p> - </item> - <item><p><c>Answer = term()</c></p> - </item> - <item><p><c>AbstractExpression = </c> - parse trees - for Erlang expressions, see the <seealso - marker="erts:absform">abstract format</seealso> - documentation in the ERTS User's Guide -</p> - </item> - <item><p><c>MatchExpression = </c> - - match specifications, see the <seealso - marker="erts:match_spec">match specification</seealso> - documentation in the ERTS User's Guide and <seealso - marker="ms_transform">ms_transform(3)</seealso> -</p> - </item> - <item><p><c>SpawnOptions = default | spawn_options()</c></p> - </item> - <item><p><c>SortOptions = [SortOption] | SortOption</c></p> - </item> - <item><p><c>SortOption = {compressed, bool()} - | {no_files, NoFiles} - | {order, Order} - | {size, Size} - | {tmpdir, TempDirectory} - | {unique, bool()} </c> - - see <seealso - marker="file_sorter">file_sorter(3)</seealso> -</p> - </item> - <item><p><c>Order = ascending | descending | OrderFun</c></p> - </item> - <item><p><c>OrderFun = fun(term(), term()) -> bool()</c></p> - </item> - <item><p><c>TempDirectory = "" | filename()</c></p> - </item> - <item><p><c>Size = int() > 0</c></p> - </item> - <item><p><c>NoFiles = int() > 1</c></p> - </item> - <item><p><c>KeyPos = int() > 0 | [int() > 0]</c></p> - </item> - <item><p><c>MaxListSize = int() >= 0</c></p> - </item> - <item><p><c>bool() = true | false</c></p> - </item> - <item><p><c>Cache = ets | list | no</c></p> - </item> - <item><p><c>TmpFileUsage = allowed | not_allowed | info_msg - | warning_msg | error_msg</c></p> - </item> - <item><p><c>filename() = </c> - see <seealso - marker="filename">filename(3)</seealso> -</p> - </item> - <item><p><c>spawn_options() = </c> - see <seealso - marker="erts:erlang">erlang(3)</seealso> -</p> - </item> - - </list> - - </section> - <section><title>Getting started</title> <p><marker id="getting_started"></marker> As already mentioned @@ -679,34 +614,105 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </section> + <datatypes> + <datatype> + <name name="abstract_expr"></name> + <desc><p>Parse trees for Erlang expression, see the <seealso + marker="erts:absform">abstract format</seealso> + documentation in the ERTS User's Guide.</p></desc> + </datatype> + <datatype> + <name name="answer"></name> + </datatype> + <datatype> + <name name="answers"></name> + </datatype> + <datatype> + <name name="cache"></name> + </datatype> + <datatype> + <name name="match_expression"></name> + <desc><p>Match specification, see the <seealso + marker="erts:match_spec">match specification</seealso> + documentation in the ERTS User's Guide and <seealso + marker="ms_transform">ms_transform(3).</seealso></p></desc> + </datatype> + <datatype> + <name name="no_files"></name> + <desc><p>Actually an integer > 1.</p></desc> + </datatype> + <datatype> + <name name="key_pos"></name> + </datatype> + <datatype> + <name name="max_list_size"></name> + </datatype> + <datatype> + <name name="order"></name> + </datatype> + <datatype> + <name name="order_fun"></name> + </datatype> + <datatype> + <name name="query_cursor"></name> + <desc><p>A <seealso marker="#query_cursor">query cursor</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="query_handle"></name> + <desc><p>A <seealso marker="#query_handle">query handle</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="query_handle_or_list"></name> + </datatype> + <datatype> + <name name="query_list_comprehension"></name> + <desc><p>A literal + <seealso marker="#query_list_comprehension">query + list comprehension</seealso>.</p></desc> + </datatype> + <datatype> + <name name="spawn_options"></name> + </datatype> + <datatype> + <name name="sort_options"></name> + </datatype> + <datatype> + <name name="sort_option"></name> + <desc><p>See <seealso + marker="file_sorter">file_sorter(3)</seealso>.</p></desc> + </datatype> + <datatype> + <name name="tmp_directory"></name> + </datatype> + <datatype> + <name name="tmp_file_usage"></name> + <desc><p></p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>append(QHL) -> QH</name> + <name name="append" arity="1"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QHL = [QueryHandleOrList]</v> - <v>QH = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH</c> all answers to the first query handle in - <c>QHL</c> is returned followed by all answers to the rest - of the query handles in <c>QHL</c>.</p> + <c><anno>QH</anno></c> all answers to the first query handle in + <c><anno>QHL</anno></c> are returned followed by all answers + to the rest of the query handles in <c><anno>QHL</anno></c>.</p> </desc> </func> <func> - <name>append(QH1, QH2) -> QH3</name> + <name name="append" arity="2"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QH2 = QueryHandleOrList</v> - <v>QH3 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH3</c> all answers to <c>QH1</c> are returned followed - by all answers to <c>QH2</c>.</p> + <c><anno>QH3</anno></c> all answers to + <c><anno>QH1</anno></c> are returned followed by all answers + to <c><anno>QH2</anno></c>.</p> <p><c>append(QH1, QH2)</c> is equivalent to <c>append([QH1, QH2])</c>.</p> @@ -714,17 +720,9 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </func> <func> - <name>cursor(QueryHandleOrList [, Options]) -> QueryCursor</name> + <name name="cursor" arity="1"/> + <name name="cursor" arity="2"/> <fsummary>Create a query cursor.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {spawn_options, SpawnOptions} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - </type> <desc> <p><marker id="cursor"></marker>Creates a query cursor and makes the calling process the owner of the cursor. The @@ -746,11 +744,13 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), [{b,1},{b,2}] 4> <input>qlc:delete_cursor(QC).</input> ok</pre> + <p><c>cursor(<anno>QH</anno>)</c> is equivalent to + <c>cursor(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>delete_cursor(QueryCursor) -> ok</name> + <name name="delete_cursor" arity="1"/> <fsummary>Delete a query cursor.</fsummary> <desc> <p>Deletes a query cursor. Only the owner of the cursor can @@ -759,19 +759,11 @@ ok</pre> </func> <func> - <name>eval(QueryHandleOrList [, Options]) -> Answers | Error</name> - <name>e(QueryHandleOrList [, Options]) -> Answers</name> + <name name="eval" arity="1"/> + <name name="eval" arity="2"/> + <name name="e" arity="1"/> + <name name="e" arity="2"/> <fsummary>Return all answers to a query.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> <p><marker id="eval"></marker>Evaluates a query handle in the calling process and collects all answers in a list.</p> @@ -780,47 +772,39 @@ ok</pre> 1> <input>QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),</input> <input>qlc:eval(QH).</input> [{a,1},{a,2},{b,1},{b,2}]</pre> + <p><c>eval(<anno>QH</anno>)</c> is equivalent to + <c>eval(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, QueryHandleOrList [, Options]) -> - Acc1 | Error</name> + <name name="fold" arity="3"/> + <name name="fold" arity="4"/> <fsummary>Fold a function over the answers to a query.</fsummary> - <type> - <v>Function = fun(Answer, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> - <p>Calls <c>Function</c> on successive answers to the query - handle together with an extra argument <c>AccIn</c>. The - query handle and the function are evaluated in the calling - process. <c>Function</c> must return a new accumulator which - is passed to the next call. <c>Acc0</c> is returned if there - are no answers to the query handle.</p> + <p>Calls <c><anno>Function</anno></c> on successive answers to + the query handle together with an extra argument + <c><anno>AccIn</anno></c>. The query handle and the function + are evaluated in the calling process. + <c><anno>Function</anno></c> must return a new accumulator + which is passed to the next call. + <c><anno>Acc0</anno></c> is returned if there are no answers + to the query handle.</p> <pre> 1> <input>QH = [1,2,3,4,5,6],</input> <input>qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH).</input> 21</pre> + <p><c>fold(<anno>Function</anno>, <anno>Acc0</anno>, + <anno>QH</anno>)</c> is equivalent to + <c>fold(<anno>Function</anno>, <anno>Acc0</anno>, + <anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>format_error(Error) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return an English description of a an error tuple.</fsummary> - <type> - <v>Error = {error, module(), term()}</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Returns a descriptive string in English of an error tuple returned by some of the functions of the <c>qlc</c> module @@ -830,25 +814,9 @@ ok</pre> </func> <func> - <name>info(QueryHandleOrList [, Options]) -> Info</name> + <name name="info" arity="1"/> + <name name="info" arity="2"/> <fsummary>Return code describing a query handle.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = EvalOption | ReturnOption</v> - <v>EvalOption = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>ReturnOption = {depth, Depth} - | {flat, bool()} - | {format, Format} - | {n_elements, NElements}</v> - <v>Depth = infinity | int() >= 0</v> - <v>Format = abstract_code | string</v> - <v>NElements = infinity | int() > 0</v> - <v>Info = AbstractExpression | string()</v> - </type> <desc> <p><marker id="info"></marker>Returns information about a query handle. The information describes the simplifications @@ -879,18 +847,18 @@ ok</pre> <input>io:format("~s~n", [qlc:info(QH, unique_all)]).</input> begin V1 = - qlc:q([ + qlc:q([ SQV || SQV <- [x,y] ], [{unique,true}]), V2 = - qlc:q([ + qlc:q([ SQV || SQV <- [a,b] ], [{unique,true}]), - qlc:q([ + qlc:q([ {X,Y} || X <- V1, Y <- V2 @@ -913,19 +881,19 @@ end</pre> <input>io:format("~s~n", [qlc:info(Q)]).</input> begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- ets:table(17) ]), V2 = - qlc:q([ + qlc:q([ [G1|G2] || G2 <- V1, G1 <- ets:table(16), element(2, G1) =:= element(1, G2) ], [{join,lookup}]), - qlc:q([ + qlc:q([ {X,Z,W} || [{X,Z}|{W,Y}] <- V2 ]) @@ -936,44 +904,43 @@ end</pre> method chosen. A convention is used for lookup join: the first generator (<c>G2</c>) is the one traversed, the second one (<c>G1</c>) is the table where constants are looked up.</p> + + <p><c>info(<anno>QH</anno>)</c> is equivalent to + <c>info(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>keysort(KeyPos, QH1 [, SortOptions]) -> QH2</name> + <name name="keysort" arity="2"/> + <name name="keysort" arity="3"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QueryHandleOrList</v> - <v>QH2 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH2</c> the answers to the query handle <c>QH1</c> are - sorted by <seealso + <c><anno>QH2</anno></c> the answers to the query handle + <c><anno>QH1</anno></c> are sorted by <seealso marker="file_sorter">file_sorter:keysort/4</seealso> according to the options.</p> - <p>The sorter will use temporary files only if <c>QH1</c> does - not evaluate to a list and the size of the binary - representation of the answers exceeds <c>Size</c> bytes, - where <c>Size</c> is the value of the <c>size</c> option.</p> + <p>The sorter will use temporary files only if + <c><anno>QH1</anno></c> does not evaluate to a list and the + size of the binary representation of the answers exceeds + <c>Size</c> bytes, where <c>Size</c> is the value of the + <c>size</c> option.</p> + + <p><c>keysort(<anno>KeyPos</anno>, <anno>QH1</anno>)</c> + is equivalent to + <c>keysort(<anno>KeyPos</anno>, <anno>QH1</anno>, [])</c>.</p> </desc> </func> <func> - <name>next_answers(QueryCursor [, NumberOfAnswers]) -> - Answers | Error</name> + <name name="next_answers" arity="1"/> + <name name="next_answers" arity="2"/> <fsummary>Return some or all answers to a query.</fsummary> - <type> - <v>NumberOfAnswers = all_remaining | int() > 0</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> <p>Returns some or all of the remaining answers to a query - cursor. Only the owner of <c>Cursor</c> can retrieve - answers.</p> - + cursor. Only the owner of <c><anno>QueryCursor</anno></c> can + retrieve answers.</p> <p>The optional argument <c>NumberOfAnswers</c>determines the maximum number of answers returned. The default value is <c>10</c>. If less than the requested number of answers is @@ -983,21 +950,9 @@ end</pre> </func> <func> - <name>q(QueryListComprehension [, Options]) -> QueryHandle</name> + <name name="q" arity="1"/> + <name name="q" arity="2"/> <fsummary>Return a handle for a query list comprehension.</fsummary> - <type> - <v>QueryListComprehension = - - literal query listcomprehension -</v> - <v>Options = [Option] | Option</v> - <v>Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique</v> - <v>MaxLookup = int() >= 0 | infinity</v> - <v>Join = any | lookup | merge | nested_loop</v> - <v>Lookup = bool() | any</v> - </type> <desc> <p><marker id="q"></marker>Returns a query handle for a query list comprehension. The query list comprehension must be the @@ -1024,7 +979,7 @@ end</pre> <pre> ... -A = [X || {X} <- [{1},{2}]], +A = [X || {X} <- [{1},{2}]], QH = qlc:q(A), ...</pre> @@ -1034,6 +989,9 @@ QH = qlc:q(A), list comprehension"); the shell process stops with a <c>badarg</c> reason.</p> + <p><c>q(<anno>QLC</anno>)</c> is equivalent to + <c>q(<anno>QLC</anno>, [])</c>.</p> + <p>The <c>{cache, ets}</c> option can be used to cache the answers to a query list comprehension. The answers are stored in one ETS table for each cached query list @@ -1092,26 +1050,26 @@ QH = qlc:q(A), <input>io:format("~s~n", [qlc:info(Q)]).</input> begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {X,Z} <- qlc:keysort(1, [{a,1},{b,4},{c,6}], []) ]), V2 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- qlc:keysort(2, [{2,a},{3,b},{4,c}], []) ]), V3 = - qlc:q([ + qlc:q([ [G1|G2] || G1 <- V1, G2 <- V2, element(1, G1) == element(2, G2) ], [{join,merge},{cache,list}]), - qlc:q([ + qlc:q([ {A,X,Z,W} || A <- [a,b,c], [{X,Z}|{W,Y}] <- V3, @@ -1170,7 +1128,7 @@ ets:match_spec_run( elements of the key {X, Y} are compared separately.</p> <p>The <c>{lookup, true}</c> option can be used to ensure - that the <c>qlc</c> module will look up constants in some + that the <c>qlc</c> module will look up constants in some QLC table. If there are more than one QLC table among the generators' list expressions, constants have to be looked up in at least one @@ -1190,7 +1148,7 @@ ets:match_spec_run( <c>{join, nested_loop}</c> invokes the method of matching every pair of objects from two handles. The last method is mostly very slow. The evaluation of the query - fails if the <c>qlc</c> module cannot carry out the chosen + fails if the <c>qlc</c> module cannot carry out the chosen join method. The default value is <c>any</c> which means that some fast join method will be used if possible.</p> @@ -1198,47 +1156,33 @@ ets:match_spec_run( </func> <func> - <name>sort(QH1 [, SortOptions]) -> QH2</name> + <name name="sort" arity="1"/> + <name name="sort" arity="2"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QueryHandleOrList</v> - <v>QH2 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH2</c> the answers to the query handle <c>QH1</c> are - sorted by <seealso + <c><anno>QH2</anno></c> the answers to the query handle + <c><anno>QH1</anno></c> are sorted by <seealso marker="file_sorter">file_sorter:sort/3</seealso> according to the options.</p> - <p>The sorter will use temporary files only if <c>QH1</c> does - not evaluate to a list and the size of the binary - representation of the answers exceeds <c>Size</c> bytes, - where <c>Size</c> is the value of the <c>size</c> option.</p> + <p>The sorter will use temporary files only if + <c><anno>QH1</anno></c> does not evaluate to a list and the + size of the binary representation of the answers exceeds + <c>Size</c> bytes, where <c>Size</c> is the value of the + <c>size</c> option.</p> + + <p><c>sort(<anno>QH1</anno>)</c> is equivalent to + <c>sort(<anno>QH1</anno>, [])</c>.</p> + </desc> </func> <func> - <name>string_to_handle(QueryString [, Options [, Bindings]]) -> - QueryHandle | Error</name> + <name name="string_to_handle" arity="1"/> + <name name="string_to_handle" arity="2"/> + <name name="string_to_handle" arity="3"/> <fsummary>Return a handle for a query list comprehension.</fsummary> - <type> - <v>QueryString = string()</v> - <v>Options = [Option] | Option</v> - <v>Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique</v> - <v>MaxLookup = int() >= 0 | infinity</v> - <v>Join = any | lookup | merge | nested_loop</v> - <v>Lookup = bool() | any</v> - <v>Bindings = - as returned by - erl_eval:bindings/1 -</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - ErrorInfo as returned by - erl_scan:string/1 or erl_parse:parse_exprs/1 -</v> - </type> <desc> <p>A string version of <c>qlc:q/1,2</c>. When the query handle is evaluated the fun created by the parse transform is @@ -1253,57 +1197,24 @@ ets:match_spec_run( <input>qlc:eval(QH).</input> [2,3,4]</pre> + <p><c>string_to_handle(<anno>QueryString</anno>)</c> + is equivalent to + <c>string_to_handle(<anno>QueryString</anno>, [])</c>.</p> + + <p><c>string_to_handle(<anno>QueryString</anno>, + <anno>Options</anno>)</c> + is equivalent to + <c>string_to_handle(<anno>QueryString</anno>, + <anno>Options</anno>, erl_eval:new_bindings())</c>.</p> + <p>This function is probably useful mostly when called from outside of Erlang, for instance from a driver written in C.</p> </desc> </func> <func> - <name>table(TraverseFun, Options) -> QueryHandle</name> + <name name="table" arity="2"/> <fsummary>Return a query handle for a table.</fsummary> - <type> - <v>TraverseFun = TraverseFun0 | TraverseFun1</v> - <v>TraverseFun0 = fun() -> TraverseResult</v> - <v>TraverseFun1 = fun(MatchExpression) -> TraverseResult</v> - <v>TraverseResult = Objects | term()</v> - <v>Objects = [] | [term() | ObjectList]</v> - <v>ObjectList = TraverseFun0 | Objects</v> - <v>Options = [Option] | Option</v> - <v>Option = {format_fun, FormatFun} - | {info_fun, InfoFun} - | {lookup_fun, LookupFun} - | {parent_fun, ParentFun} - | {post_fun, PostFun} - | {pre_fun, PreFun} - | {key_equality, KeyComparison}</v> - <v>FormatFun = undefined | fun(SelectedObjects) -> FormatedTable</v> - <v>SelectedObjects = all - | {all, NElements, DepthFun} - | {match_spec, MatchExpression} - | {lookup, Position, Keys} - | {lookup, Position, Keys, NElements, DepthFun}</v> - <v>NElements = infinity | int() > 0</v> - <v>DepthFun = fun(term()) -> term()</v> - <v>FormatedTable = {Mod, Fun, Args} - | AbstractExpression - | character_list()</v> - <v>InfoFun = undefined | fun(InfoTag) -> InfoValue</v> - <v>InfoTag = indices | is_unique_objects | keypos | num_of_objects</v> - <v>InfoValue = undefined | term()</v> - <v>LookupFun = undefined | fun(Position, Keys) -> LookupResult</v> - <v>LookupResult = [term()] | term()</v> - <v>ParentFun = undefined | fun() -> ParentFunValue</v> - <v>PostFun = undefined | fun() -> void()</v> - <v>PreFun = undefined | fun([PreArg]) -> void()</v> - <v>PreArg = {parent_value, ParentFunValue} | {stop_fun, StopFun}</v> - <v>ParentFunValue = undefined | term()</v> - <v>StopFun = undefined | fun() -> void()</v> - <v>KeyComparison = '=:=' | '=='</v> - <v>Position = int() > 0</v> - <v>Keys = [term()]</v> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p><marker id="table"></marker>Returns a query handle for a QLC table. In Erlang/OTP there is support for ETS, Dets and @@ -1315,77 +1226,90 @@ ets:match_spec_run( as well as properties of the table are handled by callback functions provided as options to <c>qlc:table/2</c>.</p> - <p>The callback function <c>TraverseFun</c> is used for - traversing the table. It is to return a list of objects - terminated by either <c>[]</c> or a nullary fun to be used - for traversing the not yet traversed objects of the table. - Any other return value is immediately returned as value of - the query evaluation. Unary <c>TraverseFun</c>s are to - accept a match specification as argument. The match - specification is created by the parse transform by analyzing - the pattern of the generator calling <c>qlc:table/2</c> and - filters using variables introduced in the pattern. If the - parse transform cannot find a match specification equivalent - to the pattern and filters, <c>TraverseFun</c> will be - called with a match specification returning every object. - Modules that can utilize match specifications for optimized + <p>The callback function <c><anno>TraverseFun</anno></c> is + used for traversing the table. It is to return a list of + objects terminated by either <c>[]</c> or a nullary fun to + be used for traversing the not yet traversed objects of the + table. Any other return value is immediately returned as + value of the query evaluation. Unary + <c><anno>TraverseFun</anno></c>s are to accept a match + specification as argument. The match specification is + created by the parse transform by analyzing the pattern of + the generator calling <c>qlc:table/2</c> and filters using + variables introduced in the pattern. If the parse transform + cannot find a match specification equivalent to the pattern + and filters, <c><anno>TraverseFun</anno></c> will be called + with a match specification returning every object. Modules + that can utilize match specifications for optimized traversal of tables should call <c>qlc:table/2</c> with a - unary <c>TraverseFun</c> while other modules can provide a - nullary <c>TraverseFun</c>. <c>ets:table/2</c> is an example - of the former; <c>gb_table:table/1</c> in the <seealso - marker="#implementing_a_qlc_table">Implementing a QLC - table</seealso> section is an example of the latter.</p> - - <p><c>PreFun</c> is a unary callback function that is called - once before the table is read for the first time. If the - call fails, the query evaluation fails. Similarly, the - nullary callback function <c>PostFun</c> is called once - after the table was last read. The return value, which is - caught, is ignored. If <c>PreFun</c> has been called for a - table, <c>PostFun</c> is guaranteed to be called for that - table, even if the evaluation of the query fails for some - reason. The order in which pre (post) functions for + unary + <c><anno>TraverseFun</anno></c> while other modules can + provide a nullary + <c><anno>TraverseFun</anno></c>. <c>ets:table/2</c> is an + example of the former; <c>gb_table:table/1</c> in the + <seealso marker="#implementing_a_qlc_table">Implementing a + QLC table</seealso> section is an example of the latter.</p> + + <p><c><anno>PreFun</anno></c> is a unary callback function + that is called once before the table is read for the first + time. If the call fails, the query evaluation fails. + Similarly, the nullary callback function + <c><anno>PostFun</anno></c> is called once after the table + was last read. The return value, which is caught, is + ignored. If <c><anno>PreFun</anno></c> has been called for a + table, + <c><anno>PostFun</anno></c> is guaranteed to be called for + that table, even if the evaluation of the query fails for + some reason. The order in which pre (post) functions for different tables are evaluated is not specified. Other table - access than reading, such as calling <c>InfoFun</c>, is - assumed to be OK at any time. The argument <c>PreArgs</c> is - a list of tagged values. Currently there are two tags, + access than reading, such as calling + <c><anno>InfoFun</anno></c>, is assumed to be OK at any + time. The argument <c><anno>PreArgs</anno></c> is a list of + tagged values. Currently there are two tags, <c>parent_value</c> and <c>stop_fun</c>, used by Mnesia for managing transactions. The value of <c>parent_value</c> is - the value returned by <c>ParentFun</c>, or <c>undefined</c> - if there is no <c>ParentFun</c>. <c>ParentFun</c> is called - once just before the call of <c>PreFun</c> in the context of - the process calling <c>eval</c>, <c>fold</c>, or + the value returned by <c><anno>ParentFun</anno></c>, or + <c>undefined</c> if there is no <c>ParentFun</c>. + <c><anno>ParentFun</anno></c> is called once just before the + call of + <c><anno>PreFun</anno></c> in the context of the process + calling + <c>eval</c>, <c>fold</c>, or <c>cursor</c>. The value of <c>stop_fun</c> is a nullary fun that deletes the cursor if called from the parent, or <c>undefined</c> if there is no cursor.</p> <p><marker id="lookup_fun"></marker>The binary callback - function <c>LookupFun</c> is used for looking up objects in - the table. The first argument <c>Position</c> is the key - position or an indexed position and the second argument - <c>Keys</c> is a sorted list of unique values. The return - value is to be a list of all objects (tuples) such that the - element at <c>Position</c> is a member of <c>Keys</c>. Any - other return value is immediately returned as value of the - query evaluation. <c>LookupFun</c> is called instead of + function <c><anno>LookupFun</anno></c> is used for looking + up objects in the table. The first argument + <c><anno>Position</anno></c> is the key position or an + indexed position and the second argument + <c><anno>Keys</anno></c> is a sorted list of unique values. + The return value is to be a list of all objects (tuples) + such that the element at <c>Position</c> is a member of + <c><anno>Keys</anno></c>. Any other return value is + immediately returned as value of the query evaluation. + <c><anno>LookupFun</anno></c> is called instead of traversing the table if the parse transform at compile time can find out that the filters match and compare the element - at <c>Position</c> in such a way that only <c>Keys</c> need - to be looked up in order to find all potential answers. The - key position is obtained by calling <c>InfoFun(keypos)</c> - and the indexed positions by calling - <c>InfoFun(indices)</c>. If the key position can be used for - lookup it is always chosen, otherwise the indexed position - requiring the least number of lookups is chosen. If there is - a tie between two indexed positions the one occurring first - in the list returned by <c>InfoFun</c> is chosen. Positions - requiring more than <seealso - marker="#max_lookup">max_lookup</seealso> lookups are - ignored.</p> - - <p>The unary callback function <c>InfoFun</c> is to return - information about the table. <c>undefined</c> should be - returned if the value of some tag is unknown:</p> + at <c><anno>Position</anno></c> in such a way that only + <c><anno>Keys</anno></c> need to be looked up in order to + find all potential answers. The key position is obtained by + calling + <c><anno>InfoFun</anno>(keypos)</c> and the indexed + positions by calling + <c><anno>InfoFun</anno>(indices)</c>. If the key position + can be used for lookup it is always chosen, otherwise the + indexed position requiring the least number of lookups is + chosen. If there is a tie between two indexed positions the + one occurring first in the list returned by + <c><anno>InfoFun</anno></c> is chosen. Positions requiring + more than <seealso marker="#max_lookup">max_lookup</seealso> + lookups are ignored.</p> + + <p>The unary callback function <c><anno>InfoFun</anno></c> is + to return information about the table. <c>undefined</c> + should be returned if the value of some tag is unknown:</p> <list type="bulleted"> <item><c>indices</c>. Returns a list of indexed @@ -1406,20 +1330,22 @@ ets:match_spec_run( </item> </list> - <p>The unary callback function <c>FormatFun</c> is used by - <seealso marker="#info">qlc:info/1,2</seealso> for - displaying the call that created the table's query handle. - The default value, <c>undefined</c>, means that + <p>The unary callback function <c><anno>FormatFun</anno></c> + is used by <seealso marker="#info">qlc:info/1,2</seealso> + for displaying the call that created the table's query + handle. The default value, <c>undefined</c>, means that <c>info/1,2</c> displays a call to <c>'$MOD':'$FUN'/0</c>. - It is up to <c>FormatFun</c> to present the selected objects - of the table in a suitable way. However, if a character list - is chosen for presentation it must be an Erlang expression - that can be scanned and parsed (a trailing dot will be added - by <c>qlc:info</c> though). <c>FormatFun</c> is called with - an argument that describes the selected objects based on - optimizations done as a result of analyzing the filters of - the QLC where the call to <c>qlc:table/2</c> occurs. The - possible values of the argument are:</p> + It is up to <c><anno>FormatFun</anno></c> to present the + selected objects of the table in a suitable way. However, if + a character list is chosen for presentation it must be an + Erlang expression that can be scanned and parsed (a trailing + dot will be added by <c>qlc:info</c> though). + <c><anno>FormatFun</anno></c> is called with an argument + that describes the selected objects based on optimizations + done as a result of analyzing the filters of the QLC where + the call to + <c>qlc:table/2</c> occurs. The possible values of the + argument are:</p> <list type="bulleted"> <item><c>{lookup, Position, Keys, NElements, DepthFun}</c>. @@ -1443,10 +1369,12 @@ ets:match_spec_run( can be used for limiting the size of terms; calling <c>DepthFun(Term)</c> substitutes <c>'...'</c> for parts of <c>Term</c> below the depth specified by the <c>info/1,2</c> - option <c>depth</c>. If calling <c>FormatFun</c> with an - argument including <c>NElements</c> and <c>DepthFun</c> - fails, <c>FormatFun</c> is called once again with an - argument excluding <c>NElements</c> and <c>DepthFun</c> + option <c>depth</c>. If calling + <c><anno>FormatFun</anno></c> with an argument including + <c>NElements</c> and <c>DepthFun</c> fails, + <c><anno>FormatFun</anno></c> is called once again with an + argument excluding + <c>NElements</c> and <c>DepthFun</c> (<c>{lookup, Position, Keys}</c> or <c>all</c>).</p> @@ -1458,7 +1386,7 @@ ets:match_spec_run( <p>See <seealso marker="ets#qlc_table">ets(3)</seealso>, <seealso marker="dets#qlc_table">dets(3)</seealso> and - <seealso marker="mnesia:mnesia#qlc_table">mnesia(3)</seealso> + <seealso marker="mnesia:mnesia#qlc_table">mnesia(3)</seealso> for the various options recognized by <c>table/1,2</c> in respective module.</p> </desc> @@ -1472,12 +1400,12 @@ ets:match_spec_run( <seealso marker="doc/reference_manual:users_guide"> Erlang Reference Manual</seealso>, <seealso marker="erl_eval">erl_eval(3)</seealso>, - <seealso marker="erts:erlang">erlang(3)</seealso>, + <seealso marker="erts:erlang">erlang(3)</seealso>, <seealso marker="ets">ets(3)</seealso>, - <seealso marker="kernel:file">file(3)</seealso>, - <seealso marker="error_logger:file">error_logger(3)</seealso>, + <seealso marker="kernel:file">file(3)</seealso>, + <seealso marker="error_logger:file">error_logger(3)</seealso>, <seealso marker="file_sorter">file_sorter(3)</seealso>, - <seealso marker="mnesia:mnesia">mnesia(3)</seealso>, + <seealso marker="mnesia:mnesia">mnesia(3)</seealso>, <seealso marker="doc/programming_examples:users_guide"> Programming Examples</seealso>, <seealso marker="shell">shell(3)</seealso></p> diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index 5ada1c2c57..383f52d10d 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -88,122 +88,94 @@ <title>Original API</title> </section> + <datatypes> + <datatype> + <name><marker id="type-queue">queue()</marker></name> + <desc><p>As returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>new() -> Q</name> + <name name="new" arity="0"/> <fsummary>Create an empty queue</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> <p>Returns an empty queue.</p> </desc> </func> <func> - <name>is_queue(Term) -> true | false</name> + <name name="is_queue" arity="1"/> <fsummary>Test if a term is a queue</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Tests if <c>Q</c> is a queue and returns <c>true</c> if so and + <p>Tests if <c><anno>Term</anno></c> is a queue and returns <c>true</c> if so and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty(Q) -> true | false</name> + <name name="is_empty" arity="1"/> <fsummary>Test if a queue is empty</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> - <p>Tests if <c>Q</c> is empty and returns <c>true</c> if so and + <p>Tests if <c><anno>Q</anno></c> is empty and returns <c>true</c> if so and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>len(Q) -> N</name> + <name name="len" arity="1"/> <fsummary>Get the length of a queue</fsummary> - <type> - <v>Q = queue()</v> - <v>N = integer()</v> - </type> <desc> - <p>Calculates and returns the length of queue <c>Q</c>.</p> + <p>Calculates and returns the length of queue <c><anno>Q</anno></c>.</p> </desc> </func> <func> - <name>in(Item, Q1) -> Q2</name> + <name name="in" arity="2"/> <fsummary>Insert an item at the rear of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the rear of queue <c>Q1</c>. - Returns the resulting queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the rear of queue <c><anno>Q1</anno></c>. + Returns the resulting queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>in_r(Item, Q1) -> Q2</name> + <name name="in_r" arity="2"/> <fsummary>Insert an item at the front of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the front of queue <c>Q1</c>. - Returns the resulting queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the front of queue <c><anno>Q1</anno></c>. + Returns the resulting queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>out(Q1) -> Result</name> + <name name="out" arity="1"/> <fsummary>Remove the front item from a queue</fsummary> - <type> - <v>Result = {{value, Item}, Q2} | {empty, Q1}</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Removes the item at the front of queue <c>Q1</c>. Returns the - tuple <c>{{value, Item}, Q2}</c>, where <c>Item</c> is the - item removed and <c>Q2</c> is the resulting queue. If <c>Q1</c> is - empty, the tuple <c>{empty, Q1}</c> is returned.</p> + <p>Removes the item at the front of queue <c><anno>Q1</anno></c>. Returns the + tuple <c>{{value, <anno>Item</anno>}, <anno>Q2</anno>}</c>, where <c><anno>Item</anno></c> is the + item removed and <c><anno>Q2</anno></c> is the resulting queue. If <c><anno>Q1</anno></c> is + empty, the tuple <c>{empty, <anno>Q1</anno>}</c> is returned.</p> </desc> </func> <func> - <name>out_r(Q1) -> Result</name> + <name name="out_r" arity="1"/> <fsummary>Remove the rear item from a queue</fsummary> - <type> - <v>Result = {{value, Item}, Q2} | {empty, Q1}</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Removes the item at the rear of the queue <c>Q1</c>. Returns the - tuple <c>{{value, Item}, Q2}</c>, where <c>Item</c> is the - item removed and <c>Q2</c> is the new queue. If <c>Q1</c> is - empty, the tuple <c>{empty, Q1}</c> is returned. </p> + <p>Removes the item at the rear of the queue <c><anno>Q1</anno></c>. Returns the + tuple <c>{{value, <anno>Item</anno>}, <anno>Q2</anno>}</c>, where <c><anno>Item</anno></c> is the + item removed and <c><anno>Q2</anno></c> is the new queue. If <c><anno>Q1</anno></c> is + empty, the tuple <c>{empty, <anno>Q1</anno>}</c> is returned. </p> </desc> </func> <func> - <name>from_list(L) -> queue()</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list to a queue</fsummary> - <type> - <v>L = list()</v> - </type> <desc> - <p>Returns a queue containing the items in <c>L</c> in the + <p>Returns a queue containing the items in <c><anno>L</anno></c> in the same order; the head item of the list will become the front item of the queue.</p> </desc> </func> <func> - <name>to_list(Q) -> list()</name> + <name name="to_list" arity="1"/> <fsummary>Convert a queue to a list</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> <p>Returns a list of the items in the queue in the same order; the front item of the queue will become the head of the list.</p> @@ -211,57 +183,43 @@ </func> <func> - <name>reverse(Q1) -> Q2</name> + <name name="reverse" arity="1"/> <fsummary>Reverse a queue</fsummary> - <type> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that contains the items of - <c>Q1</c> in the reverse order.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that contains the items of + <c><anno>Q1</anno></c> in the reverse order.</p> </desc> </func> <func> - <name>split(N, Q1) -> {Q2,Q3}</name> + <name name="split" arity="2"/> <fsummary>Split a queue in two</fsummary> - <type> - <v>N = integer()</v> - <v>Q1 = Q2 = Q3 = queue()</v> - </type> <desc> - <p>Splits <c>Q1</c> in two. The <c>N</c> front items - are put in <c>Q2</c> and the rest in <c>Q3</c></p> + <p>Splits <c><anno>Q1</anno></c> in two. The <c><anno>N</anno></c> front items + are put in <c><anno>Q2</anno></c> and the rest in <c><anno>Q3</anno></c></p> </desc> </func> <func> - <name>join(Q1, Q2) -> Q3</name> + <name name="join" arity="2"/> <fsummary>Join two queues</fsummary> - <type> - <v>Q1 = Q2 = Q3 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q3</c> that is the result of joining - <c>Q1</c> and <c>Q2</c> with <c>Q1</c> in front of - <c>Q2</c>.</p> + <p>Returns a queue <c><anno>Q3</anno></c> that is the result of joining + <c><anno>Q1</anno></c> and <c><anno>Q2</anno></c> with <c><anno>Q1</anno></c> in front of + <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>filter(Fun, Q1) -> Q2</name> + <name name="filter" arity="2"/> <fsummary>Filter a queue</fsummary> - <type> - <v>Fun = fun(Item) -> bool() | list()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of calling - <c>Fun(Item)</c> on all items in <c>Q1</c>, + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of calling + <c><anno>Fun</anno>(<anno>Item</anno>)</c> on all items in <c><anno>Q1</anno></c>, in order from front to rear.</p> - <p>If <c>Fun(Item)</c> returns <c>true</c>, <c>Item</c> + <p>If <c><anno>Fun</anno>(<anno>Item</anno>)</c> returns <c>true</c>, <c>Item</c> is copied to the result queue. If it returns <c>false</c>, - <c>Item</c> is not copied. If it returns a list + <c><anno>Item</anno></c> is not copied. If it returns a list the list elements are inserted instead of <c>Item</c> in the result queue.</p> - <p>So, <c>Fun(Item)</c> returning <c>[Item]</c> is thereby + <p>So, <c><anno>Fun</anno>(<anno>Item</anno>)</c> returning <c>[<anno>Item</anno>]</c> is thereby semantically equivalent to returning <c>true</c>, just as returning <c>[]</c> is semantically equivalent to returning <c>false</c>. But returning a list builds @@ -269,15 +227,11 @@ </desc> </func> <func> - <name>member(Item, Q) -> bool()</name> + <name name="member" arity="2"/> <fsummary>Test if an item is in a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Item</c> matches some element - in <c>Q</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Item</anno></c> matches some element + in <c><anno>Q</anno></c>, otherwise <c>false</c>.</p> </desc> </func> </funcs> @@ -290,77 +244,53 @@ <funcs> <func> - <name>get(Q) -> Item</name> + <name name="get" arity="1"/> <fsummary>Return the front item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> at the front of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> at the front of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>get_r(Q) -> Item</name> + <name name="get_r" arity="1"/> <fsummary>Return the rear item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> at the rear of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> at the rear of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>drop(Q1) -> Q2</name> + <name name="drop" arity="1"/> <fsummary>Remove the front item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the front item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the front item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>drop_r(Q1) -> Q2</name> + <name name="drop_r" arity="1"/> <fsummary>Remove the rear item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the rear item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the rear item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>peek(Q) -> {value,Item} | empty</name> + <name name="peek" arity="1"/> <fsummary>Return the front item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tuple <c>{value, Item}</c> where <c>Item</c> is the - front item of <c>Q</c>, or <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns the tuple <c>{value, <anno>Item</anno>}</c> where <c><anno>Item</anno></c> is the + front item of <c><anno>Q</anno></c>, or <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>peek_r(Q) -> {value,Item} | empty</name> + <name name="peek_r" arity="1"/> <fsummary>Return the rear item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tuple <c>{value, Item}</c> where <c>Item</c> is the - rear item of <c>Q</c>, or <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns the tuple <c>{value, <anno>Item</anno>}</c> where <c><anno>Item</anno></c> is the + rear item of <c><anno>Q</anno></c>, or <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> </funcs> @@ -372,80 +302,56 @@ <funcs> <func> - <name>cons(Item, Q1) -> Q2</name> + <name name="cons" arity="2"/> <fsummary>Insert an item at the head of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the head of queue <c>Q1</c>. Returns - the new queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the head of queue <c><anno>Q1</anno></c>. Returns + the new queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>head(Q) -> Item</name> + <name name="head" arity="1"/> <fsummary>Return the item at the head of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> from the head of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> from the head of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>tail(Q1) -> Q2</name> + <name name="tail" arity="1"/> <fsummary>Remove the head item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the head item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the head item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>snoc(Q1, Item) -> Q2</name> + <name name="snoc" arity="2"/> <fsummary>Insert an item at the tail of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> as the tail item of queue <c>Q1</c>. Returns - the new queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> as the tail item of queue <c><anno>Q1</anno></c>. Returns + the new queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>daeh(Q) -> Item</name> - <name>last(Q) -> Item</name> + <name name="daeh" arity="1"/> + <name name="last" arity="1"/> <fsummary>Return the tail item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tail item of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns the tail item of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>liat(Q1) -> Q2</name> - <name>init(Q1) -> Q2</name> - <name>lait(Q1) -> Q2</name> + <name name="liat" arity="1"/> + <name name="init" arity="1"/> + <name name="lait" arity="1"/> <fsummary>Remove the tail item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the tail item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the tail item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> <p>The name <c>lait/1</c> is a misspelling - do not use it anymore.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml index dcc6d756e1..93affc3191 100644 --- a/lib/stdlib/doc/src/random.xml +++ b/lib/stdlib/doc/src/random.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -49,9 +49,15 @@ strong. If a strong cryptographic random number generator is needed for example <c>crypto:rand_bytes/1</c> could be used instead.</p> </description> + <datatypes> + <datatype> + <name name="ran"/> + <desc><p>The state.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>seed() -> ran()</name> + <name name="seed" arity="0"/> <fsummary>Seeds random number generation with default values</fsummary> <desc> <p>Seeds random number generation with default (fixed) values @@ -59,11 +65,8 @@ </desc> </func> <func> - <name>seed(A1, A2, A3) -> undefined | ran()</name> + <name name="seed" arity="3"/> <fsummary>Seeds random number generator</fsummary> - <type> - <v>A1 = A2 = A3 = integer()</v> - </type> <desc> <p>Seeds random number generation with integer values in the process dictionary, and returns the old state.</p> @@ -76,26 +79,23 @@ </desc> </func> <func> - <name>seed({A1, A2, A3}) -> undefined | ran()</name> + <name name="seed" arity="1"/> <fsummary>Seeds random number generator</fsummary> - <type> - <v>A1 = A2 = A3 = integer()</v> - </type> <desc> <p> - <c>seed({A1, A2, A3})</c> is equivalent to <c>seed(A1, A2, A3)</c>. + <c>seed({<anno>A1</anno>, <anno>A2</anno>, <anno>A3</anno>})</c> is equivalent to <c>seed(<anno>A1</anno>, <anno>A2</anno>, <anno>A3</anno>)</c>. </p> </desc> </func> <func> - <name>seed0() -> ran()</name> + <name name="seed0" arity="0"/> <fsummary>Return default state for random number generation</fsummary> <desc> <p>Returns the default state.</p> </desc> </func> <func> - <name>uniform()-> float()</name> + <name name="uniform" arity="0"/> <fsummary>Return a random float</fsummary> <desc> <p>Returns a random float uniformly distributed between <c>0.0</c> @@ -103,39 +103,29 @@ </desc> </func> <func> - <name>uniform(N) -> integer()</name> + <name name="uniform" arity="1"/> <fsummary>Return a random integer</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> - <p>Given an integer <c>N >= 1</c>, <c>uniform/1</c> returns a + <p>Given an integer <c><anno>N</anno> >= 1</c>, <c>uniform/1</c> returns a random integer uniformly distributed between <c>1</c> and - <c>N</c>, updating the state in the process dictionary.</p> + <c><anno>N</anno></c>, updating the state in the process dictionary.</p> </desc> </func> <func> - <name>uniform_s(State0) -> {float(), State1}</name> + <name name="uniform_s" arity="1"/> <fsummary>Return a random float</fsummary> - <type> - <v>State0 = State1 = ran()</v> - </type> <desc> <p>Given a state, <c>uniform_s/1</c>returns a random float uniformly distributed between <c>0.0</c> and <c>1.0</c>, and a new state.</p> </desc> </func> <func> - <name>uniform_s(N, State0) -> {integer(), State1}</name> + <name name="uniform_s" arity="2"/> <fsummary>Return a random integer</fsummary> - <type> - <v>N = integer()</v> - <v>State0 = State1 = ran()</v> - </type> <desc> - <p>Given an integer <c>N >= 1</c> and a state, <c>uniform_s/2</c> + <p>Given an integer <c><anno>N</anno> >= 1</c> and a state, <c>uniform_s/2</c> returns a random integer uniformly distributed between <c>1</c> and - <c>N</c>, and a new state.</p> + <c><anno>N</anno></c>, and a new state.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 9091035392..b8db55fc26 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -59,28 +59,24 @@ </description> - <section> - <title>DATA TYPES</title> - <code type="none"> - iodata() = iolist() | binary() - iolist() = [char() | binary() | iolist()] - - a binary is allowed as the tail of the list</code> - <code type="none"> - unicode_binary() = binary() with characters encoded in UTF-8 coding standard - unicode_char() = integer() representing a valid unicode codepoint - - chardata() = charlist() | unicode_binary() - - charlist() = [unicode_char() | unicode_binary() | charlist()] - - a unicode_binary is allowed as the tail of the list</code> - - <code type="none"> - mp() = Opaque datatype containing a compiled regular expression. - - The mp() is guaranteed to be a tuple() having the atom + <datatypes> + <datatype> + <name name="mp"/> + <desc> + <p>Opaque datatype containing a compiled regular expression. + The mp() is guaranteed to be a tuple() having the atom 're_pattern' as its first element, to allow for matching in guards. The arity of the tuple() or the content of the other fields - may change in future releases.</code> - </section> + may change in future releases.</p> + </desc> + </datatype> + <datatype> + <name name="nl_spec"/> + </datatype> + <datatype> + <name name="compile_option"/> + </datatype> + </datatypes> <funcs> <func> <name>compile(Regexp) -> {ok, MP} | {error, ErrSpec}</name> @@ -96,14 +92,14 @@ <name>compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec}</name> <fsummary>Compile a regular expression into a match program</fsummary> <type> - <v>Regexp = iodata() | charlist()</v> + <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> <v>Options = [ Option ]</v> <v>Option = unicode | anchored | caseless | dollar_endonly | dotall | extended | firstline | multiline | no_auto_capture | dupnames | ungreedy | {newline, NLSpec}| bsr_anycrlf | bsr_unicode</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - <v>MP = mp()</v> + <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v> + <v>MP = <seealso marker="#type-mp">mp()</seealso></v> <v>ErrSpec = {ErrString, Position}</v> <v>ErrString = string()</v> - <v>Position = int()</v> + <v>Position = non_neg_integer()</v> </type> <desc> <p>This function compiles a regular expression with the syntax @@ -116,7 +112,7 @@ time one wants to match.</p> <p>When the unicode option is given, the regular expression should be given as a valid unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p> - <p>The options have the following meanings:</p> + <p><marker id="compile_options"/>The options have the following meanings:</p> <taglist> <tag><c>unicode</c></tag> <item>The regular expression is given as a unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid unicode <c>charlist()</c> subject.</item> @@ -173,10 +169,10 @@ This option makes it possible to include comments inside complicated patterns. N <name>run(Subject,RE) -> {match, Captured} | nomatch</name> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> + <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> + <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> <v>Captured = [ CaptureData ]</v> - <v>CaptureData = {int(),int()}</v> + <v>CaptureData = {integer(),integer()}</v> </type> <desc> <p>The same as <c>run(Subject,RE,[])</c>.</p> @@ -186,18 +182,19 @@ This option makes it possible to include comments inside complicated patterns. N <name>run(Subject,RE,Options) -> {match, Captured} | match | nomatch</name> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> + <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> + <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</v> + <v>Option = anchored | global | notbol | noteol | notempty | {offset, integer() >= 0} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</v> <v>Type = index | list | binary</v> <v>ValueSpec = all | all_but_first | first | none | ValueList</v> <v>ValueList = [ ValueID ]</v> - <v>ValueID = int() | string() | atom()</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any</v> + <v>ValueID = integer() | string() | atom()</v> + <v>CompileOpt = <seealso marker="#type-compile_option">compile_option()</seealso></v> + <d>See <seealso marker="#compile_options">compile/2</seealso> above.</d> + <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v> <v>Captured = [ CaptureData ] | [ [ CaptureData ] ... ]</v> - <v>CaptureData = {int(),int()} | ListConversionData | binary()</v> + <v>CaptureData = {integer(),integer()} | ListConversionData | binary()</v> <v>ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()}</v> </type> <desc> @@ -217,7 +214,7 @@ This option makes it possible to include comments inside complicated patterns. N <p>If the regular expression is previously compiled, the option list can only contain the options <c>anchored</c>, <c>global</c>, <c>notbol</c>, <c>noteol</c>, - <c>notempty</c>, <c>{offset, int()}</c>, <c>{newline, + <c>notempty</c>, <c>{offset, integer() >= 0}</c>, <c>{newline, NLSpec}</c> and <c>{capture, ValueSpec}/{capture, ValueSpec, Type}</c>. Otherwise all options valid for the <c>re:compile/2</c> function are allowed as well. Options @@ -360,7 +357,7 @@ This option makes it possible to include comments inside complicated patterns. N behavior of the dollar metacharacter. It does not affect \Z or \z.</item> - <tag><c>{offset, int()}</c></tag> + <tag><c>{offset, integer() >= 0}</c></tag> <item>Start matching at the offset (position) given in the subject string. The offset is zero-based, so that the default is @@ -503,42 +500,27 @@ This option makes it possible to include comments inside complicated patterns. N </desc> </func> <func> - <name>replace(Subject,RE,Replacement) -> iodata() | charlist()</name> + <name name="replace" arity="3"/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata()</v> - <v>Replacement = iodata() | charlist()</v> - </type> <desc> - <p>The same as <c>replace(Subject,RE,Replacement,[])</c>.</p> + <p>The same as <c>replace(<anno>Subject</anno>,<anno>RE</anno>,<anno>Replacement</anno>,[])</c>.</p> </desc> </func> <func> - <name>replace(Subject,RE,Replacement,Options) -> iodata() | charlist() | binary() | list()</name> + <name name="replace" arity="4"/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> - <v>Replacement = iodata() | charlist()</v> - <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | CompileOpt</v> - <v>ReturnType = iodata | list | binary</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - </type> <desc> - <p>Replaces the matched part of the <c>Subject</c> string with the contents of <c>Replacement</c>.</p> + <p>Replaces the matched part of the <c><anno>Subject</anno></c> string with the contents of <c><anno>Replacement</anno></c>.</p> <p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed. - Instead a <c>{return, ReturnType}</c> is present. The default return type is <c>iodata</c>, constructed in a + Instead a <c>{return, <anno>ReturnType</anno>}</c> is present. The default return type is <c>iodata</c>, constructed in a way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p> <p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled - with the <c>unicode</c> option requires the <c>Subject</c> to be + with the <c>unicode</c> option requires the <c><anno>Subject</anno></c> to be a Unicode <c>charlist()</c>. If compilation is done implicitly and the <c>unicode</c> compilation option is given to this - function, both the regular expression and the <c>Subject</c> + function, both the regular expression and the <c><anno>Subject</anno></c> should be given as valid Unicode <c>charlist()</c>s.</p> <p>The replacement string can contain the special character @@ -565,34 +547,17 @@ This option makes it possible to include comments inside complicated patterns. N </desc> </func> <func> - <name>split(Subject,RE) -> SplitList</name> + <name name="split" arity="2"/> <fsummary>Split a string by tokens specified as a regular expression</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata()</v> - <v>SplitList = [ iodata() | charlist() ]</v> - </type> <desc> - <p>The same as <c>split(Subject,RE,[])</c>.</p> + <p>The same as <c>split(<anno>Subject</anno>,<anno>RE</anno>,[])</c>.</p> </desc> </func> <func> - <name>split(Subject,RE,Options) -> SplitList</name> + <name name="split" arity="3"/> <fsummary>Split a string by tokens specified as a regular expression</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> - <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | {parts, NumParts} | group | trim | CompileOpt</v> - <v>NumParts = int() | infinity</v> - <v>ReturnType = iodata | list | binary</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - <v>SplitList = [ RetData ] | [ GroupedRetData ]</v> - <v>GroupedRetData = [ RetData ]</v> - <v>RetData = iodata() | charlist() | binary() | list()</v> - </type> + <type_desc variable="CompileOpt">See <seealso marker="#compile_options">compile/2</seealso> above.</type_desc> <desc> <p>This function splits the input into parts by finding tokens according to the regular expression supplied.</p> @@ -602,10 +567,10 @@ This option makes it possible to include comments inside complicated patterns. N of the string is removed from the output.</p> <p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled - with the <c>unicode</c> option requires the <c>Subject</c> to be + with the <c>unicode</c> option requires the <c><anno>Subject</anno></c> to be a Unicode <c>charlist()</c>. If compilation is done implicitly and the <c>unicode</c> compilation option is given to this - function, both the regular expression and the <c>Subject</c> + function, both the regular expression and the <c><anno>Subject</anno></c> should be given as valid Unicode <c>charlist()</c>s.</p> <p>The result is given as a list of "strings", the @@ -722,7 +687,7 @@ This option makes it possible to include comments inside complicated patterns. N <p>Summary of options not previously described for the <c>re:run/3</c> function:</p> <taglist> - <tag>{return,ReturnType}</tag> + <tag>{return,<anno>ReturnType</anno>}</tag> <item><p>Specifies how the parts of the original string are presented in the result list. The possible types are:</p> <taglist> <tag>iodata</tag> diff --git a/lib/stdlib/doc/src/regexp.xml b/lib/stdlib/doc/src/regexp.xml index 8c4191c88f..35d8e1c3f8 100644 --- a/lib/stdlib/doc/src/regexp.xml +++ b/lib/stdlib/doc/src/regexp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -40,176 +40,150 @@ <p>This module contains functions for regular expression matching and substitution.</p> </description> + <datatypes> + <datatype> + <name name="errordesc"></name> + </datatype> + <datatype> + <name name="regexp"></name> + <desc><p>Internal representation of a regular expression.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>match(String, RegExp) -> MatchRes</name> + <name name="match" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match,Start,Length} | nomatch | {error,errordesc()}</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Finds the first, longest match of the regular expression <c>RegExp</c> in <c>String</c>. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:</p> + <p>Finds the first, longest match of the regular expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:</p> <taglist> - <tag><c>{match,Start,Length}</c></tag> + <tag><c>{match,<anno>Start</anno>,<anno>Length</anno>}</c></tag> <item> - <p>if the match succeeded. <c>Start</c> is the starting - position of the match, and <c>Length</c> is the length of + <p>if the match succeeded. <c><anno>Start</anno></c> is the starting + position of the match, and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> <tag><c>nomatch</c></tag> <item> <p>if there were no matching characters.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>first_match(String, RegExp) -> MatchRes</name> + <name name="first_match" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match,Start,Length} | nomatch | {error,errordesc()}</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Finds the first match of the regular expression <c>RegExp</c> in <c>String</c>. This call is + <p>Finds the first match of the regular expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. This call is usually faster than <c>match</c> and it is also a useful way to ascertain that a match exists. It returns as follows:</p> <taglist> - <tag><c>{match,Start,Length}</c></tag> + <tag><c>{match,<anno>Start</anno>,<anno>Length</anno>}</c></tag> <item> - <p>if the match succeeded. <c>Start</c> is the starting - position of the match and <c>Length</c> is the length of + <p>if the match succeeded. <c><anno>Start</anno></c> is the starting + position of the match and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> <tag><c>nomatch</c></tag> <item> <p>if there were no matching characters.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>matches(String, RegExp) -> MatchRes</name> + <name name="matches" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match, Matches} | {error, errordesc()}</v> - <v>Matches = list()</v> - </type> <desc> <p>Finds all non-overlapping matches of the - expression <c>RegExp</c> in <c>String</c>. + expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. It returns as follows:</p> <taglist> - <tag><c>{match, Matches}</c></tag> + <tag><c>{match, <anno>Matches</anno>}</c></tag> <item> <p>if the regular expression was correct. - The list will be empty if there was no match. Each element in the list looks like <c>{Start, Length}</c>, where <c>Start</c> is the starting position of the match, and <c>Length</c> is the length of the matching string.</p> + The list will be empty if there was no match. Each element in the list looks like <c>{<anno>Start</anno>, <anno>Length</anno>}</c>, where <c><anno>Start</anno></c> is the starting position of the match, and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>sub(String, RegExp, New) -> SubRes</name> + <name name="sub" arity="3"/> <fsummary>Substitute the first occurrence of a regular expression</fsummary> - <type> - <v>String = RegExp = New = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> - <p>Substitutes the first occurrence of a substring matching <c>RegExp</c> in <c>String</c> with the string <c>New</c>. A <c><![CDATA[&]]></c> in the string <c>New</c> is replaced by the matched substring of <c>String</c>. <c><![CDATA[\&]]></c> puts a literal <c><![CDATA[&]]></c> into the replacement string. It returns as follows:</p> + <p>Substitutes the first occurrence of a substring matching <c><anno>RegExp</anno></c> in <c><anno>String</anno></c> with the string <c><anno>New</anno></c>. A <c><![CDATA[&]]></c> in the string <c><anno>New</anno></c> is replaced by the matched substring of <c><anno>String</anno></c>. <c><![CDATA[\&]]></c> puts a literal <c><![CDATA[&]]></c> into the replacement string. It returns as follows:</p> <taglist> - <tag><c>{ok,NewString,RepCount}</c></tag> + <tag><c>{ok,<anno>NewString</anno>,<anno>RepCount</anno>}</c></tag> <item> - <p>if <c>RegExp</c> is correct. <c>RepCount</c> is the number of replacements which have been made + <p>if <c><anno>RegExp</anno></c> is correct. <c><anno>RepCount</anno></c> is the number of replacements which have been made (this will be either 0 or 1).</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>gsub(String, RegExp, New) -> SubRes</name> + <name name="gsub" arity="3"/> <fsummary>Substitute all occurrences of a regular expression</fsummary> - <type> - <v>String = RegExp = New = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> <p>The same as <c>sub</c>, except that all non-overlapping occurrences of a substring matching - <c>RegExp</c> in <c>String</c> are replaced by the string <c>New</c>. It returns:</p> + <c><anno>RegExp</anno></c> in <c><anno>String</anno></c> are replaced by the string <c><anno>New</anno></c>. It returns:</p> <taglist> - <tag><c>{ok,NewString,RepCount}</c></tag> + <tag><c>{ok,<anno>NewString</anno>,<anno>RepCount</anno>}</c></tag> <item> - <p>if <c>RegExp</c> is correct. <c>RepCount</c> is the number of replacements which have been made.</p> + <p>if <c><anno>RegExp</anno></c> is correct. <c><anno>RepCount</anno></c> is the number of replacements which have been made.</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>split(String, RegExp) -> SplitRes</name> + <name name="split" arity="2"/> <fsummary>Split a string into fields</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>SubRes = {ok,FieldList} | {error,errordesc()}</v> - <v>Fieldlist = [string()]</v> - </type> <desc> - <p><c>String</c> is split into fields (sub-strings) by the - regular expression <c>RegExp</c>.</p> + <p><c><anno>String</anno></c> is split into fields (sub-strings) by the + regular expression <c><anno>RegExp</anno></c>.</p> <p>If the separator expression is <c>" "</c> (a single space), then the fields are separated by blanks and/or tabs and leading and trailing blanks and tabs are discarded. For all other values of the separator, leading and trailing blanks and tabs are not discarded. It returns:</p> <taglist> - <tag><c>{ok, FieldList}</c></tag> + <tag><c>{ok, <anno>FieldList</anno>}</c></tag> <item> <p>to indicate that the string has been split up into the fields of - <c>FieldList</c>.</p> + <c><anno>FieldList</anno></c>.</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>sh_to_awk(ShRegExp) -> AwkRegExp</name> + <name name="sh_to_awk" arity="1"/> <fsummary>Convert an <c>sh</c>regular expression into an <c>AWK</c>one</fsummary> - <type> - <v>ShRegExp AwkRegExp = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> <p>Converts the <c>sh</c> type regular expression - <c>ShRegExp</c> into a full <c>AWK</c> regular + <c><anno>ShRegExp</anno></c> into a full <c>AWK</c> regular expression. Returns the converted regular expression string. <c>sh</c> expressions are used in the shell for matching file names and have the following special @@ -236,40 +210,32 @@ </desc> </func> <func> - <name>parse(RegExp) -> ParseRes</name> + <name name="parse" arity="1"/> <fsummary>Parse a regular expression</fsummary> - <type> - <v>RegExp = string()</v> - <v>ParseRes = {ok,RE} | {error,errordesc()}</v> - </type> <desc> - <p>Parses the regular expression <c>RegExp</c> and builds the + <p>Parses the regular expression <c><anno>RegExp</anno></c> and builds the internal representation used in the other regular expression functions. Such representations can be used in all of the other functions instead of a regular expression string. This is more efficient when the same regular expression is used in many strings. It returns:</p> <taglist> - <tag><c>{ok, RE}</c>if <c>RegExp</c>is correct and <c>RE</c>is the internal representation.</tag> + <tag><c>{ok, <anno>RE</anno>}</c></tag> <item> - <p></p> + <p>if <c>RegExp</c> is correct and <c><anno>RE</anno></c> is the internal representation.</p> </item> - <tag><c>{error, Error}</c>if there is an error in <c>RegExpString</c>.</tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p></p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> - <p>Returns a string which describes the error <c>ErrorDescriptor</c> + <p>Returns a string which describes the error <c><anno>ErrorDescriptor</anno></c> returned when there is an error in a regular expression.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml index 3610bb0184..071ee437cb 100644 --- a/lib/stdlib/doc/src/sets.xml +++ b/lib/stdlib/doc/src/sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,202 +43,141 @@ different if and only if they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -set() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-dict">set()</marker></name> + <desc><p>As returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>new() -> Set</name> + <name name="new" arity="0"/> <fsummary>Return an empty set</fsummary> - <type> - <v>Set = set()</v> - </type> <desc> <p>Returns a new empty set.</p> </desc> </func> <func> - <name>is_set(Set) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for an <c>Set</c></fsummary> - <type> - <v>Set = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> is a set of + <p>Returns <c>true</c> if <c><anno>Set</anno></c> is a set of elements, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>size(Set) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a set</fsummary> - <type> - <v>Set = term()</v> - </type> <desc> - <p>Returns the number of elements in <c>Set</c>.</p> + <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> </desc> </func> <func> - <name>to_list(Set) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert an <c>Set</c>into a list</fsummary> - <type> - <v>Set = set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Set</c> as a list.</p> + <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> </desc> </func> <func> - <name>from_list(List) -> Set</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into an <c>Set</c></fsummary> - <type> - <v>List = [term()]</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns an set of the elements in <c>List</c>.</p> + <p>Returns an set of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>is_element(Element, Set) -> bool()</name> + <name name="is_element" arity="2"/> <fsummary>Test for membership of an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Set</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Set</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>add_element(Element, Set1) -> Set2</name> + <name name="add_element" arity="2"/> <fsummary>Add an element to an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns a new set formed from <c>Set1</c> with - <c>Element</c> inserted.</p> + <p>Returns a new set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted.</p> </desc> </func> <func> - <name>del_element(Element, Set1) -> Set2</name> + <name name="del_element" arity="2"/> <fsummary>Remove an element from an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>Set1</c>, but with <c>Element</c> removed.</p> + <p>Returns <c><anno>Set1</anno></c>, but with <c><anno>Element</anno></c> removed.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns the merged (union) set of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>union(SetList) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of <c>Sets</c></fsummary> - <type> - <v>SetList = [set()]</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the merged (union) set of the list of sets.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns the intersection of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the intersection of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>intersection(SetList) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of <c>Sets</c></fsummary> - <type> - <v>SetList = [set()]</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of sets.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two <c>Sets</c> are disjoint</fsummary> - <type> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set1</c> and - <c>Set2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>subtract(Set1, Set2) -> Set3</name> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns only the elements of <c>Set1</c> which are not - also elements of <c>Set2</c>.</p> + <p>Returns only the elements of <c><anno>Set1</anno></c> which are not + also elements of <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Set</c>1 is - also a member of <c>Set2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c>1 is + also a member of <c><anno>Set2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Set) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Set = set()</v> - </type> <desc> - <p>Fold <c>Function</c> over every element in <c>Set</c> + <p>Fold <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>filter(Pred, Set1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Filter elements in <c>Set1</c> with boolean function - <c>Fun</c>.</p> + <p>Filter elements in <c><anno>Set1</anno></c> with boolean function + <c><anno>Pred</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index 73cc1b33bd..bc2120c37d 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -763,26 +763,20 @@ loop(N) -> <funcs> <func> - <name>history(N) -> integer()</name> + <name name="history" arity="1"/> <fsummary>Sets the number of previous commands to keep</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> <p>Sets the number of previous commands to keep in the - history list to <c>N</c>. The previous number is returned. + history list to <c><anno>N</anno></c>. The previous number is returned. The default number is 20.</p> </desc> </func> <func> - <name>results(N) -> integer()</name> + <name name="results" arity="1"/> <fsummary>Sets the number of previous results to keep</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> <p>Sets the number of results from previous commands to keep in - the history list to <c>N</c>. The previous number is returned. + the history list to <c><anno>N</anno></c>. The previous number is returned. The default number is 20.</p> </desc> </func> @@ -790,7 +784,7 @@ loop(N) -> <name>catch_exception(Bool) -> Bool</name> <fsummary>Sets the exception handling of the shell</fsummary> <type> - <v>Bool = bool()</v> + <v>Bool = boolean()</v> </type> <desc> <p>Sets the exception handling of the evaluator process. The @@ -804,38 +798,29 @@ loop(N) -> </desc> </func> <func> - <name>prompt_func(PromptFunc) -> prompt_func()</name> + <name name="prompt_func" arity="1"/> <fsummary>Sets the shell prompt</fsummary> - <type> - <v>PromptFunc = prompt_func()</v> - <v>prompt_func() = default | {Mod, Func}</v> - <v>Mod = Func = atom()</v> - </type> <desc> <p>Sets the shell prompt function to <c>PromptFunc</c>. The previous prompt function is returned.</p> </desc> </func> <func> - <name>start_restricted(Module) -> ok | {error, Reason}</name> + <name name="start_restricted" arity="1"/> <fsummary>Exits a normal shell and starts a restricted shell.</fsummary> - <type> - <v>Module = atom()</v> - <v>Reason = atom()</v> - </type> <desc> <p>Exits a normal shell and starts a restricted - shell. <c>Module</c> specifies the callback module for the + shell. <c><anno>Module</anno></c> specifies the callback module for the functions <c>local_allowed/3</c> and <c>non_local_allowed/3</c>. The function is meant to be called from the shell.</p> <p>If the callback module cannot be loaded, an error tuple is - returned. The <c>Reason</c> in the error tuple is the one + returned. The <c><anno>Reason</anno></c> in the error tuple is the one returned by the code loader when trying to load the code of the callback module.</p> </desc> </func> <func> - <name>stop_restricted() -> ok</name> + <name name="stop_restricted" arity="0"/> <fsummary>Exits a restricted shell and starts a normal shell.</fsummary> <desc> <p>Exits a restricted shell and starts a normal shell. The function diff --git a/lib/stdlib/doc/src/slave.xml b/lib/stdlib/doc/src/slave.xml index 168d83f301..15b6711731 100644 --- a/lib/stdlib/doc/src/slave.xml +++ b/lib/stdlib/doc/src/slave.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -56,22 +56,16 @@ </description> <funcs> <func> - <name>start(Host) -></name> - <name>start(Host, Name) -></name> - <name>start(Host, Name, Args) -> {ok, Node} | {error, Reason}</name> + <name name="start" arity="1"/> + <name name="start" arity="2"/> + <name name="start" arity="3"/> <fsummary>Start a slave node on a host</fsummary> - <type> - <v>Host = Name = atom()</v> - <v>Args = string()</v> - <v>Node = node()</v> - <v>Reason = timeout | no_rsh | {already_running, Node}</v> - </type> <desc> - <p>Starts a slave node on the host <c>Host</c>. Host names need + <p>Starts a slave node on the host <c><anno>Host</anno></c>. Host names need not necessarily be specified as fully qualified names; short names can also be used. This is the same condition that applies to names of distributed Erlang nodes.</p> - <p>The name of the started node will be <c>Name@Host</c>. If no + <p>The name of the started node will be <c><anno>Name</anno>@<anno>Host</anno></c>. If no name is provided, the name will be the same as the node which executes the call (with the exception of the host name part of the node name).</p> @@ -79,7 +73,7 @@ terminal I/O which is produced at the slave is automatically relayed to the master. Also, the file process will be relayed to the master.</p> - <p>The <c>Args</c> argument is used to set <c>erl</c> command + <p>The <c><anno>Args</anno></c> argument is used to set <c>erl</c> command line arguments. If provided, it is passed to the new node and can be used for a variety of purposes. See <seealso marker="erts:erl#erl">erl(1)</seealso></p> @@ -103,9 +97,9 @@ E = " -env DISPLAY " ++ net_adm:localhost() ++ ":0 ", Arg = "-mnesia_dir " ++ M ++ " -pa " ++ Dir ++ E, slave:start(H, Name, Arg).</code> - <p>If successful, the function returns <c>{ok, Node}</c>, - where <c>Node</c> is the name of the new node. Otherwise it - returns <c>{error, Reason}</c>, where <c>Reason</c> can be + <p>If successful, the function returns <c>{ok, <anno>Node</anno>}</c>, + where <c><anno>Node</anno></c> is the name of the new node. Otherwise it + returns <c>{error, <anno>Reason</anno>}</c>, where <c><anno>Reason</anno></c> can be one of:</p> <taglist> <tag><c>timeout</c></tag> @@ -123,24 +117,18 @@ slave:start(H, Name, Arg).</code> <item> <p>There is no <c>rsh</c> program on the computer.</p> </item> - <tag><c>{already_running, Node}</c></tag> + <tag><c>{already_running, <anno>Node</anno>}</c></tag> <item> - <p>A node with the name <c>Name@Host</c> already exists.</p> + <p>A node with the name <c><anno>Name</anno>@<anno>Host</anno></c> already exists.</p> </item> </taglist> </desc> </func> <func> - <name>start_link(Host) -></name> - <name>start_link(Host, Name) -></name> - <name>start_link(Host, Name, Args) -> {ok, Node} | {error, Reason}</name> + <name name="start_link" arity="1"/> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Start and link to a slave node on a host</fsummary> - <type> - <v>Host = Name = atom()</v> - <v>Args = string()</v> - <v>Node = node()</v> - <v>Reason = timeout | no_rsh | {already_running, Node}</v> - </type> <desc> <p>Starts a slave node in the same way as <c>start/1,2,3</c>, except that the slave node is linked to the currently @@ -151,11 +139,8 @@ slave:start(H, Name, Arg).</code> </desc> </func> <func> - <name>stop(Node) -> ok</name> + <name name="stop" arity="1"/> <fsummary>Stop (kill) a node</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> <p>Stops (kills) a node.</p> </desc> @@ -177,12 +162,8 @@ slave:start(H, Name, Arg).</code> </desc> </func> <func> - <name>pseudo(Master, ServerList) -> ok</name> + <name name="pseudo" arity="2"/> <fsummary>Start a number of pseudo servers</fsummary> - <type> - <v>Master = node()</v> - <v>ServerList = [atom()]</v> - </type> <desc> <p>Starts a number of pseudo servers. A pseudo server is a server with a registered name which does absolutely nothing @@ -198,16 +179,13 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]).</code> </desc> </func> <func> - <name>relay(Pid)</name> + <name name="relay" arity="1"/> <fsummary>Run a pseudo server</fsummary> - <type> - <v>Pid = pid()</v> - </type> <desc> <p>Runs a pseudo server. This function never returns any value and the process which executes the function will receive messages. All messages received will simply be passed on to - <c>Pid</c>.</p> + <c><anno>Pid</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml index 729df1e678..2e7768a1df 100644 --- a/lib/stdlib/doc/src/sofs.xml +++ b/lib/stdlib/doc/src/sofs.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -181,10 +181,11 @@ the <marker id="canonical_map"></marker><em>canonical map</em> is the function that maps every element of X onto its equivalence class. </p> - <p>Relations as defined above (as sets of ordered pairs) will from - now on be referred to as <em>binary relations</em>. We call a - set of ordered sets (x[1], ..., x[n]) - an <em>(n-ary) relation</em>, and say that the relation is a subset of + <p><marker id="binary_relation"></marker>Relations as defined above + (as sets of ordered pairs) will from now on be referred to as + <em>binary relations</em>. We call a set of ordered sets + (x[1], ..., x[n]) an <marker id="n_ary_relation"></marker> + <em>(n-ary) relation</em>, and say that the relation is a subset of the <marker id="Cartesian_product_tuple"></marker>Cartesian product X[1] × ... × X[n] where x[i] is an element of X[i], 1 <= i <= n. @@ -293,7 +294,8 @@ <c>partition_family/2</c>, <c>projection/2</c>, <c>restriction/3</c>, <c>substitution/2</c>) accept an Erlang function as a means to modify each element of a given unordered - set. Such a function, called SetFun in the following, can be + set. <marker id="set_fun"></marker>Such a function, called + SetFun in the following, can be specified as a functional object (fun), a tuple <c>{external, Fun}</c>, or an integer. If SetFun is specified as a fun, the fun is applied to each element of the @@ -337,34 +339,73 @@ fun(S) -> sofs:partition(1, S) end message when given badly formed arguments or sets the types of which are not compatible.</p> <p>When comparing external sets the operator <c>==/2</c> is used.</p> - <p><em>Types</em></p> - <pre> -anyset() = - an unordered, ordered or atomic set - -binary_relation() = - a binary relation - -bool() = true | false -external_set() = - an external set - -family() = - a family (of subsets) - -function() = - a function - -ordset() = - an ordered set - -relation() = - an n-ary relation - -set() = - an unordered set - -set_of_sets() = - an unordered set of set() - -set_fun() = integer() >= 1 - | {external, fun(external_set()) -> external_set()} - | fun(anyset()) -> anyset() -spec_fun() = {external, fun(external_set()) -> bool()} - | fun(anyset()) -> bool() -type() = - a type - </pre> </description> + <datatypes> + <datatype> + <name name="anyset"></name> + <desc><p>Any kind of set (also included are the atomic sets).</p></desc> + </datatype> + <datatype> + <name name="binary_relation"></name> + <desc><p>A <seealso marker="#binary_relation">binary + relation</seealso>.</p></desc> + </datatype> + <datatype> + <name name="external_set"></name> + <desc><p>An <seealso marker="#external_set">external + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="family"></name> + <desc><p>A <seealso marker="#family">family</seealso> (of subsets).</p> + </desc> + </datatype> + <datatype> + <name name="a_function"></name> + <desc><p>A <seealso marker="#function">function</seealso>.</p></desc> + </datatype> + <datatype> + <name name="ordset"></name> + <desc><p>An <seealso marker="#sets_definition">ordered + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="relation"></name> + <desc><p>An <seealso marker="#n_ary_relation">n-ary relation</seealso>. + </p></desc> + </datatype> + <datatype> + <name name="a_set"></name> + <desc><p>An <seealso marker="#sets_definition">unordered + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="set_of_sets"></name> + <desc><p>An <seealso marker="#sets_definition">unordered + set</seealso> of unordered sets.</p></desc> + </datatype> + <datatype> + <name name="set_fun"></name> + <desc><p>A <seealso marker="#set_fun">SetFun</seealso>.</p></desc> + </datatype> + <datatype> + <name name="spec_fun"></name> + </datatype> + <datatype> + <name name="type"></name> + <desc><p>A <seealso marker="#type">type</seealso>.</p></desc> + </datatype> + <datatype> + <!-- Parameterized opaque types are NYI: --> + <name><marker id="type-tuple_of">tuple_of(T)</marker></name> + <desc><p>A tuple where the elements are of type <c>T</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>a_function(Tuples [, Type]) -> Function</name> + <name name="a_function" arity="1"/> + <name name="a_function" arity="2"/> <fsummary>Create a function.</fsummary> - <type> - <v>Function = function()</v> - <v>Tuples = [tuple()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#function">function</seealso>. <c>a_function(F, T)</c> is equivalent to @@ -375,16 +416,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>canonical_relation(SetOfSets) -> BinRel</name> + <name name="canonical_relation" arity="1"/> <fsummary>Return the canonical map.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the binary relation containing the elements - (E, Set) such that Set belongs to SetOfSets and E - belongs to Set. If SetOfSets is + (E, Set) such that Set belongs to <anno>SetOfSets</anno> and E + belongs to Set. If SetOfSets is a <seealso marker="#partition">partition</seealso> of a set X and R is the equivalence relation in X induced by SetOfSets, then the returned relation is @@ -398,14 +435,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>composite(Function1, Function2) -> Function3</name> + <name name="composite" arity="2"/> <fsummary>Return the composite of two functions.</fsummary> - <type> - <v>Function1 = Function2 = Function3 = function()</v> - </type> <desc> <p>Returns the <seealso marker="#composite">composite</seealso> of - the functions Function1 and Function2.</p> + the functions <anno>Function1</anno> and + <anno>Function2</anno>.</p> <pre> 1> <input>F1 = sofs:a_function([{a,1},{b,2},{c,2}]),</input> <input>F2 = sofs:a_function([{1,x},{2,y},{3,z}]),</input> @@ -415,14 +450,9 @@ type() = - a type - </pre> </desc> </func> <func> - <name>constant_function(Set, AnySet) -> Function</name> + <name name="constant_function" arity="2"/> <fsummary>Create the function that maps each element of a set onto another set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Function = function()</v> - <v>Set = set()</v> - </type> <desc> <p>Creates the <seealso marker="#function">function</seealso> that maps each element of the set Set onto AnySet.</p> @@ -435,14 +465,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>converse(BinRel1) -> BinRel2</name> + <name name="converse" arity="1"/> <fsummary>Return the converse of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#converse">converse</seealso> - of the binary relation BinRel1.</p> + of the binary relation <anno>BinRel1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,a}]),</input> <input>R2 = sofs:converse(R1),</input> @@ -451,31 +478,25 @@ type() = - a type - </pre> </desc> </func> <func> - <name>difference(Set1, Set2) -> Set3</name> + <name name="difference" arity="2"/> <fsummary>Return the difference of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#difference">difference</seealso> of - the sets Set1 and Set2.</p> + the sets <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>digraph_to_family(Graph [, Type]) -> Family</name> + <name name="digraph_to_family" arity="1"/> + <name name="digraph_to_family" arity="2"/> <fsummary>Create a family from a directed graph.</fsummary> - <type> - <v>Graph = digraph() - see digraph(3) -</v> - <v>Family = family()</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#family">family</seealso> from - the directed graph Graph. Each vertex a of Graph is + the directed graph <anno>Graph</anno>. Each vertex a of + <anno>Graph</anno> is represented by a pair (a, {b[1], ..., b[n]}) where the b[i]'s are the out-neighbours of a. If no type is explicitly given, [{atom, [atom]}] is used as type of - the family. It is assumed that Type is + the family. It is assumed that <anno>Type</anno> is a <seealso marker="#valid_type">valid type</seealso> of the external set of the family.</p> <p>If G is a directed graph, it holds that the vertices and @@ -484,15 +505,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>domain(BinRel) -> Set</name> + <name name="domain" arity="1"/> <fsummary>Return the domain of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#domain">domain</seealso> of - the binary relation BinRel.</p> + the binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:domain(R),</input> @@ -501,16 +518,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>drestriction(BinRel1, Set) -> BinRel2</name> + <name name="drestriction" arity="2"/> <fsummary>Return a restriction of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns the difference between the binary relation BinRel1 + <p>Returns the difference between the binary relation + <anno>BinRel1</anno> and the <seealso marker="#restriction">restriction</seealso> - of BinRel1 to Set.</p> + of <anno>BinRel1</anno> to <anno>Set</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([2,4,6]),</input> @@ -522,16 +536,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>drestriction(SetFun, Set1, Set2) -> Set3</name> + <name name="drestriction" arity="3"/> <fsummary>Return a restriction of a relation.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = set()</v> - </type> - <desc> - <p>Returns a subset of Set1 containing those elements that do - not yield an element in Set2 as the result of applying - SetFun.</p> + <desc> + <p>Returns a subset of <anno>Set1</anno> containing those elements + that do + not yield an element in <anno>Set2</anno> as the result of applying + <anno>SetFun</anno>.</p> <pre> 1> <input>SetFun = {external, fun({_A,B,C}) -> {B,C} end},</input> <input>R1 = sofs:relation([{a,aa,1},{b,bb,2},{c,cc,3}]),</input> @@ -544,11 +555,8 @@ type() = - a type - </pre> </desc> </func> <func> - <name>empty_set() -> Set</name> + <name name="empty_set" arity="0"/> <fsummary>Return the untyped empty set.</fsummary> - <type> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">untyped empty set</seealso>. <c>empty_set()</c> is equivalent to @@ -556,19 +564,14 @@ type() = - a type - </pre> </desc> </func> <func> - <name>extension(BinRel1, Set, AnySet) -> BinRel2</name> + <name name="extension" arity="3"/> <fsummary>Extend the domain of a binary relation.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#extension">extension</seealso> of - BinRel1 such that - for each element E in Set that does not belong to the - <seealso marker="#domain">domain</seealso> of BinRel1, - BinRel2 contains the pair (E, AnySet).</p> + <anno>BinRel1</anno> such that + for each element E in <anno>Set</anno> that does not belong to the + <seealso marker="#domain">domain</seealso> of <anno>BinRel1</anno>, + <anno>BinRel2</anno> contains the pair (E, AnySet).</p> <pre> 1> <input>S = sofs:set([b,c]),</input> <input>A = sofs:empty_set(),</input> @@ -579,13 +582,9 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family(Tuples [, Type]) -> Family</name> + <name name="family" arity="1"/> + <name name="family" arity="2"/> <fsummary>Create a family of subsets.</fsummary> - <type> - <v>Family = family()</v> - <v>Tuples = [tuple()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#family">family of subsets</seealso>. <c>family(F, T)</c> is equivalent to @@ -596,18 +595,17 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_difference(Family1, Family2) -> Family3</name> + <name name="family_difference" arity="2"/> <fsummary>Return the difference of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> <desc> - <p>If Family1 and Family2 + <p>If <anno>Family1</anno> and <anno>Family2</anno> are <seealso marker="#family">families</seealso>, then - Family3 is the family + <anno>Family3</anno> is the family such that the index set is equal to the index set of - Family1, and Family3[i] is the difference between Family1[i] - and Family2[i] if Family2 maps i, Family1[i] otherwise.</p> + <anno>Family1</anno>, and <anno>Family3</anno>[i] is the + difference between <anno>Family1</anno>[i] + and <anno>Family2</anno>[i] if <anno>Family2</anno> maps i, + <anno>Family1</anno>[i] otherwise.</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[6,7]}]),</input> @@ -617,17 +615,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_domain(Family1) -> Family2</name> + <name name="family_domain" arity="1"/> <fsummary>Return a family of domains.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#domain">domain</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#domain">domain</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_domain(FR),</input> @@ -636,17 +635,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_field(Family1) -> Family2</name> + <name name="family_field" arity="1"/> <fsummary>Return a family of fields.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#field">field</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#field">field</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_field(FR),</input> @@ -657,21 +657,21 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_intersection(Family1) -> Family2</name> + <name name="family_intersection" arity="1"/> <fsummary>Return the intersection of a family of sets of sets.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a set of sets for every i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the <seealso marker="#intersection_n">intersection</seealso> of - Family1[i].</p> - <p>If Family1[i] is an empty set for some i, then the process - exits with a <c>badarg</c> message.</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a set of sets for every i in + the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#intersection_n">intersection</seealso> + of <anno>Family1</anno>[i].</p> + <p>If <anno>Family1</anno>[i] is an empty set for some i, then + the process exits with a <c>badarg</c> message.</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2,3],[2,3,4]]},{b,[[x,y,z],[x,y]]}]),</input> <input>F2 = sofs:family_intersection(F1),</input> @@ -680,17 +680,16 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_intersection(Family1, Family2) -> Family3</name> + <name name="family_intersection" arity="2"/> <fsummary>Return the intersection of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> - <desc> - <p>If Family1 and Family2 - are <seealso marker="#family">families</seealso>, then Family3 - is the family such that the index set is the intersection of - Family1's and Family2's index sets, and Family3[i] is the - intersection of Family1[i] and Family2[i].</p> + <desc> + <p>If <anno>Family1</anno> and <anno>Family2</anno> + are <seealso marker="#family">families</seealso>, + then <anno>Family3</anno> is the family such that the index + set is the intersection of <anno>Family1</anno>'s and + <anno>Family2</anno>'s index sets, + and <anno>Family3</anno>[i] is the intersection of + <anno>Family1</anno>[i] and <anno>Family2</anno>[i].</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),</input> @@ -700,18 +699,16 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_projection(SetFun, Family1) -> Family2</name> + <name name="family_projection" arity="2"/> <fsummary>Return a family of modified subsets.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Family1 = Family2 = family()</v> - <v>Set = set()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - then Family2 is the family with the same index set as - Family1 such that Family2[i] is the result of calling SetFun - with Family1[i] as argument.</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is the result of + calling <anno>SetFun</anno> with <anno>Family1</anno>[i] as + argument.</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),</input> <input>F2 = sofs:family_projection({sofs, union}, F1),</input> @@ -720,17 +717,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_range(Family1) -> Family2</name> + <name name="family_range" arity="1"/> <fsummary>Return a family of ranges.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#range">range</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#range">range</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_range(FR),</input> @@ -739,22 +737,21 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_specification(Fun, Family1) -> Family2</name> + <name name="family_specification" arity="2"/> <fsummary>Select a subset of a family using a predicate.</fsummary> - <type> - <v>Fun = spec_fun()</v> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso>, - then Family2 is - the <seealso marker="#restriction">restriction</seealso> of - Family1 to those elements i of the - index set for which Fun applied to Family1[i] returns - <c>true</c>. If Fun is a tuple <c>{external, Fun2}</c>, - Fun2 is applied to - the <seealso marker="#external_set">external set</seealso> of - Family1[i], otherwise Fun is applied to Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso>, + then <anno>Family2</anno> is + the <seealso marker="#restriction">restriction</seealso> of + <anno>Family1</anno> to those elements i of the index set + for which <anno>Fun</anno> applied + to <anno>Family1</anno>[i] returns + <c>true</c>. If <anno>Fun</anno> is a + tuple <c>{external, Fun2}</c>, Fun2 is applied to + the <seealso marker="#external_set">external set</seealso> + of <anno>Family1</anno>[i], otherwise <anno>Fun</anno> is + applied to <anno>Family1</anno>[i].</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2,3]},{b,[1,2]},{c,[1]}]),</input> <input>SpecFun = fun(S) -> sofs:no_elements(S) =:= 2 end,</input> @@ -764,24 +761,22 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_to_digraph(Family [, GraphType]) -> Graph</name> + <name name="family_to_digraph" arity="1"/> + <name name="family_to_digraph" arity="2"/> <fsummary>Create a directed graph from a family.</fsummary> - <type> - <v>Graph = digraph()</v> - <v>Family = family()</v> - <v>GraphType = - see digraph(3) -</v> - </type> <desc> <p>Creates a directed graph from - the <seealso marker="#family">family</seealso> Family. For each - pair (a, {b[1], ..., b[n]}) of Family, the vertex + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + For each pair (a, {b[1], ..., b[n]}) + of <anno>Family</anno>, the vertex a as well the edges (a, b[i]) for 1 <= i <= n are added to a newly created directed graph.</p> - <p>If no graph type is given, <c>digraph:new/1</c> is used for - creating the directed graph, otherwise the GraphType + <p>If no graph type is given <seealso marker="digraph#new/0"> + digraph:new/0</seealso> is used for + creating the directed graph, otherwise the <anno>GraphType</anno> argument is passed on as second argument to - <c>digraph:new/2</c>.</p> + <seealso marker="digraph#new/1">digraph:new/1</seealso>.</p> <p>It F is a family, it holds that F is a subset of <c>digraph_to_family(family_to_digraph(F), type(F))</c>. Equality holds if <c>union_of_family(F)</c> is a subset of @@ -791,17 +786,15 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_to_relation(Family) -> BinRel</name> + <name name="family_to_relation" arity="1"/> <fsummary>Create a binary relation from a family.</fsummary> - <type> - <v>Family = family()</v> - <v>BinRel = binary_relation()</v> - </type> - <desc> - <p>If Family is a <seealso marker="#family">family</seealso>, - then BinRel is the binary relation containing all pairs - (i, x) such that i belongs to the index set of Family - and x belongs to Family[i].</p> + <desc> + <p>If <anno>Family</anno> is + a <seealso marker="#family">family</seealso>, + then <anno>BinRel</anno> is the binary relation containing + all pairs (i, x) such that i belongs to the index set + of <anno>Family</anno> and x belongs + to <anno>Family</anno>[i].</p> <pre> 1> <input>F = sofs:family([{a,[]}, {b,[1]}, {c,[2,3]}]),</input> <input>R = sofs:family_to_relation(F),</input> @@ -810,17 +803,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_union(Family1) -> Family2</name> + <name name="family_union" arity="1"/> <fsummary>Return the union of a family of sets of sets.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a set of sets for each i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the <seealso marker="#union_n">union</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a set of sets for each i in + the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#union_n">union</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),</input> <input>F2 = sofs:family_union(F1),</input> @@ -831,18 +825,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_union(Family1, Family2) -> Family3</name> + <name name="family_union" arity="2"/> <fsummary>Return the union of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> - <desc> - <p>If Family1 and Family2 - are <seealso marker="#family">families</seealso>, then Family3 - is the family such that the index set is the union of Family1's - and Family2's index sets, and Family3[i] is the union of - Family1[i] and Family2[i] if both maps i, Family1[i] or - Family2[i] otherwise.</p> + <desc> + <p>If <anno>Family1</anno> and <anno>Family2</anno> + are <seealso marker="#family">families</seealso>, + then <anno>Family3</anno> is the family such that the index + set is the union of <anno>Family1</anno>'s + and <anno>Family2</anno>'s index sets, + and <anno>Family3</anno>[i] is the union + of <anno>Family1</anno>[i] and <anno>Family2</anno>[i] if + both maps i, <anno>Family1</anno>[i] + or <anno>Family2</anno>[i] otherwise.</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),</input> @@ -852,15 +846,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>field(BinRel) -> Set</name> + <name name="field" arity="1"/> <fsummary>Return the field of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#field">field</seealso> of the - binary relation BinRel.</p> + binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:field(R),</input> @@ -871,31 +861,24 @@ type() = - a type - </pre> </desc> </func> <func> - <name>from_external(ExternalSet, Type) -> AnySet</name> + <name name="from_external" arity="2"/> <fsummary>Create a set.</fsummary> - <type> - <v>ExternalSet = external_set()</v> - <v>AnySet = anyset()</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a set from the <seealso marker="#external_set">external - set</seealso> ExternalSet - and the <seealso marker="#type">type</seealso> Type. It is - assumed that Type is a <seealso marker="#valid_type">valid - type</seealso> of ExternalSet.</p> + set</seealso> <anno>ExternalSet</anno> + and the <seealso marker="#type">type</seealso> <anno>Type</anno>. + It is assumed that <anno>Type</anno> is + a <seealso marker="#valid_type">valid + type</seealso> of <anno>ExternalSet</anno>.</p> </desc> </func> <func> - <name>from_sets(ListOfSets) -> Set</name> + <name name="from_sets" arity="1" clause_i="1"/> <fsummary>Create a set out of a list of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>ListOfSets = [anyset()]</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">unordered - set</seealso> containing the sets of the list ListOfSets.</p> + set</seealso> containing the sets of the list + <anno>ListOfSets</anno>.</p> <pre> 1> <input>S1 = sofs:relation([{a,1},{b,2}]),</input> <input>S2 = sofs:relation([{x,3},{y,4}]),</input> @@ -905,38 +888,33 @@ type() = - a type - </pre> </desc> </func> <func> - <name>from_sets(TupleOfSets) -> Ordset</name> + <name name="from_sets" arity="1" clause_i="2"/> <fsummary>Create an ordered set out of a tuple of sets.</fsummary> - <type> - <v>Ordset = ordset()</v> - <v>TupleOfSets = tuple-of(anyset())</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">ordered set</seealso> containing the sets of the non-empty tuple - TupleOfSets.</p> + <anno>TupleOfSets</anno>.</p> </desc> </func> <func> - <name>from_term(Term [, Type]) -> AnySet</name> + <name name="from_term" arity="1"/> + <name name="from_term" arity="2"/> <fsummary>Create a set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Term = term()</v> - <v>Type = type()</v> - </type> <desc> <p><marker id="from_term"></marker>Creates an element of <seealso marker="#sets_definition">Sets</seealso> by - traversing the term Term, sorting lists, removing duplicates and + traversing the term <anno>Term</anno>, sorting lists, + removing duplicates and deriving or verifying a <seealso marker="#valid_type">valid type</seealso> for the so obtained external set. An - explicitly given <seealso marker="#type">type</seealso> Type + explicitly given <seealso marker="#type">type</seealso> + <anno>Type</anno> can be used to limit the depth of the traversal; an atomic type stops the traversal, as demonstrated by this example where "foo" and {"foo"} are left unmodified:</p> <pre> -1> <input>S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}], [{atom,[atom]}]),</input> +1> <input>S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}], +[{atom,[atom]}]),</input> <input>sofs:to_external(S).</input> [{{"foo"},[1]},{"foo",[2]}]</pre> <p><c>from_term</c> can be used for creating atomic or ordered @@ -963,15 +941,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>image(BinRel, Set1) -> Set2</name> + <name name="image" arity="2"/> <fsummary>Return the image of a set under a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#image">image</seealso> of the - set Set1 under the binary relation BinRel.</p> + set <anno>Set1</anno> under the binary + relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),</input> <input>S1 = sofs:set([1,2]),</input> @@ -981,42 +956,32 @@ type() = - a type - </pre> </desc> </func> <func> - <name>intersection(SetOfSets) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a set of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the <seealso marker="#intersection_n">intersection</seealso> of - the set of sets SetOfSets.</p> + the set of sets <anno>SetOfSets</anno>.</p> <p>Intersecting an empty set of sets exits the process with a <c>badarg</c> message.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#intersection">intersection</seealso> of - Set1 and Set2.</p> + <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>intersection_of_family(Family) -> Set</name> + <name name="intersection_of_family" arity="1"/> <fsummary>Return the intersection of a family.</fsummary> - <type> - <v>Family = family()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the intersection of - the <seealso marker="#family">family</seealso> Family.</p> + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + </p> <p>Intersecting an empty family exits the process with a <c>badarg</c> message.</p> <pre> @@ -1027,14 +992,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>inverse(Function1) -> Function2</name> + <name name="inverse" arity="1"/> <fsummary>Return the inverse of a function.</fsummary> - <type> - <v>Function1 = Function2 = function()</v> - </type> <desc> <p>Returns the <seealso marker="#inverse">inverse</seealso> - of the function Function1.</p> + of the function <anno>Function1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>R2 = sofs:inverse(R1),</input> @@ -1043,16 +1005,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>inverse_image(BinRel, Set1) -> Set2</name> + <name name="inverse_image" arity="2"/> <fsummary>Return the inverse image of a set under a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns the <seealso marker="#inverse_image">inverse - image</seealso> of Set1 under the binary relation BinRel.</p> + <p>Returns the <seealso marker="#inverse_image">inverse + image</seealso> of <anno>Set1</anno> under the binary + relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),</input> <input>S1 = sofs:set([c,d,e]),</input> @@ -1062,52 +1021,38 @@ type() = - a type - </pre> </desc> </func> <func> - <name>is_a_function(BinRel) -> Bool</name> + <name name="is_a_function" arity="1"/> <fsummary>Test for a function.</fsummary> - <type> - <v>Bool = bool()</v> - <v>BinRel = binary_relation()</v> - </type> <desc> - <p>Returns <c>true</c> if the binary relation BinRel is a - <seealso marker="#function">function</seealso> or the + <p>Returns <c>true</c> if the binary relation <anno>BinRel</anno> + is a <seealso marker="#function">function</seealso> or the untyped empty set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> Bool</name> + <name name="is_disjoint" arity="2"/> <fsummary>Test for disjoint sets.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if Set1 and Set2 + <p>Returns <c>true</c> if <anno>Set1</anno> + and <anno>Set2</anno> are <seealso marker="#disjoint">disjoint</seealso>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty_set(AnySet) -> Bool</name> + <name name="is_empty_set" arity="1"/> <fsummary>Test for an empty set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if Set is an empty unordered set, - <c>false</c> otherwise.</p> + <p>Returns <c>true</c> if <anno>AnySet</anno> is an empty + unordered set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_equal(AnySet1, AnySet2) -> Bool</name> + <name name="is_equal" arity="2"/> <fsummary>Test two sets for equality.</fsummary> - <type> - <v>AnySet1 = AnySet2 = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if the AnySet1 and AnySet2 + <p>Returns <c>true</c> if the <anno>AnySet1</anno> + and <anno>AnySet2</anno> are <seealso marker="#equal">equal</seealso>, <c>false</c> otherwise. This example shows that <c>==/2</c> is used when comparing sets for equality:</p> @@ -1119,67 +1064,49 @@ true</pre> </desc> </func> <func> - <name>is_set(AnySet) -> Bool</name> + <name name="is_set" arity="1"/> <fsummary>Test for an unordered set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if AnySet is + <p>Returns <c>true</c> if <anno>AnySet</anno> is an <seealso marker="#sets_definition">unordered set</seealso>, and - <c>false</c> if AnySet is an ordered set or an atomic set.</p> + <c>false</c> if <anno>AnySet</anno> is an ordered set or an + atomic set.</p> </desc> </func> <func> - <name>is_sofs_set(Term) -> Bool</name> + <name name="is_sofs_set" arity="1"/> <fsummary>Test for an unordered set.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if Term is + <p>Returns <c>true</c> if <anno>Term</anno> is an <seealso marker="#sets_definition">unordered set</seealso>, an ordered set or an atomic set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> Bool</name> + <name name="is_subset" arity="2"/> <fsummary>Test two sets for subset.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if Set1 is - a <seealso marker="#subset">subset</seealso> of Set2, <c>false</c> - otherwise.</p> + <p>Returns <c>true</c> if <anno>Set1</anno> is + a <seealso marker="#subset">subset</seealso> + of <anno>Set2</anno>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_type(Term) -> Bool</name> + <name name="is_type" arity="1"/> <fsummary>Test for a type.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if the term Term is + <p>Returns <c>true</c> if the term <anno>Term</anno> is a <seealso marker="#type">type</seealso>.</p> </desc> </func> <func> - <name>join(Relation1, I, Relation2, J) -> Relation3</name> + <name name="join" arity="4"/> <fsummary>Return the join of two relations.</fsummary> - <type> - <v>Relation1 = Relation2 = Relation3 = relation()</v> - <v>I = J = integer() > 0</v> - </type> <desc> <p>Returns the <seealso marker="#natural_join">natural - join</seealso> of the relations Relation1 and Relation2 on - coordinates I and J.</p> + join</seealso> of the relations <anno>Relation1</anno> + and <anno>Relation2</anno> on coordinates <anno>I</anno> and + <anno>J</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{a,x,1},{b,y,2}]),</input> <input>R2 = sofs:relation([{1,f,g},{1,h,i},{2,3,4}]),</input> @@ -1189,20 +1116,17 @@ true</pre> </desc> </func> <func> - <name>multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2</name> + <name name="multiple_relative_product" arity="2"/> <fsummary>Return the multiple relative product of a tuple of binary relations and a relation.</fsummary> - <type> - <v>TupleOfBinRels = tuple-of(BinRel)</v> - <v>BinRel = BinRel1 = BinRel2 = binary_relation()</v> - </type> - <desc> - <p>If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is + <desc> + <p>If <anno>TupleOfBinRels</anno> is a non-empty tuple + {R[1], ..., R[n]} of binary relations + and <anno>BinRel1</anno> is a binary relation, + then <anno>BinRel2</anno> is the <seealso marker="#multiple_relative_product">multiple relative product</seealso> of the ordered set - (R[i], ..., R[n]) and BinRel1.</p> + (R[i], ..., R[n]) and <anno>BinRel1</anno>.</p> <pre> 1> <input>Ri = sofs:relation([{a,1},{b,2},{c,3}]),</input> <input>R = sofs:relation([{a,b},{b,c},{c,a}]),</input> @@ -1212,29 +1136,21 @@ true</pre> </desc> </func> <func> - <name>no_elements(ASet) -> NoElements</name> + <name name="no_elements" arity="1"/> <fsummary>Return the number of elements of a set.</fsummary> - <type> - <v>ASet = set() | ordset()</v> - <v>NoElements = integer() >= 0 </v> - </type> <desc> <p>Returns the number of elements of the ordered or unordered - set ASet.</p> + set <anno>ASet</anno>.</p> </desc> </func> <func> - <name>partition(SetOfSets) -> Partition</name> + <name name="partition" arity="1"/> <fsummary>Return the coarsest partition given a set of sets.</fsummary> - <type> - <v>SetOfSets = set_of_sets()</v> - <v>Partition = set()</v> - </type> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of - the union of the set of sets SetOfSets such that two + the union of the set of sets <anno>SetOfSets</anno> such that two elements are considered equal if they belong to the same - elements of SetOfSets.</p> + elements of <anno>SetOfSets</anno>.</p> <pre> 1> <input>Sets1 = sofs:from_term([[a,b,c],[d,e,f],[g,h,i]]),</input> <input>Sets2 = sofs:from_term([[b,c,d],[e,f,g],[h,i,j]]),</input> @@ -1244,17 +1160,12 @@ true</pre> </desc> </func> <func> - <name>partition(SetFun, Set) -> Partition</name> + <name name="partition" arity="2"/> <fsummary>Return a partition of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Partition = set()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of - Set such that two elements are considered equal if the - results of applying SetFun are equal.</p> + <anno>Set</anno> such that two elements are considered equal + if the results of applying <anno>SetFun</anno> are equal.</p> <pre> 1> <input>Ss = sofs:from_term([[a],[b],[c,d],[e,f]]),</input> <input>SetFun = fun(S) -> sofs:from_term(sofs:no_elements(S)) end,</input> @@ -1264,19 +1175,16 @@ true</pre> </desc> </func> <func> - <name>partition(SetFun, Set1, Set2) -> {Set3, Set4}</name> + <name name="partition" arity="3"/> <fsummary>Return a partition of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = Set4 = set()</v> - </type> <desc> <p>Returns a pair of sets that, regarded as constituting a set, forms a <seealso marker="#partition">partition</seealso> of - Set1. If the - result of applying SetFun to an element of Set1 yields an - element in Set2, the element belongs to Set3, otherwise the - element belongs to Set4.</p> + <anno>Set1</anno>. If the + result of applying <anno>SetFun</anno> to an element + of <anno>Set1</anno> yields an element in <anno>Set2</anno>, + the element belongs to <anno>Set3</anno>, otherwise the + element belongs to <anno>Set4</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([2,4,6]),</input> @@ -1289,21 +1197,17 @@ true</pre> </desc> </func> <func> - <name>partition_family(SetFun, Set) -> Family</name> + <name name="partition_family" arity="2"/> <fsummary>Return a family indexing a partition.</fsummary> - <type> - <v>Family = family()</v> - <v>SetFun = set_fun()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#family">family</seealso> - Family where the indexed set is - a <seealso marker="#partition">partition</seealso> of Set - such that two elements are considered equal if the results - of applying SetFun are the same value i. This i is the index - that Family maps onto - the <seealso marker="#equivalence_class">equivalence + <anno>Family</anno> where the indexed set is + a <seealso marker="#partition">partition</seealso> + of <anno>Set</anno> such that two elements are considered + equal if the results of applying <anno>SetFun</anno> are the + same value i. This i is the index that <anno>Family</anno> + maps onto + the <seealso marker="#equivalence_class">equivalence class</seealso>.</p> <pre> 1> <input>S = sofs:relation([{a,a,a,a},{a,a,b,b},{a,b,b,b}]),</input> @@ -1314,18 +1218,15 @@ true</pre> </desc> </func> <func> - <name>product(TupleOfSets) -> Relation</name> + <name name="product" arity="1"/> <fsummary>Return the Cartesian product of a tuple of sets.</fsummary> - <type> - <v>Relation = relation()</v> - <v>TupleOfSets = tuple-of(set())</v> - </type> <desc> <p>Returns the <seealso marker="#Cartesian_product_tuple">Cartesian product</seealso> of the non-empty tuple of sets - TupleOfSets. If (x[1], ..., x[n]) is an element of - the n-ary relation Relation, then x[i] is drawn from element - i of TupleOfSets.</p> + <anno>TupleOfSets</anno>. If (x[1], ..., x[n]) is + an element of the n-ary relation <anno>Relation</anno>, then + x[i] is drawn from element i + of <anno>TupleOfSets</anno>.</p> <pre> 1> <input>S1 = sofs:set([a,b]),</input> <input>S2 = sofs:set([1,2]),</input> @@ -1336,15 +1237,12 @@ true</pre> </desc> </func> <func> - <name>product(Set1, Set2) -> BinRel</name> + <name name="product" arity="2"/> <fsummary>Return the Cartesian product of two sets.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#Cartesian_product">Cartesian - product</seealso> of Set1 and Set2.</p> + product</seealso> of <anno>Set1</anno> + and <anno>Set2</anno>.</p> <pre> 1> <input>S1 = sofs:set([1,2]),</input> <input>S2 = sofs:set([a,b]),</input> @@ -1356,19 +1254,16 @@ true</pre> </desc> </func> <func> - <name>projection(SetFun, Set1) -> Set2</name> + <name name="projection" arity="2"/> <fsummary>Return a set of substituted elements.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the set created by substituting each element of - Set1 by the result of applying SetFun to the element.</p> - <p>If SetFun is a number i >= 1 and Set1 is a - relation, then the returned set is - the <seealso marker="#projection">projection</seealso> of Set1 - onto coordinate i.</p> + <anno>Set1</anno> by the result of + applying <anno>SetFun</anno> to the element.</p> + <p>If <anno>SetFun</anno> is a number i >= 1 and + <anno>Set1</anno> is a relation, then the returned set is + the <seealso marker="#projection">projection</seealso> of + <anno>Set1</anno> onto coordinate i.</p> <pre> 1> <input>S1 = sofs:from_term([{1,a},{2,b},{3,a}]),</input> <input>S2 = sofs:projection(2, S1),</input> @@ -1377,15 +1272,11 @@ true</pre> </desc> </func> <func> - <name>range(BinRel) -> Set</name> + <name name="range" arity="1"/> <fsummary>Return the range of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#range">range</seealso> of the - binary relation BinRel.</p> + binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:range(R),</input> @@ -1394,41 +1285,33 @@ true</pre> </desc> </func> <func> - <name>relation(Tuples [, Type]) -> Relation</name> + <name name="relation" arity="1"/> + <name name="relation" arity="2"/> <fsummary>Create a relation.</fsummary> - <type> - <v>N = integer()</v> - <v>Type = N | type()</v> - <v>Relation = relation()</v> - <v>Tuples = [tuple()]</v> - </type> <desc> <p>Creates a <seealso marker="#relation">relation</seealso>. <c>relation(R, T)</c> is equivalent to <c>from_term(R, T)</c>, if T is a <seealso marker="#type">type</seealso> and the result is a - relation. If Type is an integer N, then + relation. If <anno>Type</anno> is an integer N, then <c>[{atom, ..., atom}])</c>, where the size of the tuple is N, is used as type of the relation. If no type is - explicitly given, the size of the first tuple of Tuples is + explicitly given, the size of the first tuple of + <anno>Tuples</anno> is used if there is such a tuple. <c>relation([])</c> is equivalent to <c>relation([], 2)</c>.</p> </desc> </func> <func> - <name>relation_to_family(BinRel) -> Family</name> + <name name="relation_to_family" arity="1"/> <fsummary>Create a family from a binary relation.</fsummary> - <type> - <v>Family = family()</v> - <v>BinRel = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#family">family</seealso> - Family such that the index set is equal to + <anno>Family</anno> such that the index set is equal to the <seealso marker="#domain">domain</seealso> of the binary - relation BinRel, and Family[i] is + relation <anno>BinRel</anno>, and <anno>Family</anno>[i] is the <seealso marker="#image">image</seealso> of the set of i - under BinRel.</p> + under <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{b,1},{c,2},{c,3}]),</input> <input>F = sofs:relation_to_family(R),</input> @@ -1437,63 +1320,57 @@ true</pre> </desc> </func> <func> - <name>relative_product(TupleOfBinRels [, BinRel1]) -> BinRel2</name> - <fsummary>Return the relative product of a tuple of binary relations + <name name="relative_product" arity="1"/> + <name name="relative_product" arity="2" clause_i="1"/> + <fsummary>Return the relative product of a list of binary relations and a binary relation.</fsummary> - <type> - <v>TupleOfBinRels = tuple-of(BinRel)</v> - <v>BinRel = BinRel1 = BinRel2 = binary_relation()</v> - </type> - <desc> - <p>If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is - the <seealso marker="#tuple_relative_product">relative - product</seealso> of the ordered set (R[i], ..., R[n]) - and BinRel1.</p> - <p>If BinRel1 is omitted, the relation of equality between the - elements of - the <seealso marker="#Cartesian_product_tuple">Cartesian - product</seealso> of the ranges of R[i], + <desc> + <p>If <anno>ListOfBinRels</anno> is a non-empty list + [R[1], ..., R[n]] of binary relations and + <anno>BinRel1</anno> + is a binary relation, then <anno>BinRel2</anno> is the <seealso + marker="#tuple_relative_product">relative product</seealso> + of the ordered set (R[i], ..., R[n]) and + <anno>BinRel1</anno>.</p> + <p>If <anno>BinRel1</anno> is omitted, the relation of equality + between the elements of + the <seealso marker="#Cartesian_product_tuple">Cartesian + product</seealso> of the ranges of R[i], range R[1] × ... × range R[n], is used instead (intuitively, nothing is "lost").</p> <pre> 1> <input>TR = sofs:relation([{1,a},{1,aa},{2,b}]),</input> <input>R1 = sofs:relation([{1,u},{2,v},{3,c}]),</input> -<input>R2 = sofs:relative_product({TR, R1}),</input> +<input>R2 = sofs:relative_product([TR, R1]),</input> <input>sofs:to_external(R2).</input> [{1,{a,u}},{1,{aa,u}},{2,{b,v}}]</pre> - <p>Note that <c>relative_product({R1}, R2)</c> is + <p>Note that <c>relative_product([R1], R2)</c> is different from <c>relative_product(R1, R2)</c>; the - tuple of one element is not identified with the element + list of one element is not identified with the element itself.</p> </desc> </func> <func> - <name>relative_product(BinRel1, BinRel2) -> BinRel3</name> + <name name="relative_product" arity="2" clause_i="2"/> <fsummary>Return the relative product of two binary relations.</fsummary> - <type> - <v>BinRel1 = BinRel2 = BinRel3 = binary_relation()</v> - </type> <desc> <p><marker id="relprod_impl"></marker>Returns the <seealso marker="#relative_product">relative - product</seealso> of the binary relations BinRel1 and BinRel2.</p> + product</seealso> of the binary relations <anno>BinRel1</anno> + and <anno>BinRel2</anno>.</p> </desc> </func> <func> - <name>relative_product1(BinRel1, BinRel2) -> BinRel3</name> + <name name="relative_product1" arity="2"/> <fsummary>Return the relative_product of two binary relations.</fsummary> - <type> - <v>BinRel1 = BinRel2 = BinRel3 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#relative_product">relative product</seealso> of the <seealso marker="#converse">converse</seealso> of the - binary relation BinRel1 and the binary relation BinRel2.</p> + binary relation <anno>BinRel1</anno> and the binary + relation <anno>BinRel2</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{1,aa},{2,b}]),</input> <input>R2 = sofs:relation([{1,u},{2,v},{3,c}]),</input> @@ -1505,15 +1382,12 @@ true</pre> </desc> </func> <func> - <name>restriction(BinRel1, Set) -> BinRel2</name> + <name name="restriction" arity="2"/> <fsummary>Return a restriction of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#restriction">restriction</seealso> of - the binary relation BinRel1 to Set.</p> + the binary relation <anno>BinRel1</anno> + to <anno>Set</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([1,2,4]),</input> @@ -1523,15 +1397,12 @@ true</pre> </desc> </func> <func> - <name>restriction(SetFun, Set1, Set2) -> Set3</name> + <name name="restriction" arity="3"/> <fsummary>Return a restriction of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns a subset of Set1 containing those elements that - yield an element in Set2 as the result of applying SetFun.</p> + <p>Returns a subset of <anno>Set1</anno> containing those + elements that yield an element in <anno>Set2</anno> as the + result of applying <anno>SetFun</anno>.</p> <pre> 1> <input>S1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S2 = sofs:set([b,c,d]),</input> @@ -1541,13 +1412,9 @@ true</pre> </desc> </func> <func> - <name>set(Terms [, Type]) -> Set</name> + <name name="set" arity="1"/> + <name name="set" arity="2"/> <fsummary>Create a set of atoms or any type of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>Terms = [term()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates an <seealso marker="#sets_definition">unordered set</seealso>. <c>set(L, T)</c> is equivalent to @@ -1557,18 +1424,16 @@ true</pre> </desc> </func> <func> - <name>specification(Fun, Set1) -> Set2</name> + <name name="specification" arity="2"/> <fsummary>Select a subset using a predicate.</fsummary> - <type> - <v>Fun = spec_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns the set containing every element of Set1 for which - Fun returns <c>true</c>. If Fun is a tuple + <p>Returns the set containing every element + of <anno>Set1</anno> for which <anno>Fun</anno> + returns <c>true</c>. If <anno>Fun</anno> is a tuple <c>{external, Fun2}</c>, Fun2 is applied to the <seealso marker="#external_set">external set</seealso> of - each element, otherwise Fun is applied to each element.</p> + each element, otherwise <anno>Fun</anno> is applied to each + element.</p> <pre> 1> <input>R1 = sofs:relation([{a,1},{b,2}]),</input> <input>R2 = sofs:relation([{x,1},{x,2},{y,3}]),</input> @@ -1579,15 +1444,13 @@ true</pre> </desc> </func> <func> - <name>strict_relation(BinRel1) -> BinRel2</name> + <name name="strict_relation" arity="1"/> <fsummary>Return the strict relation corresponding to a given relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#strict_relation">strict - relation</seealso> corresponding to the binary relation BinRel1.</p> + relation</seealso> corresponding to the binary + relation <anno>BinRel1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,1},{1,2},{2,1},{2,2}]),</input> <input>R2 = sofs:strict_relation(R1),</input> @@ -1596,16 +1459,13 @@ true</pre> </desc> </func> <func> - <name>substitution(SetFun, Set1) -> Set2</name> + <name name="substitution" arity="2"/> <fsummary>Return a function with a given set as domain.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> - <desc> - <p>Returns a function, the domain of which is Set1. The value - of an element of the domain is the result of applying SetFun - to the element.</p> + <desc> + <p>Returns a function, the domain of which + is <anno>Set1</anno>. The value of an element of the domain + is the result of applying <anno>SetFun</anno> to the + element.</p> <pre> 1> <input>L = [{a,1},{b,2}].</input> [{a,1},{b,2}] @@ -1647,14 +1507,12 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>symdiff(Set1, Set2) -> Set3</name> + <name name="symdiff" arity="2"/> <fsummary>Return the symmetric difference of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#symmetric_difference">symmetric - difference</seealso> (or the Boolean sum) of Set1 and Set2.</p> + difference</seealso> (or the Boolean sum) + of <anno>Set1</anno> and <anno>Set2</anno>.</p> <pre> 1> <input>S1 = sofs:set([1,2,3]),</input> <input>S2 = sofs:set([2,3,4]),</input> @@ -1664,88 +1522,67 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5}</name> + <name name="symmetric_partition" arity="2"/> <fsummary>Return a partition of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = Set4 = Set5 = set()</v> - </type> <desc> - <p>Returns a triple of sets: Set3 contains the elements - of Set1 that do not belong to Set2; Set4 contains the - elements of Set1 that belong to Set2; Set5 contains the - elements of Set2 that do not belong to Set1.</p> + <p>Returns a triple of sets: <anno>Set3</anno> contains the + elements of <anno>Set1</anno> that do not belong + to <anno>Set2</anno>; <anno>Set4</anno> contains the + elements of <anno>Set1</anno> that belong + to <anno>Set2</anno>; <anno>Set5</anno> contains the + elements of <anno>Set2</anno> that do not belong + to <anno>Set1</anno>.</p> </desc> </func> <func> - <name>to_external(AnySet) -> ExternalSet</name> + <name name="to_external" arity="1"/> <fsummary>Return the elements of a set.</fsummary> - <type> - <v>ExternalSet = external_set()</v> - <v>AnySet = anyset()</v> - </type> <desc> <p>Returns the <seealso marker="#external_set">external set</seealso> of an atomic, ordered or unordered set.</p> </desc> </func> <func> - <name>to_sets(ASet) -> Sets</name> + <name name="to_sets" arity="1"/> <fsummary>Return a list or a tuple of the elements of set.</fsummary> - <type> - <v>ASet = set() | ordset()</v> - <v>Sets = tuple_of(AnySet) | [AnySet]</v> - </type> <desc> - <p>Returns the elements of the ordered set ASet as a tuple of - sets, and the elements of the unordered set ASet as a sorted - list of sets without duplicates.</p> + <p>Returns the elements of the ordered set <anno>ASet</anno> + as a tuple of sets, and the elements of the unordered set + <anno>ASet</anno> as a sorted list of sets without + duplicates.</p> </desc> </func> <func> - <name>type(AnySet) -> Type</name> + <name name="type" arity="1"/> <fsummary>Return the type of a set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Type = type()</v> - </type> <desc> <p>Returns the <seealso marker="#type">type</seealso> of an atomic, ordered or unordered set.</p> </desc> </func> <func> - <name>union(SetOfSets) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a set of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the <seealso marker="#union_n">union</seealso> of the - set of sets SetOfSets.</p> + set of sets <anno>SetOfSets</anno>.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#union">union</seealso> of - Set1 and Set2.</p> + <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>union_of_family(Family) -> Set</name> + <name name="union_of_family" arity="1"/> <fsummary>Return the union of a family.</fsummary> - <type> - <v>Family = family()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the union of - the <seealso marker="#family">family</seealso> Family.</p> + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + </p> <pre> 1> <input>F = sofs:family([{a,[0,2,4]},{b,[0,1,2]},{c,[2,3]}]),</input> <input>S = sofs:union_of_family(F),</input> @@ -1754,17 +1591,15 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>weak_relation(BinRel1) -> BinRel2</name> + <name name="weak_relation" arity="1"/> <fsummary>Return the weak relation corresponding to a given relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns a subset S of the <seealso marker="#weak_relation">weak relation</seealso> W - corresponding to the binary relation BinRel1. Let F be the - <seealso marker="#field">field</seealso> of BinRel1. The + corresponding to the binary relation <anno>BinRel1</anno>. + Let F be the <seealso marker="#field">field</seealso> of + <anno>BinRel1</anno>. The subset S is defined so that x S y if x W y for some x in F and for some y in F.</p> <pre> diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml new file mode 100644 index 0000000000..98338b5ec2 --- /dev/null +++ b/lib/stdlib/doc/src/specs.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="latin1" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_array.xml"/> + <xi:include href="../specs/specs_base64.xml"/> + <xi:include href="../specs/specs_beam_lib.xml"/> + <xi:include href="../specs/specs_binary.xml"/> + <xi:include href="../specs/specs_c.xml"/> + <xi:include href="../specs/specs_calendar.xml"/> + <xi:include href="../specs/specs_dets.xml"/> + <xi:include href="../specs/specs_dict.xml"/> + <xi:include href="../specs/specs_digraph.xml"/> + <xi:include href="../specs/specs_digraph_utils.xml"/> + <xi:include href="../specs/specs_epp.xml"/> + <xi:include href="../specs/specs_erl_eval.xml"/> + <xi:include href="../specs/specs_erl_expand_records.xml"/> + <xi:include href="../specs/specs_erl_id_trans.xml"/> + <xi:include href="../specs/specs_erl_internal.xml"/> + <xi:include href="../specs/specs_erl_lint.xml"/> + <xi:include href="../specs/specs_erl_parse.xml"/> + <xi:include href="../specs/specs_erl_pp.xml"/> + <xi:include href="../specs/specs_erl_scan.xml"/> + <xi:include href="../specs/specs_erl_tar.xml"/> + <xi:include href="../specs/specs_ets.xml"/> + <xi:include href="../specs/specs_file_sorter.xml"/> + <xi:include href="../specs/specs_filelib.xml"/> + <xi:include href="../specs/specs_filename.xml"/> + <xi:include href="../specs/specs_gb_sets.xml"/> + <xi:include href="../specs/specs_gb_trees.xml"/> + <xi:include href="../specs/specs_gen_event.xml"/> + <xi:include href="../specs/specs_gen_fsm.xml"/> + <xi:include href="../specs/specs_gen_server.xml"/> + <xi:include href="../specs/specs_io.xml"/> + <xi:include href="../specs/specs_io_lib.xml"/> + <xi:include href="../specs/specs_lib.xml"/> + <xi:include href="../specs/specs_lists.xml"/> + <xi:include href="../specs/specs_log_mf_h.xml"/> + <xi:include href="../specs/specs_math.xml"/> + <xi:include href="../specs/specs_ms_transform.xml"/> + <xi:include href="../specs/specs_orddict.xml"/> + <xi:include href="../specs/specs_ordsets.xml"/> + <xi:include href="../specs/specs_pg.xml"/> + <xi:include href="../specs/specs_pool.xml"/> + <xi:include href="../specs/specs_proc_lib.xml"/> + <xi:include href="../specs/specs_proplists.xml"/> + <xi:include href="../specs/specs_qlc.xml"/> + <xi:include href="../specs/specs_queue.xml"/> + <xi:include href="../specs/specs_random.xml"/> + <xi:include href="../specs/specs_re.xml"/> + <xi:include href="../specs/specs_regexp.xml"/> + <xi:include href="../specs/specs_sets.xml"/> + <xi:include href="../specs/specs_shell.xml"/> + <xi:include href="../specs/specs_shell_default.xml"/> + <xi:include href="../specs/specs_slave.xml"/> + <xi:include href="../specs/specs_sofs.xml"/> + <xi:include href="../specs/specs_string.xml"/> + <xi:include href="../specs/specs_supervisor.xml"/> + <xi:include href="../specs/specs_supervisor_bridge.xml"/> + <xi:include href="../specs/specs_sys.xml"/> + <xi:include href="../specs/specs_timer.xml"/> + <xi:include href="../specs/specs_unicode.xml"/> + <xi:include href="../specs/specs_win32reg.xml"/> + <xi:include href="../specs/specs_zip.xml"/> +</specs> diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index de1b99a2d5..48867ffe72 100644 --- a/lib/stdlib/doc/src/string.xml +++ b/lib/stdlib/doc/src/string.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,65 +38,46 @@ </description> <funcs> <func> - <name>len(String) -> Length</name> + <name name="len" arity="1"/> <fsummary>Return the length of a string</fsummary> - <type> - <v>String = string()</v> - <v>Length = integer()</v> - </type> <desc> <p>Returns the number of characters in the string.</p> </desc> </func> <func> - <name>equal(String1, String2) -> bool()</name> + <name name="equal" arity="2"/> <fsummary>Test string equality</fsummary> - <type> - <v>String1 = String2 = string()</v> - </type> <desc> <p>Tests whether two strings are equal. Returns <c>true</c> if they are, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>concat(String1, String2) -> String3</name> + <name name="concat" arity="2"/> <fsummary>Concatenate two strings</fsummary> - <type> - <v>String1 = String2 = String3 = string()</v> - </type> <desc> <p>Concatenates two strings to form a new string. Returns the new string.</p> </desc> </func> <func> - <name>chr(String, Character) -> Index</name> - <name>rchr(String, Character) -> Index</name> + <name name="chr" arity="2"/> + <name name="rchr" arity="2"/> <fsummary>Return the index of the first/last occurrence of<c>Character</c>in <c>String</c></fsummary> - <type> - <v>String = string()</v> - <v>Character = char()</v> - <v>Index = integer()</v> - </type> <desc> <p>Returns the index of the first/last occurrence of - <c>Character</c> in <c>String</c>. <c>0</c> is returned if <c>Character</c> does not + <c><anno>Character</anno></c> in <c><anno>String</anno></c>. <c>0</c> is returned if <c><anno>Character</anno></c> does not occur.</p> </desc> </func> <func> - <name>str(String, SubString) -> Index</name> - <name>rstr(String, SubString) -> Index</name> + <name name="str" arity="2"/> + <name name="rstr" arity="2"/> <fsummary>Find the index of a substring</fsummary> - <type> - <v>String = SubString = string()</v> - <v>Index = integer()</v> - </type> <desc> <p>Returns the position where the first/last occurrence of - <c>SubString</c> begins in <c>String</c>. <c>0</c> is returned if <c>SubString</c> - does not exist in <c>String</c>. + <c><anno>SubString</anno></c> begins in <c><anno>String</anno></c>. <c>0</c> is returned if <c><anno>SubString</anno></c> + does not exist in <c><anno>String</anno></c>. For example:</p> <code type="none"> > string:str(" Hello Hello World World ", "Hello World"). @@ -104,17 +85,13 @@ </desc> </func> <func> - <name>span(String, Chars) -> Length </name> - <name>cspan(String, Chars) -> Length</name> + <name name="span" arity="2"/> + <name name="cspan" arity="2"/> <fsummary>Span characters at start of string</fsummary> - <type> - <v>String = Chars = string()</v> - <v>Length = integer()</v> - </type> <desc> <p>Returns the length of the maximum initial segment of - String, which consists entirely of characters from (not - from) Chars.</p> + <c><anno>String</anno></c>, which consists entirely of characters from (not + from) <c><anno>Chars</anno></c>.</p> <p>For example:</p> <code type="none"> > string:span("\t abcdef", " \t"). @@ -124,17 +101,13 @@ </desc> </func> <func> - <name>substr(String, Start) -> SubString</name> - <name>substr(String, Start, Length) -> Substring</name> + <name name="substr" arity="2"/> + <name name="substr" arity="3"/> <fsummary>Return a substring of <c>String</c></fsummary> - <type> - <v>String = SubString = string()</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Returns a substring of <c>String</c>, starting at the - position <c>Start</c>, and ending at the end of the string or - at length <c>Length</c>.</p> + <p>Returns a substring of <c><anno>String</anno></c>, starting at the + position <c><anno>Start</anno></c>, and ending at the end of the string or + at length <c><anno>Length</anno></c>.</p> <p>For example:</p> <code type="none"> > substr("Hello World", 4, 5). @@ -142,15 +115,11 @@ </desc> </func> <func> - <name>tokens(String, SeparatorList) -> Tokens</name> + <name name="tokens" arity="2"/> <fsummary>Split string into tokens</fsummary> - <type> - <v>String = SeparatorList = string()</v> - <v>Tokens = [string()]</v> - </type> <desc> - <p>Returns a list of tokens in <c>String</c>, separated by the - characters in <c>SeparatorList</c>.</p> + <p>Returns a list of tokens in <c><anno>String</anno></c>, separated by the + characters in <c><anno>SeparatorList</anno></c>.</p> <p>For example:</p> <code type="none"> > tokens("abc defxxghix jkl", "x "). @@ -158,15 +127,11 @@ </desc> </func> <func> - <name>join(StringList, Separator) -> String</name> + <name name="join" arity="2"/> <fsummary>Join a list of strings with separator</fsummary> - <type> - <v>StringList = [string()]</v> - <v>Separator = string()</v> - </type> <desc> - <p>Returns a string with the elements of <c>StringList</c> - separated by the string in <c>Seperator</c>.</p> + <p>Returns a string with the elements of <c><anno>StringList</anno></c> + separated by the string in <c><anno>Separator</anno></c>.</p> <p>For example:</p> <code type="none"> > join(["one", "two", "three"], ", "). @@ -174,44 +139,30 @@ </desc> </func> <func> - <name>chars(Character, Number) -> String</name> - <name>chars(Character, Number, Tail) -> String</name> + <name name="chars" arity="2"/> + <name name="chars" arity="3"/> <fsummary>Returns a string consisting of numbers of characters</fsummary> - <type> - <v>Character = char()</v> - <v>Number = integer()</v> - <v>String = string()</v> - </type> <desc> - <p>Returns a string consisting of <c>Number</c> of characters - <c>Character</c>. Optionally, the string can end with the - string <c>Tail</c>.</p> + <p>Returns a string consisting of <c><anno>Number</anno></c> of characters + <c><anno>Character</anno></c>. Optionally, the string can end with the + string <c><anno>Tail</anno></c>.</p> </desc> </func> <func> - <name>copies(String, Number) -> Copies</name> + <name name="copies" arity="2"/> <fsummary>Copy a string</fsummary> - <type> - <v>String = Copies = string()</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns a string containing <c>String</c> repeated - <c>Number</c> times.</p> + <p>Returns a string containing <c><anno>String</anno></c> repeated + <c><anno>Number</anno></c> times.</p> </desc> </func> <func> - <name>words(String) -> Count</name> - <name>words(String, Character) -> Count</name> + <name name="words" arity="1"/> + <name name="words" arity="2"/> <fsummary>Count blank separated words</fsummary> - <type> - <v>String = string()</v> - <v>Character = char()</v> - <v>Count = integer()</v> - </type> <desc> - <p>Returns the number of words in <c>String</c>, separated by - blanks or <c>Character</c>.</p> + <p>Returns the number of words in <c><anno>String</anno></c>, separated by + blanks or <c><anno>Character</anno></c>.</p> <p>For example:</p> <code type="none"> > words(" Hello old boy!", $o). @@ -219,17 +170,12 @@ </desc> </func> <func> - <name>sub_word(String, Number) -> Word</name> - <name>sub_word(String, Number, Character) -> Word</name> + <name name="sub_word" arity="2"/> + <name name="sub_word" arity="3"/> <fsummary>Extract subword</fsummary> - <type> - <v>String = Word = string()</v> - <v>Character = char()</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the word in position <c>Number</c> of <c>String</c>. - Words are separated by blanks or <c>Character</c>s.</p> + <p>Returns the word in position <c><anno>Number</anno></c> of <c><anno>String</anno></c>. + Words are separated by blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:sub_word(" Hello old boy !",3,$o). @@ -237,19 +183,14 @@ </desc> </func> <func> - <name>strip(String) -> Stripped</name> - <name>strip(String, Direction) -> Stripped</name> - <name>strip(String, Direction, Character) -> Stripped</name> + <name name="strip" arity="1"/> + <name name="strip" arity="2"/> + <name name="strip" arity="3"/> <fsummary>Strip leading or trailing characters</fsummary> - <type> - <v>String = Stripped = string()</v> - <v>Direction = left | right | both</v> - <v>Character = char()</v> - </type> <desc> <p>Returns a string, where leading and/or trailing blanks or a - number of <c>Character</c> have been removed. - <c>Direction</c> can be <c>left</c>, <c>right</c>, or + number of <c><anno>Character</anno></c> have been removed. + <c><anno>Direction</anno></c> can be <c>left</c>, <c>right</c>, or <c>both</c> and indicates from which direction blanks are to be removed. The function <c>strip/1</c> is equivalent to <c>strip(String, both)</c>.</p> @@ -260,19 +201,14 @@ </desc> </func> <func> - <name>left(String, Number) -> Left</name> - <name>left(String, Number, Character) -> Left</name> + <name name="left" arity="2"/> + <name name="left" arity="3"/> <fsummary>Adjust left end of string</fsummary> - <type> - <v>String = Left = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the <c>String</c> with the length adjusted in - accordance with <c>Number</c>. The left margin is - fixed. If the <c>length(String)</c> < <c>Number</c>, - <c>String</c> is padded with blanks or <c>Character</c>s.</p> + <p>Returns the <c><anno>String</anno></c> with the length adjusted in + accordance with <c><anno>Number</anno></c>. The left margin is + fixed. If the <c>length(<anno>String</anno>)</c> < <c><anno>Number</anno></c>, + <c><anno>String</anno></c> is padded with blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:left("Hello",10,$.). @@ -280,19 +216,14 @@ </desc> </func> <func> - <name>right(String, Number) -> Right</name> - <name>right(String, Number, Character) -> Right</name> + <name name="right" arity="2"/> + <name name="right" arity="3"/> <fsummary>Adjust right end of string</fsummary> - <type> - <v>String = Right = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the <c>String</c> with the length adjusted in - accordance with <c>Number</c>. The right margin is - fixed. If the length of <c>(String)</c> < <c>Number</c>, - <c>String</c> is padded with blanks or <c>Character</c>s.</p> + <p>Returns the <c><anno>String</anno></c> with the length adjusted in + accordance with <c><anno>Number</anno></c>. The right margin is + fixed. If the length of <c>(<anno>String</anno>)</c> < <c><anno>Number</anno></c>, + <c><anno>String</anno></c> is padded with blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:right("Hello", 10, $.). @@ -300,32 +231,23 @@ </desc> </func> <func> - <name>centre(String, Number) -> Centered</name> - <name>centre(String, Number, Character) -> Centered</name> + <name name="centre" arity="2"/> + <name name="centre" arity="3"/> <fsummary>Center a string</fsummary> - <type> - <v>String = Centered = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns a string, where <c>String</c> is centred in the + <p>Returns a string, where <c><anno>String</anno></c> is centred in the string and surrounded by blanks or characters. The resulting - string will have the length <c>Number</c>.</p> + string will have the length <c><anno>Number</anno></c>.</p> </desc> </func> <func> - <name>sub_string(String, Start) -> SubString</name> - <name>sub_string(String, Start, Stop) -> SubString</name> + <name name="sub_string" arity="2"/> + <name name="sub_string" arity="3"/> <fsummary>Extract a substring</fsummary> - <type> - <v>String = SubString = string()</v> - <v>Start = Stop = integer()</v> - </type> <desc> - <p>Returns a substring of <c>String</c>, starting at the - position <c>Start</c> to the end of the string, or to and - including the <c>Stop</c> position.</p> + <p>Returns a substring of <c><anno>String</anno></c>, starting at the + position <c><anno>Start</anno></c> to the end of the string, or to and + including the <c><anno>Stop</anno></c> position.</p> <p>For example:</p> <code type="none"> sub_string("Hello World", 4, 8). @@ -383,15 +305,15 @@ sub_string("Hello World", 4, 8). </desc> </func> <func> - <name>to_lower(String) -> Result</name> - <name>to_lower(Char) -> CharResult</name> - <name>to_upper(String) -> Result</name> - <name>to_upper(Char) -> CharResult</name> + <name name="to_lower" arity="1" clause_i="1"/> + <name name="to_lower" arity="1" clause_i="2"/> + <name name="to_upper" arity="1" clause_i="1"/> + <name name="to_upper" arity="1" clause_i="2"/> <fsummary>Convert case of string (ISO/IEC 8859-1)</fsummary> - <type> - <v>String = Result = string()</v> - <v>Char = CharResult = integer()</v> - </type> + <type variable="String" name_i="1"/> + <type variable="Result" name_i="1"/> + <type variable="Char"/> + <type variable="CharResult"/> <desc> <p>The given string or character is case-converted. Note that the supported character set is ISO/IEC 8859-1 (a.k.a. Latin 1), diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index d6203bdaa0..009aa60faa 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -199,51 +199,81 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </item> </list> </section> + <datatypes> + <datatype> + <name name="child"/> + </datatype> + <datatype> + <name name="child_id"/> + <desc><p>Not a <c>pid()</c>.</p></desc> + </datatype> + <datatype> + <name name="child_spec"/> + </datatype> + <datatype> + <name name="mfargs"/> + <desc><p><c>A</c> (the argument list) has the value + <c>undefined</c> if <c>Restart</c> is <c>temporary</c>.</p> + </desc> + </datatype> + <datatype> + <name name="modules"/> + </datatype> + <datatype> + <name name="restart"/> + </datatype> + <datatype> + <name name="shutdown"/> + </datatype> + <datatype> + <name name="strategy"/> + </datatype> + <datatype> + <name name="sup_ref"/> + </datatype> + <datatype> + <name name="worker"/> + </datatype> + </datatypes> <funcs> <func> - <name>start_link(Module, Args) -> Result</name> - <name>start_link(SupName, Module, Args) -> Result</name> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Create a supervisor process.</fsummary> - <type> - <v>SupName = {local,Name} | {global,Name}</v> - <v> Name = atom()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid}} | shutdown | term()</v> - </type> + <type name="startlink_ret"/> + <type name="startlink_err"/> + <type name="sup_name"/> <desc> <p>Creates a supervisor process as part of a supervision tree. The function will, among other things, ensure that the supervisor is linked to the calling process (its supervisor).</p> - <p>The created supervisor process calls <c>Module:init/1</c> to + <p>The created supervisor process calls <c><anno>Module</anno>:init/1</c> to find out about restart strategy, maximum restart frequency and child processes. To ensure a synchronized start-up procedure, <c>start_link/2,3</c> does not return until - <c>Module:init/1</c> has returned and all child processes + <c><anno>Module</anno>:init/1</c> has returned and all child processes have been started.</p> - <p>If <c>SupName={local,Name}</c> the supervisor is registered + <p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered locally as <c>Name</c> using <c>register/2</c>. If - <c>SupName={global,Name}</c> the supervisor is registered + <c><anno>SupName</anno>={global,Name}</c> the supervisor is registered globally as <c>Name</c> using <c>global:register_name/2</c>. If no name is provided, the supervisor is not registered.</p> - <p><c>Module</c> is the name of the callback module.</p> - <p><c>Args</c> is an arbitrary term which is passed as - the argument to <c>Module:init/1</c>.</p> + <p><c><anno>Module</anno></c> is the name of the callback module.</p> + <p><c><anno>Args</anno></c> is an arbitrary term which is passed as + the argument to <c><anno>Module</anno>:init/1</c>.</p> <p>If the supervisor and its child processes are successfully created (i.e. if all child process start functions return <c>{ok,Child}</c>, <c>{ok,Child,Info}</c>, or <c>ignore</c>) the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of the supervisor. If there already exists a process - with the specified <c>SupName</c> the function returns + with the specified <c><anno>SupName</anno></c> the function returns <c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is the pid of that process.</p> - <p>If <c>Module:init/1</c> returns <c>ignore</c>, this function + <p>If <c><anno>Module</anno>:init/1</c> returns <c>ignore</c>, this function returns <c>ignore</c> as well and the supervisor terminates with reason <c>normal</c>. - If <c>Module:init/1</c> fails or returns an incorrect value, + If <c><anno>Module</anno>:init/1</c> fails or returns an incorrect value, this function returns <c>{error,Term}</c> where <c>Term</c> is a term with information about the error, and the supervisor terminates with reason <c>Term</c>.</p> @@ -255,21 +285,15 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>start_child(SupRef, ChildSpec) -> Result</name> + <name name="start_child" arity="2"/> <fsummary>Dynamically add a child process to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>ChildSpec = child_spec() | [term()]</v> - <v>Result = {ok,Child} | {ok,Child,Info} | {error,Error}</v> - <v> Child = pid() | undefined</v> - <v> Info = term()</v> - <v> Error = already_present | {already_started,Child} | term()</v> - </type> + <type name="child_spec"/> + <type name="startchild_ret"/> + <type name="startchild_err"/> <desc> <p>Dynamically adds a child specification to the supervisor - <c>SupRef</c> which starts the corresponding child process.</p> - <p><c>SupRef</c> can be:</p> + <c><anno>SupRef</anno></c> which starts the corresponding child process.</p> + <p><marker id="SupRef"><c><anno>SupRef</anno></c></marker> can be:</p> <list type="bulleted"> <item>the pid,</item> <item><c>Name</c>, if the supervisor is locally registered,</item> @@ -278,26 +302,26 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <item><c>{global,Name}</c>, if the supervisor is globally registered.</item> </list> - <p><c>ChildSpec</c> should be a valid child specification + <p><c><anno>ChildSpec</anno></c> should be a valid child specification (unless the supervisor is a <c>simple_one_for_one</c> supervisor, see below). The child process will be started by using the start function as defined in the child specification.</p> <p>If the case of a <c>simple_one_for_one</c> supervisor, the child specification defined in <c>Module:init/1</c> will - be used and <c>ChildSpec</c> should instead be an arbitrary - list of terms <c>List</c>. The child process will then be - started by appending <c>List</c> to the existing start + be used and <c><anno>ChildSpec</anno></c> should instead be an arbitrary + list of terms <c><anno>List</anno></c>. The child process will then be + started by appending <c><anno>List</anno></c> to the existing start function arguments, i.e. by calling - <c>apply(M, F, A++List)</c> where <c>{M,F,A}</c> is the start + <c>apply(M, F, A++<anno>List</anno>)</c> where <c>{M,F,A}</c> is the start function defined in the child specification.</p> <p>If there already exists a child specification with - the specified <c>Id</c>, <c>ChildSpec</c> is discarded and + the specified <c><anno>Id</anno></c>, <c><anno>ChildSpec</anno></c> is discarded and the function returns <c>{error,already_present}</c> or - <c>{error,{already_started,Child}}</c>, depending on if + <c>{error,{already_started,<anno>Child</anno>}}</c>, depending on if the corresponding child process is running or not.</p> - <p>If the child process start function returns <c>{ok,Child}</c> - or <c>{ok,Child,Info}</c>, the child specification and pid is + <p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c> + or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the child specification and pid is added to the supervisor and the function returns the same value.</p> <p>If the child process start function returns <c>ignore</c>, @@ -312,27 +336,20 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>terminate_child(SupRef, Id) -> Result</name> + <name name="terminate_child" arity="2"/> <fsummary>Terminate a child process belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = pid() | term()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = not_found | simple_one_for_one</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to terminate the given + <p>Tells the supervisor <c><anno>SupRef</anno></c> to terminate the given child.</p> <p>If the supervisor is not <c>simple_one_for_one</c>, - <c>Id</c> must be the child specification identifier. The + <c><anno>Id</anno></c> must be the child specification identifier. The process, if there is one, is terminated but the child specification is kept by the supervisor. The child process may later be restarted by the supervisor. The child process can also be restarted explicitly by calling <c>restart_child/2</c>. Use <c>delete_child/2</c> to remove the child specification.</p> - <p>If the supervisor is <c>simple_one_for_one</c>, <c>Id</c> + <p>If the supervisor is <c>simple_one_for_one</c>, <c><anno>Id</anno></c> must be the child process' <c>pid()</c>. I the specified process is alive, but is not a child of the given supervisor, the function will return @@ -340,119 +357,93 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} identifier is given instead instead of a <c>pid()</c>, the function will return <c>{error,simple_one_for_one}</c>.</p> <p>If successful, the function returns <c>ok</c>. If there is - no child specification with the specified <c>Id</c>, the + no child specification with the specified <c><anno>Id</anno></c>, the function returns <c>{error,not_found}</c>.</p> <p>See <c>start_child/2</c> for a description of - <c>SupRef</c>.</p> + <c><anno>SupRef</anno></c>.</p> </desc> </func> <func> - <name>delete_child(SupRef, Id) -> Result</name> + <name name="delete_child" arity="2"/> <fsummary>Delete a child specification from a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = running | not_found | simple_one_for_one</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to delete the child - specification identified by <c>Id</c>. The corresponding child + <p>Tells the supervisor <c><anno>SupRef</anno></c> to delete the child + specification identified by <c><anno>Id</anno></c>. The corresponding child process must not be running, use <c>terminate_child/2</c> to terminate it.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> <p>If successful, the function returns <c>ok</c>. If the child - specification identified by <c>Id</c> exists but + specification identified by <c><anno>Id</anno></c> exists but the corresponding child process is running, the function returns <c>{error,running}</c>. If the child specification - identified by <c>Id</c> does not exist, the function returns + identified by <c><anno>Id</anno></c> does not exist, the function returns <c>{error,not_found}</c>.</p> </desc> </func> <func> - <name>restart_child(SupRef, Id) -> Result</name> + <name name="restart_child" arity="2"/> <fsummary>Restart a terminated child process belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term()</v> - <v>Result = {ok,Child} | {ok,Child,Info} | {error,Error}</v> - <v> Child = pid() | undefined</v> - <v> Error = running | not_found | simple_one_for_one | term()</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to restart a child process + <p>Tells the supervisor <c><anno>SupRef</anno></c> to restart a child process corresponding to the child specification identified by - <c>Id</c>. The child specification must exist and + <c><anno>Id</anno></c>. The child specification must exist and the corresponding child process must not be running.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> - <p>If the child specification identified by <c>Id</c> does not + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> + <p>If the child specification identified by <c><anno>Id</anno></c> does not exist, the function returns <c>{error,not_found}</c>. If the child specification exists but the corresponding process is already running, the function returns <c>{error,running}</c>.</p> - <p>If the child process start function returns <c>{ok,Child}</c> - or <c>{ok,Child,Info}</c>, the pid is added to the supervisor + <p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c> + or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the pid is added to the supervisor and the function returns the same value.</p> <p>If the child process start function returns <c>ignore</c>, the pid remains set to <c>undefined</c> and the function returns <c>{ok,undefined}</c>.</p> <p>If the child process start function returns an error tuple or an erroneous value, or if it fails, the function returns - <c>{error,Error}</c> where <c>Error</c> is a term containing + <c>{error,<anno>Error</anno>}</c> where <c><anno>Error</anno></c> is a term containing information about the error.</p> </desc> </func> <func> - <name>which_children(SupRef) -> [{Id,Child,Type,Modules}]</name> + <name name="which_children" arity="1"/> <fsummary>Return information about all children specifications and child processes belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term() | undefined</v> - <v>Child = pid() | undefined</v> - <v>Type = worker | supervisor</v> - <v>Modules = [Module] | dynamic</v> - <v> Module = atom()</v> - </type> <desc> <p>Returns a newly created list with information about all child specifications and child processes belonging to - the supervisor <c>SupRef</c>.</p> + the supervisor <c><anno>SupRef</anno></c>.</p> <p>Note that calling this function when supervising a large number of children under low memory conditions can cause an out of memory exception.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> <p>The information given for each child specification/process is:</p> <list type="bulleted"> <item> - <p><c>Id</c> - as defined in the child specification or + <p><c><anno>Id</anno></c> - as defined in the child specification or <c>undefined</c> in the case of a <c>simple_one_for_one</c> supervisor.</p> </item> <item> - <p><c>Child</c> - the pid of the corresponding child + <p><c><anno>Child</anno></c> - the pid of the corresponding child process, or <c>undefined</c> if there is no such process.</p> </item> <item> - <p><c>Type</c> - as defined in the child specification.</p> + <p><c><anno>Type</anno></c> - as defined in the child specification.</p> </item> <item> - <p><c>Modules</c> - as defined in the child specification.</p> + <p><c><anno>Modules</anno></c> - as defined in the child specification.</p> </item> </list> </desc> </func> <func> - <name>count_children(SupRef) -> PropListOfCounts</name> + <name name="count_children" arity="1"/> <fsummary>Return counts for the number of childspecs, active children, supervisors and workers.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>PropListOfCounts = [{specs, ChildSpecCount}, {active, ActiveProcessCount}, {supervisors, ChildSupervisorCount}, {workers, ChildWorkerCount}]</v> - </type> <desc> <p>Returns a property list (see <c>proplists</c>) containing the counts for each of the following elements of the supervisor's @@ -479,17 +470,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>check_childspecs([ChildSpec]) -> Result</name> + <name name="check_childspecs" arity="1"/> <fsummary>Check if children specifications are syntactically correct.</fsummary> - <type> - <v>ChildSpec = child_spec()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = term()</v> - </type> <desc> <p>This function takes a list of child specification as argument and returns <c>ok</c> if all of them are syntactically - correct, or <c>{error,Error}</c> otherwise.</p> + correct, or <c>{error,<anno>Error</anno>}</c> otherwise.</p> </desc> </func> </funcs> @@ -506,9 +492,9 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <type> <v>Args = term()</v> <v>Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore</v> - <v> RestartStrategy = one_for_all | one_for_one | rest_for_one | simple_one_for_one</v> - <v> MaxR = MaxT = int()>=0</v> - <v> ChildSpec = child_spec()</v> + <v> RestartStrategy = <seealso marker="#type-strategy">strategy()</seealso></v> + <v> MaxR = MaxT = integer()>=0</v> + <v> ChildSpec = <seealso marker="#type-child_spec">child_spec()</seealso></v> </type> <desc> <p>Whenever a supervisor is started using diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml index cbd0d9230b..c1a5e7947f 100644 --- a/lib/stdlib/doc/src/supervisor_bridge.xml +++ b/lib/stdlib/doc/src/supervisor_bridge.xml @@ -50,46 +50,37 @@ </description> <funcs> <func> - <name>start_link(Module, Args) -> Result</name> - <name>start_link(SupBridgeName, Module, Args) -> Result</name> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Create a supervisor bridge process.</fsummary> - <type> - <v>SupBridgeName = {local,Name} | {global,Name}</v> - <v> Name = atom()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid} | term()</v> - </type> <desc> <p>Creates a supervisor_bridge process, linked to the calling - process, which calls <c>Module:init/1</c> to start the subsystem. + process, which calls <c><anno>Module</anno>:init/1</c> to start the subsystem. To ensure a synchronized start-up procedure, this function does - not return until <c>Module:init/1</c> has returned.</p> - <p>If <c>SupBridgeName={local,Name}</c> the supervisor_bridge is - registered locally as <c>Name</c> using <c>register/2</c>. - If <c>SupBridgeName={global,Name}</c> the supervisor_bridge is - registered globally as <c>Name</c> using + not return until <c><anno>Module</anno>:init/1</c> has returned.</p> + <p>If <c><anno>SupBridgeName</anno>={local,<anno>Name</anno>}</c> the supervisor_bridge is + registered locally as <c><anno>Name</anno></c> using <c>register/2</c>. + If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c> the supervisor_bridge is + registered globally as <c><anno>Name</anno></c> using <c>global:register_name/2</c>. If no name is provided, the supervisor_bridge is not registered. If there already exists a process with the specified - <c>SupBridgeName</c> the function returns - <c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is the pid + <c><anno>SupBridgeName</anno></c> the function returns + <c>{error,{already_started,<anno>Pid</anno>}}</c>, where <c><anno>Pid</anno></c> is the pid of that process.</p> - <p><c>Module</c> is the name of the callback module.</p> - <p><c>Args</c> is an arbitrary term which is passed as the argument - to <c>Module:init/1</c>.</p> + <p><c><anno>Module</anno></c> is the name of the callback module.</p> + <p><c><anno>Args</anno></c> is an arbitrary term which is passed as the argument + to <c><anno>Module</anno>:init/1</c>.</p> <p>If the supervisor_bridge and the subsystem are successfully - started the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is + started the function returns <c>{ok,<anno>Pid</anno>}</c>, where <c><anno>Pid</anno></c> is is the pid of the supervisor_bridge.</p> - <p>If <c>Module:init/1</c> returns <c>ignore</c>, this function + <p>If <c><anno>Module</anno>:init/1</c> returns <c>ignore</c>, this function returns <c>ignore</c> as well and the supervisor_bridge terminates with reason <c>normal</c>. - If <c>Module:init/1</c> fails or returns an error tuple or an - incorrect value, this function returns <c>{error,Term}</c> where - <c>Term</c> is a term with information about the error, and - the supervisor_bridge terminates with reason <c>Term</c>.</p> + If <c><anno>Module</anno>:init/1</c> fails or returns an error tuple or an + incorrect value, this function returns <c>{error,<anno>Error</anno>r}</c> where + <c><anno>Error</anno></c> is a term with information about the error, and + the supervisor_bridge terminates with reason <c><anno>Error</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index efa8922a9d..073faf2df2 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -39,24 +39,12 @@ <p>Functions used for implementation of processes should also understand system messages such as debugging messages and code change. These functions must be used to implement the use of system messages for a process; either directly, or through standard behaviours, such as <c>gen_server</c>.</p> - <p>The following types are used in the functions defined below:</p> - <list type="bulleted"> - <item> - <p><c>Name = pid() | atom() | {global, atom()}</c></p> - </item> - <item> - <p><c>Timeout = int() >= 0 | infinity</c></p> - </item> - <item> - <p><c>system_event() = {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()</c></p> - </item> - </list> <p>The default timeout is 5000 ms, unless otherwise specified. The <c>timeout</c> defines the time period to wait for the process to respond to a request. If the process does not respond, the function evaluates <c>exit({timeout, {M, F, A}})</c>. </p> - <p>The functions make reference to a debug structure. + <p><marker id="dbg_opt"/>The functions make reference to a debug structure. The debug structure is a list of <c>dbg_opt()</c>. <c>dbg_opt()</c> is an internal data type used by the <c>handle_system_msg/6</c> function. No debugging is performed if it is an empty list. @@ -113,20 +101,31 @@ own system events. It is always up to the process itself to format these events.</p> </section> + <datatypes> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="system_event"/> + </datatype> + <datatype> + <name name="dbg_opt"/> + <desc><p>See <seealso marker="#dbg_opt">above</seealso>.</p></desc> + </datatype> + <datatype> + <name name="dbg_fun"/> + </datatype> + </datatypes> <funcs> <func> - <name>log(Name,Flag)</name> - <name>log(Name,Flag,Timeout) -> ok | {ok, [system_event()]}</name> + <name name="log" arity="2"/> + <name name="log" arity="3"/> <fsummary>Log system events in memory</fsummary> - <type> - <v>Flag = true | {true, N} | false | get | print</v> - <v>N = integer() > 0</v> - </type> <desc> <p>Turns the logging of system events On or Off. If On, a - maximum of <c>N</c> events are kept in the - debug structure (the default is 10). If <c>Flag</c> is <c>get</c>, a list of all - logged events is returned. If <c>Flag</c> is <c>print</c>, the + maximum of <c><anno>N</anno></c> events are kept in the + debug structure (the default is 10). If <c><anno>Flag</anno></c> is <c>get</c>, a list of all + logged events is returned. If <c><anno>Flag</anno></c> is <c>print</c>, the logged events are printed to <c>standard_io</c>. The events are formatted with a function that is defined by the process that generated the event (with a call to @@ -134,13 +133,9 @@ </desc> </func> <func> - <name>log_to_file(Name,Flag)</name> - <name>log_to_file(Name,Flag,Timeout) -> ok | {error, open_file}</name> + <name name="log_to_file" arity="2"/> + <name name="log_to_file" arity="3"/> <fsummary>Log system events to the specified file</fsummary> - <type> - <v>Flag = FileName | false</v> - <v>FileName = string()</v> - </type> <desc> <p>Enables or disables the logging of all system events in textual format to the file. The events are formatted with a function that is @@ -149,27 +144,18 @@ </desc> </func> <func> - <name>statistics(Name,Flag)</name> - <name>statistics(Name,Flag,Timeout) -> ok | {ok, Statistics} </name> + <name name="statistics" arity="2"/> + <name name="statistics" arity="3"/> <fsummary>Enable or disable the collections of statistics</fsummary> - <type> - <v>Flag = true | false | get</v> - <v>Statistics = [{start_time, {Date1, Time1}}, {current_time, {Date, Time2}}, {reductions, integer()}, {messages_in, integer()}, {messages_out, integer()}]</v> - <v>Date1 = Date2 = {Year, Month, Day}</v> - <v>Time1 = Time2 = {Hour, Min, Sec}</v> - </type> <desc> - <p>Enables or disables the collection of statistics. If <c>Flag</c> is + <p>Enables or disables the collection of statistics. If <c><anno>Flag</anno></c> is <c>get</c>, the statistical collection is returned.</p> </desc> </func> <func> - <name>trace(Name,Flag)</name> - <name>trace(Name,Flag,Timeout) -> void()</name> + <name name="trace" arity="2"/> + <name name="trace" arity="3"/> <fsummary>Print all system events on <c>standard_io</c></fsummary> - <type> - <v>Flag = boolean()</v> - </type> <desc> <p>Prints all system events on <c>standard_io</c>. The events are formatted with a function that is defined by the process that @@ -178,8 +164,8 @@ </desc> </func> <func> - <name>no_debug(Name)</name> - <name>no_debug(Name,Timeout) -> void()</name> + <name name="no_debug" arity="1"/> + <name name="no_debug" arity="2"/> <fsummary>Turn off debugging</fsummary> <desc> <p>Turns off all debugging for the process. This includes @@ -188,8 +174,8 @@ </desc> </func> <func> - <name>suspend(Name)</name> - <name>suspend(Name,Timeout) -> void()</name> + <name name="suspend" arity="1"/> + <name name="suspend" arity="2"/> <fsummary>Suspend the process</fsummary> <desc> <p>Suspends the process. When the process is suspended, it @@ -198,49 +184,37 @@ </desc> </func> <func> - <name>resume(Name)</name> - <name>resume(Name,Timeout) -> void()</name> + <name name="resume" arity="1"/> + <name name="resume" arity="2"/> <fsummary>Resume a suspended process</fsummary> <desc> <p>Resumes a suspended process.</p> </desc> </func> <func> - <name>change_code(Name, Module, OldVsn, Extra)</name> - <name>change_code(Name, Module, OldVsn, Extra, Timeout) -> ok | {error, Reason}</name> + <name name="change_code" arity="4"/> + <name name="change_code" arity="5"/> <fsummary>Send the code change system message to the process</fsummary> - <type> - <v>OldVsn = undefined | term()</v> - <v>Module = atom()</v> - <v>Extra = term()</v> - </type> <desc> <p>Tells the process to change code. The process must be - suspended to handle this message. The <c>Extra</c> argument is + suspended to handle this message. The <c><anno>Extra</anno></c> argument is reserved for each process to use as its own. The function - <c>Mod:system_code_change/4</c> is called. <c>OldVsn</c> is - the old version of the <c>Module</c>.</p> + <c><anno>Module</anno>:system_code_change/4</c> is called. <c><anno>OldVsn</anno></c> is + the old version of the <c><anno>Module</anno></c>.</p> </desc> </func> <func> - <name>get_status(Name)</name> - <name>get_status(Name,Timeout) -> {status, Pid, {module, Mod}, [PDict, SysState, Parent, Dbg, Misc]}</name> + <name name="get_status" arity="1"/> + <name name="get_status" arity="2"/> <fsummary>Get the status of the process</fsummary> - <type> - <v>PDict = [{Key, Value}]</v> - <v>SysState = running | suspended</v> - <v>Parent = pid()</v> - <v>Dbg = [dbg_opt()]</v> - <v>Misc = term()</v> - </type> <desc> <p>Gets the status of the process.</p> - <p>The value of <c>Misc</c> varies for different types of + <p>The value of <c><anno>Misc</anno></c> varies for different types of processes. For example, a <c>gen_server</c> process returns the callback module's state, and a <c>gen_fsm</c> process returns information such as its current state name. Callback modules for <c>gen_server</c> and <c>gen_fsm</c> can also - customise the value of <c>Misc</c> by exporting + customise the value of <c><anno>Misc</anno></c> by exporting a <c>format_status/2</c> function that contributes module-specific information; see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso> @@ -249,17 +223,9 @@ </desc> </func> <func> - <name>install(Name,{Func,FuncState})</name> - <name>install(Name,{Func,FuncState},Timeout)</name> + <name name="install" arity="2"/> + <name name="install" arity="3"/> <fsummary>Install a debug function in the process</fsummary> - <type> - <v>Func = dbg_fun()</v> - <v>dbg_fun() = fun(FuncState, Event, ProcState) -> done | NewFuncState</v> - <v>FuncState = term()</v> - <v>Event = system_event()</v> - <v>ProcState = term()</v> - <v>NewFuncState = term()</v> - </type> <desc> <p>This function makes it possible to install other debug functions than the ones defined above. An example of such a @@ -267,22 +233,19 @@ special event and performs some action when the event is generated. This could, for example, be turning on low level tracing. </p> - <p><c>Func</c> is called whenever a system event is + <p><c><anno>Func</anno></c> is called whenever a system event is generated. This function should return <c>done</c>, or a new func state. In the first case, the function is removed. It is removed if the function fails.</p> </desc> </func> <func> - <name>remove(Name,Func)</name> - <name>remove(Name,Func,Timeout) -> void()</name> + <name name="remove" arity="2"/> + <name name="remove" arity="3"/> <fsummary>Remove a debug function from the process</fsummary> - <type> - <v>Func = dbg_fun()</v> - </type> <desc> <p>Removes a previously installed debug function from the - process. <c>Func</c> must be the same as previously + process. <c><anno>Func</anno></c> must be the same as previously installed.</p> </desc> </func> @@ -296,86 +259,65 @@ </section> <funcs> <func> - <name>debug_options(Options) -> [dbg_opt()]</name> + <name name="debug_options" arity="1"/> <fsummary>Convert a list of options to a debug structure</fsummary> - <type> - <v>Options = [Opt]</v> - <v>Opt = trace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}}</v> - <v>Func = dbg_fun()</v> - <v>FuncState = term()</v> - </type> <desc> <p>This function can be used by a process that initiates a debug structure from a list of options. The values of the - <c>Opt</c> argument are the same as the corresponding + <c><anno>Opt</anno></c> argument are the same as the corresponding functions.</p> </desc> </func> <func> - <name>get_debug(Item,Debug,Default) -> term()</name> + <name name="get_debug" arity="3"/> <fsummary>Get the data associated with a debug option</fsummary> - <type> - <v>Item = log | statistics</v> - <v>Debug = [dbg_opt()]</v> - <v>Default = term()</v> - </type> <desc> - <p>This function gets the data associated with a debug option. <c>Default</c> is returned if the - <c>Item</c> is not found. Can be + <p>This function gets the data associated with a debug option. <c><anno>Default</anno></c> is returned if the + <c><anno>Item</anno></c> is not found. Can be used by the process to retrieve debug data for printing before it terminates.</p> </desc> </func> <func> - <name>handle_debug([dbg_opt()],FormFunc,Extra,Event) -> [dbg_opt()]</name> + <name name="handle_debug" arity="4"/> <fsummary>Generate a system event</fsummary> - <type> - <v>FormFunc = dbg_fun()</v> - <v>Extra = term()</v> - <v>Event = system_event()</v> - </type> <desc> - <p>This function is called by a process when it generates a system event. <c>FormFunc</c> is a formatting function which is called as <c>FormFunc(Device, Event, Extra)</c> in order to print the events, which is necessary if tracing is activated. <c>Extra</c> is any - extra information which the process needs in the format function, for example the name of the process.</p> + <p>This function is called by a process when it generates a + system event. <c><anno>FormFunc</anno></c> is a formatting + function which is called as <c><anno>FormFunc</anno>(Device, + <anno>Event</anno>, <anno>Extra</anno>)</c> in order to print + the events, which is necessary if tracing is activated. + <c><anno>Extra</anno></c> is any extra information which the + process needs in the format function, for example the name + of the process.</p> </desc> </func> <func> - <name>handle_system_msg(Msg,From,Parent,Module,Debug,Misc)</name> + <name name="handle_system_msg" arity="6"/> <fsummary>Take care of system messages</fsummary> - <type> - <v>Msg = term()</v> - <v>From = pid()</v> - <v>Parent = pid()</v> - <v>Module = atom()</v> - <v>Debug = [dbg_opt()]</v> - <v>Misc = term()</v> - </type> <desc> <p>This function is used by a process module that wishes to take care of system - messages. The process receives a <c>{system, From, Msg}</c> - message and passes the <c>Msg</c> and <c>From</c> to this + messages. The process receives a <c>{system, <anno>From</anno>, <anno>Msg</anno>}</c> + message and passes the <c><anno>Msg</anno></c> and <c><anno>From</anno></c> to this function. </p> <p>This function <em>never</em> returns. It calls the function - <c>Module:system_continue(Parent, NDebug, Misc)</c> where the + <c><anno>Module</anno>:system_continue(<anno>Parent</anno>, NDebug, <anno>Misc</anno>)</c> where the process continues the execution, or - <c>Module:system_terminate(Reason, Parent, Debug, Misc)</c> if - the process should terminate. The <c>Module</c> must export + <c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if + the process should terminate. The <c><anno>Module</anno></c> must export <c>system_continue/3</c>, <c>system_terminate/4</c>, and <c>system_code_change/4</c> (see below). </p> - <p>The <c>Misc</c> argument can be used to save internal data + <p>The <c><anno>Misc</anno></c> argument can be used to save internal data in a process, for example its state. It is sent to - <c>Module:system_continue/3</c> or - <c>Module:system_terminate/4</c></p> + <c><anno>Module</anno>:system_continue/3</c> or + <c><anno>Module</anno>:system_terminate/4</c></p> </desc> </func> <func> - <name>print_log(Debug) -> void()</name> + <name name="print_log" arity="1"/> <fsummary>Print the logged events in the debug structure</fsummary> - <type> - <v>Debug = [dbg_opt()]</v> - </type> <desc> <p>Prints the logged system events in the debug structure using <c>FormFunc</c> as defined when the event was @@ -383,11 +325,11 @@ </desc> </func> <func> - <name>Mod:system_continue(Parent, Debug, Misc)</name> + <name>Mod:system_continue(Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process should continue its execution</fsummary> <type> <v>Parent = pid()</v> - <v>Debug = [dbg_opt()]</v> + <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v> <v>Misc = term()</v> </type> <desc> @@ -397,12 +339,12 @@ </desc> </func> <func> - <name>Mod:system_terminate(Reason, Parent, Debug, Misc)</name> + <name>Mod:system_terminate(Reason, Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process should terminate</fsummary> <type> <v>Reason = term()</v> <v>Parent = pid()</v> - <v>Debug = [dbg_opt()]</v> + <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v> <v>Misc = term()</v> </type> <desc> diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index cae655f801..b741ab7db1 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -49,9 +49,19 @@ as requested. </p> </description> + <datatypes> + <datatype> + <name name="time"/> + <desc><p>Time in milliseconds.</p></desc> + </datatype> + <datatype> + <name name="tref"/> + <desc><p>A timer reference.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>start() -> ok</name> + <name name="start" arity="0"/> <fsummary>Start a global timer server (named <c>timer_server</c>).</fsummary> <desc> <p>Starts the timer server. Normally, the server does not need @@ -62,214 +72,181 @@ </desc> </func> <func> - <name>apply_after(Time, Module, Function, Arguments) -> {ok, Tref} | {error, Reason}</name> + <name name="apply_after" arity="4"/> <fsummary>Apply <c>Module:Function(Arguments)</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in Milliseconds</v> - <v>Module = Function = atom()</v> - <v>Arguments = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(M, F, A)</c> after <c>Time</c> amount of time - has elapsed. Returns <c>{ok, TRef}</c>, or <c>{error, Reason}</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> after <c><anno>Time</anno></c> amount of time + has elapsed. Returns <c>{ok, <anno>TRef</anno>}</c>, or <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>send_after(Time, Pid, Message) -> {ok, TRef} | {error,Reason}</name> - <name>send_after(Time, Message) -> {ok, TRef} | {error,Reason}</name> + <name name="send_after" arity="2"/> + <name name="send_after" arity="3"/> <fsummary>Send <c>Message</c>to <c>Pid</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in Milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Message = term()</v> - <v>Result = {ok, TRef} | {error, Reason}</v> - </type> <desc> <p></p> <taglist> <tag><c>send_after/3</c></tag> <item> - <p>Evaluates <c>Pid ! Message</c> after <c>Time</c> amount - of time has elapsed. (<c>Pid</c> can also be an atom of a - registered name.) Returns <c>{ok, TRef}</c>, or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c><anno>Pid</anno> ! <anno>Message</anno></c> after <c><anno>Time</anno></c> amount + of time has elapsed. (<c><anno>Pid</anno></c> can also be an atom of a + registered name.) Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason</anno>}</c>.</p> </item> <tag><c>send_after/2</c></tag> <item> - <p>Same as <c>send_after(Time, self(), Message)</c>.</p> + <p>Same as <c>send_after(<anno>Time</anno>, self(), <anno>Message</anno>)</c>.</p> </item> </taglist> </desc> </func> <func> - <name>exit_after(Time, Pid, Reason1) -> {ok, TRef} | {error,Reason2}</name> - <name>exit_after(Time, Reason1) -> {ok, TRef} | {error,Reason2}</name> - <name>kill_after(Time, Pid)-> {ok, TRef} | {error,Reason2}</name> - <name>kill_after(Time) -> {ok, TRef} | {error,Reason2}</name> + <name name="kill_after" arity="1"/> + <name name="kill_after" arity="2"/> + <name name="exit_after" arity="2"/> + <name name="exit_after" arity="3"/> <fsummary>Send an exit signal with <c>Reason</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Reason1 = Reason2 = term()</v> - </type> <desc> <p></p> <taglist> <tag><c>exit_after/3</c></tag> <item> - <p>Send an exit signal with reason <c>Reason1</c> to Pid - <c>Pid</c>. Returns <c>{ok, TRef}</c>, or - <c>{error, Reason2}</c>.</p> + <p>Send an exit signal with reason <c><anno>Reason1</anno></c> to Pid + <c><anno>Pid</anno></c>. Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason2</anno>}</c>.</p> </item> <tag><c>exit_after/2</c></tag> <item> - <p>Same as <c>exit_after(Time, self(), Reason1)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, self(), <anno>Reason1</anno>)</c>. </p> </item> <tag><c>kill_after/2</c></tag> <item> - <p>Same as <c>exit_after(Time, Pid, kill)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, <anno>Pid</anno>, kill)</c>. </p> </item> <tag><c>kill_after/1</c></tag> <item> - <p>Same as <c>exit_after(Time, self(), kill)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, self(), kill)</c>. </p> </item> </taglist> </desc> </func> <func> - <name>apply_interval(Time, Module, Function, Arguments) -> {ok, TRef} | {error, Reason}</name> + <name name="apply_interval" arity="4"/> <fsummary>Evaluate <c>Module:Function(Arguments)</c>repeatedly at intervals of <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Module = Function = atom()</v> - <v>Arguments = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> repeatedly at - intervals of <c>Time</c>. Returns <c>{ok, TRef}</c>, or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> repeatedly at + intervals of <c><anno>Time</anno></c>. Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>send_interval(Time, Pid, Message) -> {ok, TRef} | {error, Reason}</name> - <name>send_interval(Time, Message) -> {ok, TRef} | {error, Reason}</name> + <name name="send_interval" arity="2"/> + <name name="send_interval" arity="3"/> <fsummary>Send <c>Message</c>repeatedly at intervals of <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Message = term()</v> - <v>Reason = term()</v> - </type> <desc> <p></p> <taglist> <tag><c>send_interval/3</c></tag> <item> - <p>Evaluates <c>Pid ! Message</c> repeatedly after <c>Time</c> - amount of time has elapsed. (<c>Pid</c> can also be an atom of - a registered name.) Returns <c>{ok, TRef}</c> or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c><anno>Pid</anno> ! <anno>Message</anno></c> repeatedly after <c><anno>Time</anno></c> + amount of time has elapsed. (<c><anno>Pid</anno></c> can also be an atom of + a registered name.) Returns <c>{ok, <anno>TRef</anno>}</c> or + <c>{error, <anno>Reason</anno>}</c>.</p> </item> <tag><c>send_interval/2</c></tag> <item> - <p>Same as <c>send_interval(Time, self(), Message)</c>.</p> + <p>Same as <c>send_interval(<anno>Time</anno>, self(), <anno>Message</anno>)</c>.</p> </item> </taglist> </desc> </func> <func> - <name>cancel(TRef) -> {ok, cancel} | {error, Reason}</name> + <name name="cancel" arity="1"/> <fsummary>Cancel a previously requested timeout identified by <c>TRef</c>.</fsummary> <desc> - <p>Cancels a previously requested timeout. <c>TRef</c> is a unique + <p>Cancels a previously requested timeout. <c><anno>TRef</anno></c> is a unique timer reference returned by the timer function in question. Returns - <c>{ok, cancel}</c>, or <c>{error, Reason}</c> when <c>TRef</c> + <c>{ok, cancel}</c>, or <c>{error, <anno>Reason</anno>}</c> when <c><anno>TRef</anno></c> is not a timer reference.</p> </desc> </func> <func> - <name>sleep(Time) -> ok</name> + <name name="sleep" arity="1"/> <fsummary>Suspend the calling process for <c>Time</c>amount of milliseconds.</fsummary> - <type> - <v>Time = integer() in milliseconds or the atom infinity</v> - </type> <desc> - <p>Suspends the process calling this function for <c>Time</c> amount + <p>Suspends the process calling this function for <c><anno>Time</anno></c> amount of milliseconds and then returns <c>ok</c>, or suspend the process - forever if <c>Time</c> is the atom <c>infinity</c>. Naturally, this + forever if <c><anno>Time</anno></c> is the atom <c>infinity</c>. Naturally, this function does <em>not</em> return immediately.</p> </desc> </func> <func> - <name>tc(Module, Function, Arguments) -> {Time, Value}</name> - <name>tc(Fun, Arguments) -> {Time, Value}</name> + <name name="tc" arity="1"/> + <name name="tc" arity="2"/> + <name name="tc" arity="3"/> <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Fun = fun()</v> - <v>Arguments = [term()]</v> - <v>Time = integer() in microseconds</v> - <v>Value = term()</v> - </type> + <type_desc variable="Time">In microseconds</type_desc> <desc> <p></p> <taglist> <tag><c>tc/3</c></tag> <item> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures - the elapsed real time as reported by <c>now/0</c>. - Returns <c>{Time, Value}</c>, where - <c>Time</c> is the elapsed real time in <em>microseconds</em>, - and <c>Value</c> is what is returned from the apply.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> and measures + the elapsed real time as reported by <c>os:timestamp/0</c>. + Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where + <c><anno>Time</anno></c> is the elapsed real time in <em>microseconds</em>, + and <c><anno>Value</anno></c> is what is returned from the apply.</p> </item> <tag><c>tc/2</c></tag> <item> - <p>Evaluates <c>apply(Fun, Arguments)</c>. Otherwise works + <p>Evaluates <c>apply(<anno>Fun</anno>, <anno>Arguments</anno>)</c>. Otherwise works like <c>tc/3</c>.</p> </item> + <tag><c>tc/1</c></tag> + <item> + <p>Evaluates <c><anno>Fun</anno>()</c>. Otherwise works like <c>tc/2</c>.</p> + </item> + </taglist> </desc> </func> <func> - <name>now_diff(T2, T1) -> Tdiff</name> + <name name="now_diff" arity="2"/> <fsummary>Calculate time difference between <c>now/0</c>timestamps</fsummary> - <type> - <v>T1 = T2 = {MegaSecs, Secs, MicroSecs}</v> - <v>Tdiff = MegaSecs = Secs = MicroSecs = integer()</v> - </type> + <type_desc variable="Tdiff">In microseconds</type_desc> <desc> - <p>Calculates the time difference <c>Tdiff = T2 - T1</c> in - <em>microseconds</em>, where <c>T1</c> and <c>T2</c> probably + <p>Calculates the time difference <c><anno>Tdiff</anno> = <anno>T2</anno> - <anno>T1</anno></c> in + <em>microseconds</em>, where <c><anno>T1</anno></c> and <c><anno>T2</anno></c> probably are timestamp tuples returned from <c>erlang:now/0</c>.</p> </desc> </func> <func> - <name>seconds(Seconds) -> Milliseconds</name> + <name name="seconds" arity="1"/> <fsummary>Convert <c>Seconds</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Seconds</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Seconds</anno></c>.</p> </desc> </func> <func> - <name>minutes(Minutes) -> Milliseconds</name> + <name name="minutes" arity="1"/> <fsummary>Converts <c>Minutes</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Return the number of milliseconds in <c>Minutes</c>.</p> + <p>Return the number of milliseconds in <c><anno>Minutes</anno></c>.</p> </desc> </func> <func> - <name>hours(Hours) -> Milliseconds</name> + <name name="hours" arity="1"/> <fsummary>Convert <c>Hours</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Hours</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Hours</anno></c>.</p> </desc> </func> <func> - <name>hms(Hours, Minutes, Seconds) -> Milliseconds</name> + <name name="hms" arity="3"/> <fsummary>Convert <c>Hours</c>+<c>Minutes</c>+<c>Seconds</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Hours + Minutes + Seconds</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Hours</anno> + <anno>Minutes</anno> + <anno>Seconds</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index cb1cfa8ed0..d02763f75c 100644 --- a/lib/stdlib/doc/src/unicode.xml +++ b/lib/stdlib/doc/src/unicode.xml @@ -38,50 +38,83 @@ <p>It is recommended to only use external encodings for communication with external entities where this is required. When working inside the Erlang/OTP environment, it is recommended to keep binaries in UTF-8 when representing Unicode characters. Latin1 encoding is supported both for backward compatibility and for communication with external entities not supporting Unicode character sets.</p> </description> - <section> - <title>DATA TYPES</title> - <marker id="type-charlist"></marker> - <code type="none"> -unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint - -chardata() = charlist() | unicode_binary() - -charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list</code> - - <code type="none"> -external_unicode_binary() = binary() - with characters coded in a user specified Unicode encoding other - than UTF-8 (UTF-16 or UTF-32) - -external_chardata() = external_charlist() | external_unicode_binary() - -external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()] - an external_unicode_binary is allowed as the tail of the list</code> - - <code type="none"> -latin1_binary() = binary() with characters coded in iso-latin-1 -latin1_char() = integer() representing valid latin1 character (0-255) - -latin1_chardata() = latin1_charlist() | latin1_binary() + <datatypes> + <datatype> + <name name="encoding"/> + </datatype> + <datatype> + <name name="endian"/> + </datatype> + <datatype> + <name name="unicode_binary"/> + <desc> + <p>A binary() with characters encoded in the UTF-8 coding standard.</p> + </desc> + </datatype> + <datatype> + <name name="unicode_char"/> + <desc> + <p>An integer() representing a valid unicode codepoint.</p> + </desc> + </datatype> + <datatype> + <name name="chardata"/> + </datatype> + <datatype> + <name name="charlist"/> + <desc> + <p>A unicode_binary is allowed as the tail of the list.</p> + </desc> + </datatype> + <datatype> + <name name="external_unicode_binary"/> + <desc> + <p>A <c>binary()</c> with characters coded in a user specified Unicode + encoding other than UTF-8 (UTF-16 or UTF-32).</p> + </desc> + </datatype> + <datatype> + <name name="external_chardata"/> + </datatype> + <datatype> + <name name="external_charlist"/> + <desc> + <p>An <c>external_unicode_binary()</c> is allowed as the tail + of the list.</p> + </desc> + </datatype> + <datatype> + <name name="latin1_binary"/> + <desc><p>A <c>binary()</c> with characters coded in iso-latin-1.</p> + </desc> + </datatype> + <datatype> + <name name="latin1_char"/> + <desc><p>An <c>integer()</c> representing valid latin1 + character (0-255).</p> + </desc> + </datatype> + <datatype> + <name name="latin1_chardata"/> + </datatype> + <datatype> + <name name="latin1_charlist"/> + <desc><p>A <c>latin1_binary()</c> is allowed as the tail of + the list.</p> + </desc> + </datatype> + </datatypes> -latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] - a latin1_binary is allowed as the tail of the list</code> - </section> <funcs> <func> - <name>bom_to_encoding(Bin) -> {Encoding,Length}</name> + <name name="bom_to_encoding" arity="1"/> <fsummary>Identify UTF byte order marks in a binary.</fsummary> - <type> - <v>Bin = binary() of byte_size 4 or more</v> - <v>Encoding = latin1 | utf8 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>Length = int()</v> - </type> + <type name="endian"/> + <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> <desc> <p>Check for a UTF byte order mark (BOM) in the beginning of a - binary. If the supplied binary <c>Bin</c> begins with a valid + binary. If the supplied binary <c><anno>Bin</anno></c> begins with a valid byte order mark for either UTF-8, UTF-16 or UTF-32, the function returns the encoding identified along with the length of the BOM in bytes.</p> @@ -90,23 +123,24 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>characters_to_list(Data) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} </name> + <name name="characters_to_list" arity="1"/> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> - <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - </type> <desc> - <p>Same as characters_to_list(Data,unicode).</p> + <p>Same as characters_to_list(<anno>Data</anno>,unicode).</p> </desc> </func> <func> - <name>characters_to_list(Data, InEncoding) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} </name> + <name>characters_to_list(Data, InEncoding) -> Result</name> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> + <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> + | <seealso marker="#type-chardata">chardata()</seealso> + | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> + <v>Result = list() | {error, list(), RestData} | {incomplete, list(), binary()}</v> + <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> + | <seealso marker="#type-chardata">chardata()</seealso> + | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> + <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v> </type> <desc> @@ -234,44 +268,42 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>characters_to_binary(Data) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - </type> + <name name="characters_to_binary" arity="1"/> + <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <desc> <p>Same as characters_to_binary(Data, unicode, unicode).</p> </desc> </func> <func> - <name>characters_to_binary(Data,InEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> + <name>characters_to_binary(Data,InEncoding) -> Result</name> + <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + + <type> + <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> + | <seealso marker="#type-chardata">chardata()</seealso> + | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> + <v>Result = binary() | {error, binary(), RestData} | {incomplete, binary(), binary()}</v> + <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> + | <seealso marker="#type-chardata">chardata()</seealso> + | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> + <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v> </type> <desc> <p>Same as characters_to_binary(Data, InEncoding, unicode).</p> </desc> </func> <func> - <name>characters_to_binary(Data, InEncoding, OutEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> + <name name="characters_to_binary" arity="3"/> <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> - <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>OutEncoding = latin1 | unicode | utf8 | utf16 | utf32| {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - </type> <desc> <p>This function behaves as <seealso marker="#characters_to_list/2"> characters_to_list/2</seealso>, but produces an binary instead of a unicode list. The - <c>InEncoding</c> defines how input is to be interpreted if + <c><anno>InEncoding</anno></c> defines how input is to be interpreted if binaries are present in the <c>Data</c>, while - <c>OutEncoding</c> defines in what format output is to be + <c><anno>OutEncoding</anno></c> defines in what format output is to be generated.</p> <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the @@ -291,17 +323,13 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>encoding_to_bom(InEncoding) -> Bin</name> + <name name="encoding_to_bom" arity="1"/> <fsummary>Create a binary UTF byte order mark from encoding.</fsummary> - <type> - <v>Bin = binary() of byte_size 4 or less</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>Length = int()</v> - </type> + <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> <desc> <p>Create an UTF byte order mark (BOM) as a binary from the - supplied <c>InEncoding</c>. The BOM is, if supported at all, + supplied <c><anno>InEncoding</anno></c>. The BOM is, if supported at all, expected to be placed first in UTF encoded files or messages.</p> diff --git a/lib/stdlib/doc/src/win32reg.xml b/lib/stdlib/doc/src/win32reg.xml index 28960cd098..99fd7fdeb6 100644 --- a/lib/stdlib/doc/src/win32reg.xml +++ b/lib/stdlib/doc/src/win32reg.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -85,14 +85,22 @@ hkdd HKEY_DYN_DATA</pre> <p>For additional information on the Windows registry consult the Win32 Programmer's Reference.</p> </description> + <datatypes> + <datatype> + <name name="reg_handle"/> + <desc><p>As returned by <seealso marker="#open/1">open/1</seealso>.</p></desc> + </datatype> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="value"/> + </datatype> + </datatypes> <funcs> <func> - <name>change_key(RegHandle, Key) -> ReturnValue</name> + <name name="change_key" arity="2"/> <fsummary>Move to a key in the registry</fsummary> - <type> - <v>RegHandle = term()</v> - <v>Key = string()</v> - </type> <desc> <p>Changes the current key to another key. Works like cd. The key can be specified as a relative path or as an @@ -100,12 +108,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>change_key_create(RegHandle, Key) -> ReturnValue</name> + <name name="change_key_create" arity="2"/> <fsummary>Move to a key, create it if it is not there</fsummary> - <type> - <v>RegHandle = term()</v> - <v>Key = string()</v> - </type> <desc> <p>Creates a key, or just changes to it, if it is already there. Works like a combination of <c>mkdir</c> and <c>cd</c>. Calls the Win32 API function @@ -114,23 +118,16 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>close(RegHandle)-> ReturnValue</name> + <name name="close" arity="1"/> <fsummary>Close the registry.</fsummary> - <type> - <v>RegHandle = term()</v> - </type> <desc> - <p>Closes the registry. After that, the <c>RegHandle</c> cannot + <p>Closes the registry. After that, the <c><anno>RegHandle</anno></c> cannot be used.</p> </desc> </func> <func> - <name>current_key(RegHandle) -> ReturnValue</name> + <name name="current_key" arity="1"/> <fsummary>Return the path to the current key.</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = {ok, string()}</v> - </type> <desc> <p>Returns the path to the current key. This is the equivalent of <c>pwd</c>.</p> <p>Note that the current key is stored in the driver, and might be @@ -138,12 +135,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>delete_key(RegHandle) -> ReturnValue</name> + <name name="delete_key" arity="1"/> <fsummary>Delete the current key</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = ok | {error, ErrorId}</v> - </type> <desc> <p>Deletes the current key, if it is valid. Calls the Win32 API function <c>RegDeleteKey()</c>. Note that this call does not change the current key, @@ -152,12 +145,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>delete_value(RegHandle, Name) -> ReturnValue</name> + <name name="delete_value" arity="2"/> <fsummary>Delete the named value on the current key.</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = ok | {error, ErrorId}</v> - </type> <desc> <p>Deletes a named value on the current key. The atom <c>default</c> is used for the the default value.</p> @@ -165,12 +154,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>expand(String) -> ExpandedString</name> + <name name="expand" arity="1"/> <fsummary>Expand a string with environment variables</fsummary> - <type> - <v>String = string()</v> - <v>ExpandedString = string()</v> - </type> <desc> <p>Expands a string containing environment variables between percent characters. Anything between two % is taken for a environment @@ -180,23 +165,15 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>format_error(ErrorId) -> ErrorString</name> + <name name="format_error" arity="1"/> <fsummary>Convert an POSIX errorcode to a string</fsummary> - <type> - <v>ErrorId = atom()</v> - <v>ErrorString = string()</v> - </type> <desc> <p>Convert an POSIX errorcode to a string (by calling <c>erl_posix_msg:message</c>).</p> </desc> </func> <func> - <name>open(OpenModeList)-> ReturnValue</name> + <name name="open" arity="1"/> <fsummary>Open the registry for reading or writing</fsummary> - <type> - <v>OpenModeList = [OpenMode]</v> - <v>OpenMode = read | write</v> - </type> <desc> <p>Opens the registry for reading or writing. The current key will be the root (<c>HKEY_CLASSES_ROOT</c>). The <c>read</c> flag in the mode list can be omitted.</p> @@ -204,12 +181,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>set_value(RegHandle, Name, Value) -> ReturnValue</name> + <name name="set_value" arity="3"/> <fsummary>Set value at the current registry key with specified name.</fsummary> - <type> - <v>Name = string() | default</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Sets the named (or default) value to value. Calls the Win32 API function <c>RegSetValueEx()</c>. The value can be of three types, and @@ -221,13 +194,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>sub_keys(RegHandle) -> ReturnValue</name> + <name name="sub_keys" arity="1"/> <fsummary>Get subkeys to the current key.</fsummary> - <type> - <v>ReturnValue = {ok, SubKeys} | {error, ErrorId}</v> - <v>SubKeys = [SubKey]</v> - <v>SubKey = string()</v> - </type> <desc> <p>Returns a list of subkeys to the current key. Calls the Win32 API function <c>EnumRegKeysEx()</c>.</p> @@ -235,13 +203,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>value(RegHandle, Name) -> ReturnValue</name> + <name name="value" arity="2"/> <fsummary>Get the named value on the current key.</fsummary> - <type> - <v>Name = string() | default</v> - <v>ReturnValue = {ok, Value}</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Retrieves the named value (or default) on the current key. Registry values of type <c>REG_SZ</c>, are returned as strings. Type <c>REG_DWORD</c> @@ -249,15 +212,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>values(RegHandle) -> ReturnValue</name> + <name name="values" arity="1"/> <fsummary>Get all values on the current key.</fsummary> - <type> - <v>ReturnValue = {ok, ValuePairs} | {ok, ErrorId}</v> - <v>ValuePairs = [ValuePair]</v> - <v>ValuePair = {Name, Value}</v> - <v>Name = string | default</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Retrieves a list of all values on the current key. The values have types corresponding to the registry types, see <c>value</c>. diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index 529a70a23d..b03fc7f4e2 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2006</year><year>2010</year> + <year>2006</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -85,67 +85,55 @@ recreated.</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -zip_file() </code> - <p>The record <c>zip_file</c> contains the following fields.</p> - <taglist> - <tag><c>name = string()</c></tag> - <item> - <p>the name of the file</p> - </item> - <tag><c>info = file_info()</c></tag> - <item> - <p>file info as in - <seealso marker="erts:file#read_file_info/1">file:read_file_info/1</seealso></p> - </item> - <tag><c>comment = string()</c></tag> - <item> - <p>the comment for the file in the zip archive</p> - </item> - <tag><c>offset = integer()</c></tag> - <item> - <p>the offset of the file in the zip archive (used internally)</p> - </item> - <tag><c>comp_size = integer()</c></tag> - <item> - <p>the compressed size of the file (the uncompressed size is found - in <c>info</c>)</p> - </item> - </taglist> - <code type="none">zip_comment</code> - <p>The record <c>zip_comment</c> just contains the archive comment for - a zip archive</p> - <taglist> - <tag><c>comment = string()</c></tag> - <item> - <p>the comment for the zip archive</p> - </item> - </taglist> - </section> + <datatypes> + <datatype> + <name name="zip_comment"/> + <desc> + <p>The record <c>zip_comment</c> just contains the archive comment for + a zip archive</p> + </desc> + </datatype> + <datatype> + <name name="zip_file"/> + <desc> + <p>The record <c>zip_file</c> contains the following fields.</p> + <taglist> + <tag><c>name</c></tag> + <item> + <p>the name of the file</p> + </item> + <tag><c>info</c></tag> + <item> + <p>file info as in + <seealso marker="kernel:file#read_file_info/1">file:read_file_info/1</seealso></p> + </item> + <tag><c>comment</c></tag> + <item> + <p>the comment for the file in the zip archive</p> + </item> + <tag><c>offset</c></tag> + <item> + <p>the offset of the file in the zip archive (used internally)</p> + </item> + <tag><c>comp_size</c></tag> + <item> + <p>the compressed size of the file (the uncompressed size is found + in <c>info</c>)</p> + </item> + </taglist> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>zip(Name, FileList) -> RetValue</name> - <name>zip(Name, FileList, Options) -> RetValue</name> - <name>create(Name, FileList) -> RetValue</name> - <name>create(Name, FileList, Options) -> RetValue</name> + <name name="zip" arity="2"/> + <name name="zip" arity="3"/> + <name name="create" arity="2"/> + <name name="create" arity="3"/> <fsummary>Create a zip archive with options</fsummary> - <type> - <v>Name = filename()</v> - <v>FileList = [FileSpec]</v> - <v>FileSpec = filename() | {filename(), binary()} | {filename(), binary(), #file_info{}}</v> - <v>Options = [Option]</v> - <v>Option = memory | cooked | verbose | {comment, Comment} | {cwd, CWD} | {compress, What} | {uncompress, What}</v> - <v>What = all | [Extension] | {add, [Extension]} | {del, [Extension]}</v> - <v>Extension = string()</v> - <v>Comment = CWD = string()</v> - <v>RetValue = {ok, Name} | {ok, {Name, binary()}} | {error, Reason}</v> - <v>Reason = term()</v> - </type> <desc> <p>The <marker id="zip_2"></marker><c>zip</c> function creates a - zip archive containing the files specified in <c>FileList</c>.</p> + zip archive containing the files specified in <c><anno>FileList</anno></c>.</p> <p>As synonyms, the functions <c>create/2</c> and <c>create/3</c> are provided, to make it resemble the <c>erl_tar</c> module.</p> <p>The file-list is a list of files, with paths relative to the @@ -161,9 +149,9 @@ zip_file() </code> <c>.Z</c>, <c>.zip</c>, <c>.zoo</c>, <c>.arc</c>, <c>.lzh</c>, <c>.arj</c>.</p> <p>It is possible to override the default behavior and - explicitly control what types of files should be - compressed by using the <c>{compress, What}</c> and - <c>{uncompress, What}</c> options. It is possible to have + explicitly control what types of files that should be + compressed by using the <c>{compress, <anno>What</anno>}</c> and + <c>{uncompress, <anno>What</anno>}</c> options. It is possible to have several <c>compress</c> and <c>uncompress</c> options. In order to trigger compression of a file, its extension must match with the @@ -191,22 +179,22 @@ zip_file() </code> <tag><c>memory</c></tag> <item> <p>The output will not be to a file, but instead as a tuple - <c>{FileName, binary()}</c>. The binary will be a full zip + <c>{<anno>FileName</anno>, binary()}</c>. The binary will be a full zip archive with header, and can be extracted with for instance <c>unzip/2</c>.</p> </item> - <tag><c>{comment, Comment}</c></tag> + <tag><c>{comment, <anno>Comment</anno>}</c></tag> <item> <p>Add a comment to the zip-archive.</p> </item> - <tag><c>{cwd, CWD}</c></tag> + <tag><c>{cwd, <anno>CWD</anno>}</c></tag> <item> <p>Use the given directory as current directory, it will be prepended to file names when adding them, although it will not be in the zip-archive. (Acting like a file:set_cwd/1, but without changing the global cwd property.)</p> </item> - <tag><c>{compress, What}</c></tag> + <tag><c>{compress, <anno>What</anno>}</c></tag> <item> <p>Controls what types of files will be compressed. It is by default set to <c>all</c>. The @@ -215,18 +203,18 @@ zip_file() </code> <tag><c>all</c></tag> <item><p> means that all files will be compressed (as long as they pass the <c>uncompress</c> condition).</p></item> - <tag><c>[Extension]</c></tag> + <tag><c>[<anno>Extension</anno>]</c></tag> <item><p>means that only files with exactly these extensions will be compressed.</p></item> - <tag><c>{add,[Extension]}</c></tag> + <tag><c>{add,[<anno>Extension</anno>]}</c></tag> <item><p>adds these extensions to the list of compress extensions.</p></item> - <tag><c>{del,[Extension]}</c></tag> + <tag><c>{del,[<anno>Extension</anno>]}</c></tag> <item><p>deletes these extensions from the list of compress extensions.</p></item> </taglist> </item> - <tag><c>{uncompress, What}</c></tag> + <tag><c>{uncompress, <anno>What</anno>}</c></tag> <item> <p>Controls what types of files will be uncompressed. It is by default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>. @@ -234,13 +222,13 @@ zip_file() </code> <taglist> <tag><c>all</c></tag> <item><p> means that no files will be compressed.</p></item> - <tag><c>[Extension]</c></tag> + <tag><c>[<anno>Extension</anno>]</c></tag> <item><p>means that files with these extensions will be uncompressed.</p></item> - <tag><c>{add,[Extension]}</c></tag> + <tag><c>{add,[<anno>Extension</anno>]}</c></tag> <item><p>adds these extensions to the list of uncompress extensions.</p></item> - <tag><c>{del,[Extension]}</c></tag> + <tag><c>{del,[<anno>Extension</anno>]}</c></tag> <item><p>deletes these extensions from the list of uncompress extensions.</p></item> </taglist> @@ -249,23 +237,11 @@ zip_file() </code> </desc> </func> <func> - <name>unzip(Archive) -> RetValue</name> - <name>unzip(Archive, Options) -> RetValue</name> - <name>extract(Archive) -> RetValue</name> - <name>extract(Archive, Options) -> RetValue</name> + <name name="unzip" arity="1"/> + <name name="unzip" arity="2"/> + <name name="extract" arity="1"/> + <name name="extract" arity="2"/> <fsummary>Extract files from a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>Options = [Option]</v> - <v>Option = {file_list, FileList} | keep_old_files | verbose | memory | {file_filter, FileFilter} | {cwd, CWD}</v> - <v>FileList = [filename()]</v> - <v>FileBinList = [{filename(),binary()}]</v> - <v>FileFilter = fun(ZipFile) -> true | false</v> - <v>CWD = string()</v> - <v>ZipFile = zip_file()</v> - <v>RetValue = {ok,FileList} | {ok,FileBinList} | {error, Reason} | {error, {Name, Reason}}</v> - <v>Reason = term()</v> - </type> <desc> <p>The <marker id="unzip_1"></marker> <c>unzip/1</c> function extracts @@ -273,17 +249,17 @@ zip_file() </code> <marker id="unzip_2"></marker> <c>unzip/2</c> function provides options to extract some files, and more.</p> - <p>If the <c>Archive</c> argument is given as a binary, + <p>If the <c><anno>Archive</anno></c> argument is given as a binary, the contents of the binary is assumed to be a zip archive, otherwise it should be a filename.</p> <p>The following options are available:</p> <taglist> - <tag><c>{file_list, FileList}</c></tag> + <tag><c>{file_list, <anno>FileList</anno>}</c></tag> <item> <p>By default, all files will be extracted from the zip - archive. With the <c>{file_list,FileList}</c> option, + archive. With the <c>{file_list, <anno>FileList</anno>}</c> option, the <c>unzip/2</c> function will only extract the files - whose names are included in <c>FileList</c>. The full + whose names are included in <c><anno>FileList</anno></c>. The full paths, including the names of all sub directories within the zip archive, must be specified.</p> </item> @@ -329,29 +305,29 @@ zip_file() </code> </desc> </func> <func> - <name>foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason}</name> + <name name="foldl" arity="3"/> <fsummary>Fold a function over all files in a zip archive</fsummary> - <type> - <v>Fun = fun(FileInArchive, GetInfo, GetBin, AccIn) -> AccOut</v> - <v>FileInArchive = filename()</v> - <v>GetInfo = fun() -> #file_info{}</v> - <v>GetBin = fun() -> binary()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Archive = filename() | {filename(), binary()}</v> - </type> <desc> <p>The <marker id="foldl_3"></marker> <c>foldl/3</c> function - calls <c>Fun(FileInArchive, GetInfo, GetBin, AccIn)</c> on - successive files in the <c>Archive</c>, starting with <c>AccIn - == Acc0</c>. <c>FileInArchive</c> is the name that the file - has in the archive. <c>GetInfo</c> is a fun that returns info - about the the file. <c>GetBin</c> returns the contents of the - file. Both <c>GetInfo</c> and <c>GetBin</c> must be called - within the <c>Fun</c>. Their behavior is undefined if they are - called outside the context of the <c>Fun</c>. The <c>Fun</c> + calls <c><anno>Fun</anno>(<anno>FileInArchive</anno>, <anno>GetInfo + </anno>, <anno>GetBin</anno>, <anno>AccIn</anno>)</c> on + successive files in the <c>Archive</c>, starting with + <c><anno>AccIn</anno> + == <anno>Acc0</anno></c>. <c><anno>FileInArchive</anno></c> is + the name that the file + has in the archive. <c><anno>GetInfo</anno></c> is a fun that + returns info + about the the file. <c><anno>GetBin</anno></c> returns the contents + of the + file. Both <c><anno>GetInfo</anno></c> and <c><anno>GetBin</anno></c> + must be called + within the <c><anno>Fun</anno></c>. Their behavior is undefined if + they are + called outside the context of the <c><anno>Fun</anno></c>. + The <c><anno>Fun</anno></c> must return a new accumulator which is passed to the next call. <c>foldl/3</c> returns the final value of the - accumulator. <c>Acc0</c> is returned if the archive is + accumulator. <c><anno>Acc0</anno></c> is returned if the archive is empty. It is not necessary to iterate over all files in the archive. The iteration may be ended prematurely in a controlled manner by throwing an exception.</p> @@ -387,26 +363,16 @@ zip_file() </code> </desc> </func> <func> - <name>list_dir(Archive) -> RetValue</name> - <name>list_dir(Archive, Options)</name> - <name>table(Archive) -> RetValue</name> - <name>table(Archive, Options)</name> + <name name="list_dir" arity="1"/> + <name name="list_dir" arity="2"/> + <name name="table" arity="1" /> + <name name="table" arity="2"/> <fsummary>Retrieve the name of all files in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>RetValue = {ok, [Comment, Files]} | {error, Reason}</v> - <v>Comment = zip_comment()</v> - <v>Files = [zip_file()]</v> - <v>Options = [Option]</v> - <v>Option = cooked</v> - <v>Reason = term()</v> - </type> <desc> - <p>The <marker id="list_dir_1"></marker> -<c>list_dir/1</c> function retrieves - the names of all files in the zip archive <c>Archive</c>. The - <marker id="list_dir_2"></marker> -<c>list_dir/2</c> function provides options.</p> + <p>The <marker id="list_dir_1"></marker><c>list_dir/1</c> + function retrieves the names of all files in the zip archive + <c><anno>Archive</anno></c>. The <marker id="list_dir_2"></marker> + <c>list_dir/2</c> function provides options.</p> <p>As synonyms, the functions <c>table/2</c> and <c>table/3</c> are provided, to make it resemble the <c>erl_tar</c> module.</p> <p>The result value is the tuple <c>{ok, List}</c>, where <c>List</c> @@ -425,43 +391,27 @@ zip_file() </code> </desc> </func> <func> - <name>t(Archive)</name> + <name name="t" arity="1"/> <fsummary>Print the name of each file in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary() | ZipHandle</v> - <v>ZipHandle = pid()</v> - </type> <desc> - <p>The <marker id="t_1"></marker> -<c>t/1</c> function prints the names - of all files in the zip archive <c>Archive</c> to the Erlang shell. + <p>The <marker id="t_1"></marker><c>t/1</c> function prints the names + of all files in the zip archive <c><anno>Archive</anno></c> to the Erlang shell. (Similar to "<c>tar t</c>".)</p> </desc> </func> <func> - <name>tt(Archive)</name> + <name name="tt" arity="1"/> <fsummary>Print name and information for each file in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - </type> <desc> - <p>The <marker id="tt_1"></marker> -<c>tt/1</c> function prints names and - information about all files in the zip archive <c>Archive</c> to + <p>The <marker id="tt_1"></marker><c>tt/1</c> function prints names and + information about all files in the zip archive <c><anno>Archive</anno></c> to the Erlang shell. (Similar to "<c>tar tv</c>".)</p> </desc> </func> <func> - <name>zip_open(Archive) -> {ok, ZipHandle} | {error, Reason}</name> - <name>zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason}</name> + <name name="zip_open" arity="1"/> + <name name="zip_open" arity="2"/> <fsummary>Open an archive and return a handle to it</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>Options = [Option]</v> - <v>Options = cooked | memory | {cwd, CWD}</v> - <v>CWD = string()</v> - <v>ZipHandle = pid()</v> - </type> <desc> <p>The <marker id="zip_open"></marker> <c>zip_open</c> function opens a @@ -472,29 +422,19 @@ zip_file() </code> </desc> </func> <func> - <name>zip_list_dir(ZipHandle) -> Result | {error, Reason}</name> + <name name="zip_list_dir" arity="1"/> <fsummary>Return a table of files in open zip archive</fsummary> - <type> - <v>Result = [ZipComment, ZipFile...]</v> - <v>ZipComment = #zip_comment{}</v> - <v>ZipFile = #zip_file{}</v> - <v>ZipHandle = pid()</v> - </type> <desc> - <p>The <marker id="zip_list_dir"></marker> -<c>zip_list_dir/1</c> function - returns the file list of an open zip archive.</p> + <p>The <marker id="zip_list_dir"></marker> + <c>zip_list_dir/1</c> function + returns the file list of an open zip archive. The first returned + element is the zip archive comment.</p> </desc> </func> <func> - <name>zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason}</name> - <name>zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason}</name> + <name name="zip_get" arity="1"/> + <name name="zip_get" arity="2"/> <fsummary>Extract files from an open archive</fsummary> - <type> - <v>FileName = filename()</v> - <v>ZipHandle = pid()</v> - <v>Result = filename() | {filename(), binary()}</v> - </type> <desc> <p>The <marker id="zip_get"></marker> <c>zip_get</c> function extracts @@ -505,11 +445,8 @@ zip_file() </code> </desc> </func> <func> - <name>zip_close(ZipHandle) -> ok | {error, einval}</name> + <name name="zip_close" arity="1"/> <fsummary>Close an open archive</fsummary> - <type> - <v>ZipHandle = pid()</v> - </type> <desc> <p>The <marker id="zip_close"></marker> <c>zip_close/1</c> function closes diff --git a/lib/stdlib/include/zip.hrl b/lib/stdlib/include/zip.hrl index 2b5ddc1dfe..6e3ed9c78a 100644 --- a/lib/stdlib/include/zip.hrl +++ b/lib/stdlib/include/zip.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ -record(zip_file, { name :: string(), % file name - info :: #file_info{}, + info :: file:file_info(), comment :: string(), % zip file comment offset :: non_neg_integer(), % offset of file's local header in archive comp_size :: non_neg_integer() % compressed size diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl index 83576c9fd3..2f69e2b0a4 100644 --- a/lib/stdlib/src/array.erl +++ b/lib/stdlib/src/array.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -163,17 +163,17 @@ -type array_indx() :: non_neg_integer(). --type array_opt() :: 'fixed' | non_neg_integer() - | {'default', term()} | {'fixed', boolean()} - | {'size', non_neg_integer()}. +-type array_opt() :: {'fixed', boolean()} | 'fixed' + | {'default', Value :: term()} + | {'size', N :: non_neg_integer()} + | (N :: non_neg_integer()). -type array_opts() :: array_opt() | [array_opt()]. --type indx_pair() :: {array_indx(), term()}. +-type indx_pair() :: {Index :: array_indx(), Value :: term()}. -type indx_pairs() :: [indx_pair()]. %%-------------------------------------------------------------------------- -%% @spec () -> array() %% @doc Create a new, extendible array with initial size zero. %% @equiv new([]) %% @@ -185,7 +185,6 @@ new() -> new([]). -%% @spec (Options::term()) -> array() %% @doc Create a new array according to the given options. By default, %% the array is extendible and has initial size zero. Array indices %% start at 0. @@ -224,12 +223,11 @@ new() -> %% @see from_list/2 %% @see fix/1 --spec new(array_opts()) -> array(). +-spec new(Options :: array_opts()) -> array(). new(Options) -> new_0(Options, 0, false). -%% @spec (Size::integer(), Options::term()) -> array() %% @doc Create a new array according to the given size and options. If %% `Size' is not a nonnegative integer, the call fails with reason %% `badarg'. By default, the array has fixed size. Note that any size @@ -245,7 +243,7 @@ new(Options) -> %% %% @see new/1 --spec new(non_neg_integer(), array_opts()) -> array(). +-spec new(Size :: non_neg_integer(), Options :: array_opts()) -> array(). new(Size, Options) when is_integer(Size), Size >= 0 -> new_0(Options, Size, true); @@ -293,13 +291,12 @@ find_max(_I, M) -> M. -%% @spec (X::term()) -> boolean() %% @doc Returns `true' if `X' appears to be an array, otherwise `false'. %% Note that the check is only shallow; there is no guarantee that `X' %% is a well-formed array representation even if this function returns %% `true'. --spec is_array(term()) -> boolean(). +-spec is_array(X :: term()) -> boolean(). is_array(#array{size = Size, max = Max}) when is_integer(Size), is_integer(Max) -> @@ -308,25 +305,23 @@ is_array(_) -> false. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array. Entries are numbered %% from 0 to `size(Array)-1'; hence, this is also the index of the first %% entry that is guaranteed to not have been previously set. %% @see set/3 %% @see sparse_size/1 --spec size(array()) -> non_neg_integer(). +-spec size(Array :: array()) -> non_neg_integer(). size(#array{size = N}) -> N; size(_) -> erlang:error(badarg). -%% @spec (array()) -> term() %% @doc Get the value used for uninitialized entries. %% %% @see new/2 --spec default(array()) -> term(). +-spec default(Array :: array()) -> term(). default(#array{default = D}) -> D; default(_) -> erlang:error(badarg). @@ -405,23 +400,21 @@ new_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Fix the size of the array. This prevents it from growing %% automatically upon insertion; see also {@link set/3}. %% @see relax/1 --spec fix(array()) -> array(). +-spec fix(Array :: array()) -> array(). fix(#array{}=A) -> A#array{max = 0}. -%% @spec (array()) -> boolean() %% @doc Check if the array has fixed size. %% Returns `true' if the array is fixed, otherwise `false'. %% @see fix/1 --spec is_fix(array()) -> boolean(). +-spec is_fix(Array :: array()) -> boolean(). is_fix(#array{max = 0}) -> true; is_fix(#array{}) -> false. @@ -455,12 +448,11 @@ fix_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Make the array resizable. (Reverses the effects of {@link %% fix/1}.) %% @see fix/1 --spec relax(array()) -> array(). +-spec relax(Array :: array()) -> array(). relax(#array{size = N}=A) -> A#array{max = find_max(N-1, ?LEAFSIZE)}. @@ -481,12 +473,11 @@ relax_test_() -> -endif. -%% @spec (integer(), array()) -> array() %% @doc Change the size of the array. If `Size' is not a nonnegative %% integer, the call fails with reason `badarg'. If the given array has %% fixed size, the resulting array will also have fixed size. --spec resize(non_neg_integer(), array()) -> array(). +-spec resize(Size :: non_neg_integer(), Array :: array()) -> array(). resize(Size, #array{size = N, max = M, elements = E}=A) when is_integer(Size), Size >= 0 -> @@ -510,8 +501,6 @@ resize(_Size, _) -> erlang:error(badarg). -%% @spec (array()) -> array() - %% @doc Change the size of the array to that reported by {@link %% sparse_size/1}. If the given array has fixed size, the resulting %% array will also have fixed size. @@ -519,7 +508,7 @@ resize(_Size, _) -> %% @see resize/2 %% @see sparse_size/1 --spec resize(array()) -> array(). +-spec resize(Array :: array()) -> array(). resize(Array) -> resize(sparse_size(Array), Array). @@ -559,7 +548,6 @@ resize_test_() -> -endif. -%% @spec (integer(), term(), array()) -> array() %% @doc Set entry `I' of the array to `Value'. If `I' is not a %% nonnegative integer, or if the array has fixed size and `I' is larger %% than the maximum index, the call fails with reason `badarg'. @@ -570,7 +558,7 @@ resize_test_() -> %% @see get/2 %% @see reset/2 --spec set(array_indx(), term(), array()) -> array(). +-spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array(). set(I, Value, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -624,7 +612,6 @@ expand(I, _S, X, D) -> setelement(I+1, ?NEW_LEAF(D), X). -%% @spec (integer(), array()) -> term() %% @doc Get the value of entry `I'. If `I' is not a nonnegative %% integer, or if the array has fixed size and `I' is larger than the %% maximum index, the call fails with reason `badarg'. @@ -634,7 +621,7 @@ expand(I, _S, X, D) -> %% @see set/3 --spec get(array_indx(), array()) -> term(). +-spec get(I :: array_indx(), Array :: array()) -> term(). get(I, #array{size = N, max = M, elements = E, default = D}) when is_integer(I), I >= 0 -> @@ -660,7 +647,6 @@ get_1(I, E, _D) -> element(I+1, E). -%% @spec (integer(), array()) -> array() %% @doc Reset entry `I' to the default value for the array. %% If the value of entry `I' is the default value the array will be %% returned unchanged. Reset will never change size of the array. @@ -675,7 +661,7 @@ get_1(I, E, _D) -> %% TODO: a reset_range function --spec reset(array_indx(), array()) -> array(). +-spec reset(I :: array_indx(), Array :: array()) -> array(). reset(I, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -756,13 +742,12 @@ set_get_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list. %% %% @see from_list/2 %% @see sparse_to_list/1 --spec to_list(array()) -> list(). +-spec to_list(Array :: array()) -> list(). to_list(#array{size = 0}) -> []; @@ -831,12 +816,11 @@ to_list_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list, skipping default-valued entries. %% %% @see to_list/1 --spec sparse_to_list(array()) -> list(). +-spec sparse_to_list(Array :: array()) -> list(). sparse_to_list(#array{size = 0}) -> []; @@ -901,15 +885,13 @@ sparse_to_list_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_list(List, undefined) --spec from_list(list()) -> array(). +-spec from_list(List :: list()) -> array(). from_list(List) -> from_list(List, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert a list to an extendible array. `Default' is used as the value %% for uninitialized entries of the array. If `List' is not a proper list, %% the call fails with reason `badarg'. @@ -917,7 +899,7 @@ from_list(List) -> %% @see new/2 %% @see to_list/1 --spec from_list(list(), term()) -> array(). +-spec from_list(List :: list(), Default :: term()) -> array(). from_list([], Default) -> new({default,Default}); @@ -1011,13 +993,12 @@ from_list_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}'. %% %% @see from_orddict/2 %% @see sparse_to_orddict/1 --spec to_orddict(array()) -> indx_pairs(). +-spec to_orddict(Array :: array()) -> indx_pairs(). to_orddict(#array{size = 0}) -> []; @@ -1104,13 +1085,12 @@ to_orddict_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}', %% skipping default-valued entries. %% %% @see to_orddict/1 --spec sparse_to_orddict(array()) -> indx_pairs(). +-spec sparse_to_orddict(Array :: array()) -> indx_pairs(). sparse_to_orddict(#array{size = 0}) -> []; @@ -1188,15 +1168,13 @@ sparse_to_orddict_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_orddict(Orddict, undefined) --spec from_orddict(indx_pairs()) -> array(). +-spec from_orddict(Orddict :: indx_pairs()) -> array(). from_orddict(Orddict) -> from_orddict(Orddict, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert an ordered list of pairs `{Index, Value}' to a %% corresponding extendible array. `Default' is used as the value for %% uninitialized entries of the array. If `List' is not a proper, @@ -1206,7 +1184,7 @@ from_orddict(Orddict) -> %% @see new/2 %% @see to_orddict/1 --spec from_orddict(indx_pairs(), term()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array(). from_orddict([], Default) -> new({default,Default}); @@ -1392,7 +1370,6 @@ from_orddict_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array. The %% elements are visited in order from the lowest index to the highest. @@ -1402,7 +1379,8 @@ from_orddict_test_() -> %% @see foldr/3 %% @see sparse_map/2 --spec map(fun((array_indx(), _) -> _), array()) -> array(). +-spec map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1485,7 +1463,6 @@ map_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array, skipping %% default-valued entries. The elements are visited in order from the @@ -1494,7 +1471,8 @@ map_test_() -> %% %% @see map/2 --spec sparse_map(fun((array_indx(), _) -> _), array()) -> array(). +-spec sparse_map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). sparse_map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1580,9 +1558,6 @@ sparse_map_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value. The elements are visited in order from the %% lowest index to the highest. If `Function' is not a function, the @@ -1592,7 +1567,8 @@ sparse_map_test_() -> %% @see map/2 %% @see sparse_foldl/3 --spec foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1656,9 +1632,6 @@ foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value, skipping default-valued entries. The %% elements are visited in order from the lowest index to the highest. @@ -1667,7 +1640,8 @@ foldl_test_() -> %% @see foldl/3 %% @see sparse_foldr/3 --spec sparse_foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1735,9 +1709,6 @@ sparse_foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value. The elements are visited in %% order from the highest index to the lowest. If `Function' is not a @@ -1746,7 +1717,8 @@ sparse_foldl_test_() -> %% @see foldl/3 %% @see map/2 --spec foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1815,9 +1787,6 @@ foldr_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value, skipping default-valued %% entries. The elements are visited in order from the highest index to @@ -1827,7 +1796,8 @@ foldr_test_() -> %% @see foldr/3 %% @see sparse_foldl/3 --spec sparse_foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1870,7 +1840,6 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> end. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array up until the last %% non-default valued entry. In other words, returns `I+1' if `I' is the %% last non-default valued entry in the array, or zero if no such entry @@ -1878,7 +1847,7 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> %% @see size/1 %% @see resize/1 --spec sparse_size(array()) -> non_neg_integer(). +-spec sparse_size(Array :: array()) -> non_neg_integer(). sparse_size(A) -> F = fun (I, _V, _A) -> throw({value, I}) end, diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index a14a72ac6d..5d800e87b8 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -38,7 +38,9 @@ %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode_to_string(string() | binary()) -> ascii_string(). +-spec encode_to_string(Data) -> Base64String when + Data :: string() | binary(), + Base64String :: ascii_string(). encode_to_string(Bin) when is_binary(Bin) -> encode_to_string(binary_to_list(Bin)); @@ -53,7 +55,9 @@ encode_to_string(List) when is_list(List) -> %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode(string() | binary()) -> binary(). +-spec encode(Data) -> Base64 when + Data :: string() | binary(), + Base64 :: binary(). encode(Bin) when is_binary(Bin) -> encode_binary(Bin); @@ -102,14 +106,18 @@ encode_binary(Bin) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode(string() | binary()) -> binary(). +-spec decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). decode(Bin) when is_binary(Bin) -> decode_binary(<<>>, Bin); decode(List) when is_list(List) -> list_to_binary(decode_l(List)). --spec mime_decode(string() | binary()) -> binary(). +-spec mime_decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); @@ -139,14 +147,18 @@ mime_decode_l(List) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode_to_string(string() | binary()) -> string(). +-spec decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). decode_to_string(Bin) when is_binary(Bin) -> decode_to_string(binary_to_list(Bin)); decode_to_string(List) when is_list(List) -> decode_l(List). --spec mime_decode_to_string(string() | binary()) -> string(). +-spec mime_decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). mime_decode_to_string(Bin) when is_binary(Bin) -> mime_decode_to_string(binary_to_list(Bin)); diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 74d4ad3da7..d9c645d787 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,13 +54,9 @@ -type beam() :: module() | file:filename() | binary(). -%% XXX: THE FOLLOWING SHOULD BE IMPORTED FROM SOMEWHERE ELSE --type forms() :: term(). +-type forms() :: [erl_parse:abstract_form()]. --type abst_vsn() :: atom(). --type abst_code() :: {abst_vsn(), forms()} | 'no_abstract_code'. --type attribute() :: atom(). --type attrvalue() :: term(). +-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'. -type dataB() :: binary(). -type index() :: non_neg_integer(). -type label() :: integer(). @@ -74,9 +70,9 @@ | 'atoms'. -type chunkref() :: chunkname() | chunkid(). --type attrib_entry() :: {attribute(), [attrvalue()]}. --type compinfo_entry() :: {atom(), term()}. --type labeled_entry() :: {atom(), arity(), label()}. +-type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}. +-type compinfo_entry() :: {InfoKey :: atom(), term()}. +-type labeled_entry() :: {Function :: atom(), arity(), label()}. -type chunkdata() :: {chunkid(), dataB()} | {'abstract_code', abst_code()} @@ -85,20 +81,17 @@ | {'exports', [{atom(), arity()}]} | {'labeled_exports', [labeled_entry()]} | {'imports', [mfa()]} - | {'indexed_imports', [{index(), module(), atom(), arity()}]} + | {'indexed_imports', [{index(), module(), Function :: atom(), arity()}]} | {'locals', [{atom(), arity()}]} | {'labeled_locals', [labeled_entry()]} | {'atoms', [{integer(), atom()}]}. --type info_pair() :: {'file', file:filename()} - | {'binary', binary()} - | {'module', module()} - | {'chunks', [{chunkid(), integer(), integer()}]}. - %% Error reasons -type info_rsn() :: {'chunk_too_big', file:filename(), - chunkid(), integer(), integer()} - | {'invalid_beam_file', file:filename(), integer()} + chunkid(), ChunkSize :: non_neg_integer(), + FileSize :: non_neg_integer()} + | {'invalid_beam_file', file:filename(), + Position :: non_neg_integer()} | {'invalid_chunk', file:filename(), chunkid()} | {'missing_chunk', file:filename(), chunkid()} | {'not_a_beam_file', file:filename()} @@ -118,20 +111,34 @@ %% Exported functions %% --spec info(beam()) -> [info_pair()] | {'error', 'beam_lib', info_rsn()}. +-spec info(Beam) -> [InfoPair] | {'error', 'beam_lib', info_rsn()} when + Beam :: beam(), + InfoPair :: {'file', Filename :: file:filename()} + | {'binary', Binary :: binary()} + | {'module', Module :: module()} + | {'chunks', [{ChunkId :: chunkid(), + Pos :: non_neg_integer(), + Size :: non_neg_integer()}]}. info(File) -> read_info(beam_filename(File)). --spec chunks(beam(), [chunkref()]) -> - {'ok', {module(), [chunkdata()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs) -> + {'ok', {module(), [chunkdata()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()]. chunks(File, Chunks) -> read_chunk_data(File, Chunks). --spec chunks(beam(), [chunkref()], ['allow_missing_chunks']) -> - {'ok', {module(), [{chunkref(), chunkdata() | 'missing_chunk'}]}} - | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs, Options) -> + {'ok', {module(), [ChunkResult]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()], + Options :: ['allow_missing_chunks'], + ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), 'missing_chunk'}. chunks(File, Chunks, Options) -> try read_chunk_data(File, Chunks, Options) @@ -142,49 +149,65 @@ chunks(File, Chunks, Options) -> all_chunks(File) -> read_all_chunks(File). --spec cmp(beam(), beam()) -> 'ok' | {'error', 'beam_lib', cmp_rsn()}. +-spec cmp(Beam1, Beam2) -> 'ok' | {'error', 'beam_lib', cmp_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). cmp(File1, File2) -> try cmp_files(File1, File2) catch Error -> Error end. --spec cmp_dirs(atom() | file:filename(), atom() | file:filename()) -> - {[file:filename()], [file:filename()], - [{file:filename(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec cmp_dirs(Dir1, Dir2) -> + {Only1, Only2, Different} | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Only1 :: [file:filename()], + Only2 :: [file:filename()], + Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}], + Reason :: {'not_a_directory', term()} | info_rsn(). cmp_dirs(Dir1, Dir2) -> catch compare_dirs(Dir1, Dir2). --spec diff_dirs(atom() | file:filename(), atom() | file:filename()) -> - 'ok' | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec diff_dirs(Dir1, Dir2) -> 'ok' | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). diff_dirs(Dir1, Dir2) -> catch diff_directories(Dir1, Dir2). --spec strip(beam()) -> - {'ok', {module(), beam()}} | {'error', 'beam_lib', info_rsn()}. +-spec strip(Beam1) -> + {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). strip(FileName) -> try strip_file(FileName) catch Error -> Error end. --spec strip_files([beam()]) -> - {'ok', [{module(), beam()}]} | {'error', 'beam_lib', info_rsn()}. +-spec strip_files(Files) -> + {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when + Files :: [beam()], + Beam :: beam(). strip_files(Files) when is_list(Files) -> try strip_fils(Files) catch Error -> Error end. --spec strip_release(atom() | file:filename()) -> +-spec strip_release(Dir) -> {'ok', [{module(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. + | {'error', 'beam_lib', Reason} when + Dir :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). strip_release(Root) -> catch strip_rel(Root). --spec version(beam()) -> - {'ok', {module(), [term()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec version(Beam) -> + {'ok', {module(), [Version :: term()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(). version(File) -> case catch read_chunk_data(File, [attributes]) of @@ -195,8 +218,10 @@ version(File) -> Error end. --spec md5(beam()) -> - {'ok', {module(), binary()}} | {'error', 'beam_lib', chnk_rsn()}. +-spec md5(Beam) -> + {'ok', {module(), MD5}} | {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + MD5 :: binary(). md5(File) -> case catch read_significant_chunks(File) of @@ -207,7 +232,8 @@ md5(File) -> Error end. --spec format_error(term()) -> [char() | string()]. +-spec format_error(Reason) -> io_lib:chars() when + Reason :: term(). format_error({error, Error}) -> format_error(Error); @@ -260,12 +286,15 @@ format_error(E) -> | {'debug_info', mode(), module(), file:filename()}. -type crypto_fun() :: fun((crypto_fun_arg()) -> term()). --spec crypto_key_fun(crypto_fun()) -> 'ok' | {'error', term()}. +-spec crypto_key_fun(CryptoKeyFun) -> 'ok' | {'error', Reason} when + CryptoKeyFun :: crypto_fun(), + Reason :: badfun | exists | term(). crypto_key_fun(F) -> call_crypto_server({crypto_key_fun, F}). --spec clear_crypto_key_fun() -> 'undefined' | {'ok', term()}. +-spec clear_crypto_key_fun() -> 'undefined' | {'ok', Result} when + Result :: 'undefined' | term(). clear_crypto_key_fun() -> call_crypto_server(clear_crypto_key_fun). diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index f6489788b2..cb1e12ae46 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,11 +38,28 @@ %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). +-opaque cp() :: tuple(). +-type part() :: {Start :: non_neg_integer(), Length :: integer()}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% split %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec split(Subject, Pattern) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Parts :: [binary()]. + split(H,N) -> split(H,N,[]). + +-spec split(Subject, Pattern, Options) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Options :: [Option], + Option :: {scope, part()} | trim | global, + Parts :: [binary()]. + split(Haystack,Needles,Options) -> try {Part,Global,Trim} = get_opts_split(Options,{no,false,false}), @@ -89,8 +106,26 @@ do_split(H,[{A,B}|T],N,Trim) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% replace %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec replace(Subject, Pattern, Replacement) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Result :: binary(). + replace(H,N,R) -> replace(H,N,R,[]). + +-spec replace(Subject, Pattern, Replacement, Options) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Options :: [Option], + Option :: global | {scope, part()} | {insert_replaced, InsPos}, + InsPos :: OnePos | [ OnePos ], + OnePos :: non_neg_integer(), + Result :: binary(). + replace(Haystack,Needles,Replacement,Options) -> try true = is_binary(Replacement), % Make badarg instead of function clause diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 235ea939a8..febfdd6285 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -71,11 +71,16 @@ help() -> %% c(FileName) %% Compile a file/module. --spec c(file:name()) -> {'ok', module()} | 'error'. +-spec c(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). c(File) -> c(File, []). --spec c(file:name(), [compile:option()]) -> {'ok', module()} | 'error'. +-spec c(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [compile:option()], + Module :: module(). c(File, Opts0) when is_list(Opts0) -> Opts = [report_errors,report_warnings|Opts0], @@ -140,7 +145,8 @@ check_load(_, Mod) -> {ok, Mod}. %% with constant c2 defined, c1=v1 (v1 must be a term!), include dir %% IDir, outdir ODir. --spec lc([erl_compile:cmd_line_arg()]) -> 'ok' | 'error'. +-spec lc(Files) -> 'ok' | 'error' when + Files :: [File :: erl_compile:cmd_line_arg()]. lc(Args) -> case catch split(Args, [], []) of @@ -205,12 +211,17 @@ make_term(Str) -> throw(error) end. --spec nc(file:name()) -> {'ok', module()} | 'error'. +-spec nc(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). nc(File) -> nc(File, []). --spec nc(file:name(), [compile:option()] | compile:option()) -> - {'ok', module} | 'error'. +-spec nc(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [Option] | Option, + Option:: compile:option(), + Module :: module(). nc(File, Opts0) when is_list(Opts0) -> Opts = Opts0 ++ [report_errors, report_warnings], @@ -234,14 +245,17 @@ nc(File, Opt) when is_atom(Opt) -> %% l(Mod) %% Reload module Mod from file of same name --spec l(module()) -> code:load_ret(). +-spec l(Module) -> code:load_ret() when + Module :: module(). l(Mod) -> code:purge(Mod), code:load_file(Mod). %% Network version of l/1 -%%-spec nl(module()) -> +-spec nl(Module) -> abcast | error when + Module :: module(). + nl(Mod) -> case code:get_object_code(Mod) of {_Module, Bin, Fname} -> @@ -396,15 +410,20 @@ fetch(Key, Info) -> false -> 0 end. --spec pid(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> pid(). +-spec pid(X, Y, Z) -> pid() when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). pid(X, Y, Z) -> list_to_pid("<" ++ integer_to_list(X) ++ "." ++ integer_to_list(Y) ++ "." ++ integer_to_list(Z) ++ ">"). --spec i(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> - [{atom(), term()}]. +-spec i(X, Y, Z) -> [{atom(), term()}] when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). i(X, Y, Z) -> pinfo(pid(X, Y, Z)). @@ -413,7 +432,8 @@ i(X, Y, Z) -> pinfo(pid(X, Y, Z)). q() -> init:stop(). --spec bt(pid()) -> 'ok' | 'undefined'. +-spec bt(Pid) -> 'ok' | 'undefined' when + Pid :: pid(). bt(Pid) -> case catch erlang:process_display(Pid, backtrace) of @@ -476,7 +496,8 @@ bi(I) -> %% %% Short and nice form of module info %% --spec m(module()) -> 'ok'. +-spec m(Module) -> 'ok' when + Module :: module(). m(M) -> L = M:module_info(), @@ -664,7 +685,8 @@ pwd() -> ok = io:format("Cannot determine current directory\n") end. --spec cd(file:name()) -> 'ok'. +-spec cd(Dir) -> 'ok' when + Dir :: file:name(). cd(Dir) -> file:set_cwd(Dir), @@ -679,7 +701,8 @@ cd(Dir) -> ls() -> ls("."). --spec ls(file:name()) -> 'ok'. +-spec ls(Dir) -> 'ok' when + Dir :: file:name(). ls(Dir) -> case file:list_dir(Dir) of @@ -729,12 +752,19 @@ w(X) -> %% memory/[0,1] %% --spec memory() -> [{atom(), non_neg_integer()}]. +-spec memory() -> [{Type, Size}] when + Type :: atom(), + Size :: non_neg_integer(). memory() -> erlang:memory(). --spec memory(atom()) -> non_neg_integer() - ; ([atom()]) -> [{atom(), non_neg_integer()}]. +-spec memory(Type) -> Size when + Type :: atom(), + Size :: non_neg_integer() + ; (Types) -> [{Type, Size}] when + Types :: [Type], + Type :: atom(), + Size :: non_neg_integer(). memory(TypeSpec) -> erlang:memory(TypeSpec). diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index 33725d999c..8d1071209e 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -63,6 +63,8 @@ %% Types %%---------------------------------------------------------------------- +-export_type([t_now/0]). + -type year() :: non_neg_integer(). -type year1970() :: 1970..10000. % should probably be 1970.. -type month() :: 1..12. @@ -74,7 +76,9 @@ -type ldom() :: 28 | 29 | 30 | 31. % last day of month -type weeknum() :: 1..53. --type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}. +-type t_now() :: {MegaSecs :: non_neg_integer(), + Secs :: non_neg_integer(), + MicroSecs :: non_neg_integer()}. -type t_date() :: {year(),month(),day()}. -type t_time() :: {hour(),minute(),second()}. @@ -106,7 +110,11 @@ %% January 1st. %% %% df/2 catches the case Year<0 --spec date_to_gregorian_days(year(),month(),day()) -> non_neg_integer(). +-spec date_to_gregorian_days(Year, Month, Day) -> Days when + Year :: year(), + Month :: month(), + Day :: day(), + Days :: non_neg_integer(). date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> Last = last_day_of_the_month(Year, Month), if @@ -114,7 +122,9 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> dy(Year) + dm(Month) + df(Year, Month) + Day - 1 end. --spec date_to_gregorian_days(t_date()) -> non_neg_integer(). +-spec date_to_gregorian_days(Date) -> Days when + Date :: t_date(), + Days :: non_neg_integer(). date_to_gregorian_days({Year, Month, Day}) -> date_to_gregorian_days(Year, Month, Day). @@ -124,7 +134,9 @@ date_to_gregorian_days({Year, Month, Day}) -> %% Computes the total number of seconds starting from year 0, %% January 1st. %% --spec datetime_to_gregorian_seconds(t_datetime()) -> non_neg_integer(). +-spec datetime_to_gregorian_seconds(DateTime) -> Seconds when + DateTime :: t_datetime(), + Seconds :: non_neg_integer(). datetime_to_gregorian_seconds({Date, Time}) -> ?SECONDS_PER_DAY*date_to_gregorian_days(Date) + time_to_seconds(Time). @@ -135,18 +147,23 @@ datetime_to_gregorian_seconds({Date, Time}) -> %% %% Returns: 1 | .. | 7. Monday = 1, Tuesday = 2, ..., Sunday = 7. %% --spec day_of_the_week(year(), month(), day()) -> daynum(). +-spec day_of_the_week(Year, Month, Day) -> daynum() when + Year :: year(), + Month :: month(), + Day :: day(). day_of_the_week(Year, Month, Day) -> (date_to_gregorian_days(Year, Month, Day) + 5) rem 7 + 1. --spec day_of_the_week(t_date()) -> daynum(). +-spec day_of_the_week(Date) -> daynum() when + Date:: t_date(). day_of_the_week({Year, Month, Day}) -> day_of_the_week(Year, Month, Day). %% gregorian_days_to_date(Days) = {Year, Month, Day} %% --spec gregorian_days_to_date(non_neg_integer()) -> t_date(). +-spec gregorian_days_to_date(Days) -> t_date() when + Days :: non_neg_integer(). gregorian_days_to_date(Days) -> {Year, DayOfYear} = day_to_year(Days), {Month, DayOfMonth} = year_day_to_date(Year, DayOfYear), @@ -155,7 +172,8 @@ gregorian_days_to_date(Days) -> %% gregorian_seconds_to_datetime(Secs) %% --spec gregorian_seconds_to_datetime(non_neg_integer()) -> t_datetime(). +-spec gregorian_seconds_to_datetime(Seconds) -> t_datetime() when + Seconds :: non_neg_integer(). gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> Days = Secs div ?SECONDS_PER_DAY, Rest = Secs rem ?SECONDS_PER_DAY, @@ -164,7 +182,8 @@ gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> %% is_leap_year(Year) = true | false %% --spec is_leap_year(year()) -> boolean(). +-spec is_leap_year(Year) -> boolean() when + Year :: year(). is_leap_year(Y) when is_integer(Y), Y >= 0 -> is_leap_year1(Y). @@ -188,7 +207,8 @@ iso_week_number() -> %% %% Calculates the iso week number for the given date. %% --spec iso_week_number(t_date()) -> t_yearweeknum(). +-spec iso_week_number(Date) -> t_yearweeknum() when + Date :: t_date(). iso_week_number({Year, Month, Day}) -> D = date_to_gregorian_days({Year, Month, Day}), W01_1_Year = gregorian_days_of_iso_w01_1(Year), @@ -216,7 +236,10 @@ iso_week_number({Year, Month, Day}) -> %% %% Returns the number of days in a month. %% --spec last_day_of_the_month(year(), month()) -> ldom(). +-spec last_day_of_the_month(Year, Month) -> LastDay when + Year :: year(), + Month :: month(), + LastDay :: ldom(). last_day_of_the_month(Y, M) when is_integer(Y), Y >= 0 -> last_day_of_the_month1(Y, M). @@ -244,7 +267,9 @@ local_time() -> %% local_time_to_universal_time(DateTime) %% --spec local_time_to_universal_time(t_datetime1970()) -> t_datetime1970(). +-spec local_time_to_universal_time(DateTime1) -> DateTime2 when + DateTime1 :: t_datetime1970(), + DateTime2 :: t_datetime1970(). local_time_to_universal_time(DateTime) -> erlang:localtime_to_universaltime(DateTime). @@ -254,7 +279,9 @@ local_time_to_universal_time(DateTime) -> local_time_to_universal_time(DateTime, IsDst) -> erlang:localtime_to_universaltime(DateTime, IsDst). --spec local_time_to_universal_time_dst(t_datetime1970()) -> [t_datetime1970()]. +-spec local_time_to_universal_time_dst(DateTime1) -> [DateTime] when + DateTime1 :: t_datetime1970(), + DateTime :: t_datetime1970(). local_time_to_universal_time_dst(DateTime) -> UtDst = erlang:localtime_to_universaltime(DateTime, true), Ut = erlang:localtime_to_universaltime(DateTime, false), @@ -282,12 +309,14 @@ local_time_to_universal_time_dst(DateTime) -> %% = MilliSec = integer() %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. %% --spec now_to_datetime(t_now()) -> t_datetime1970(). +-spec now_to_datetime(Now) -> t_datetime1970() when + Now :: t_now(). now_to_datetime({MSec, Sec, _uSec}) -> Sec0 = MSec*1000000 + Sec + ?DAYS_FROM_0_TO_1970*?SECONDS_PER_DAY, gregorian_seconds_to_datetime(Sec0). --spec now_to_universal_time(t_now()) -> t_datetime1970(). +-spec now_to_universal_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_universal_time(Now) -> now_to_datetime(Now). @@ -296,7 +325,8 @@ now_to_universal_time(Now) -> %% %% Args: Now = now() %% --spec now_to_local_time(t_now()) -> t_datetime1970(). +-spec now_to_local_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_local_time({MSec, Sec, _uSec}) -> erlang:universaltime_to_localtime( now_to_universal_time({MSec, Sec, _uSec})). @@ -305,7 +335,10 @@ now_to_local_time({MSec, Sec, _uSec}) -> %% seconds_to_daystime(Secs) = {Days, {Hour, Minute, Second}} %% --spec seconds_to_daystime(integer()) -> {integer(), t_time()}. +-spec seconds_to_daystime(Seconds) -> {Days, Time} when + Seconds :: integer(), + Days :: integer(), + Time :: t_time(). seconds_to_daystime(Secs) -> Days0 = Secs div ?SECONDS_PER_DAY, Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -323,7 +356,8 @@ seconds_to_daystime(Secs) -> %% Wraps. %% -type secs_per_day() :: 0..?SECONDS_PER_DAY. --spec seconds_to_time(secs_per_day()) -> t_time(). +-spec seconds_to_time(Seconds) -> t_time() when + Seconds :: secs_per_day(). seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> Secs0 = Secs rem ?SECONDS_PER_DAY, Hour = Secs0 div ?SECONDS_PER_HOUR, @@ -340,8 +374,11 @@ seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> %% Date = {Year, Month, Day}, Time = {Hour, Minute, Sec}, %% Year = Month = Day = Hour = Minute = Sec = integer() %% --type timediff() :: {integer(), t_time()}. --spec time_difference(t_datetime(), t_datetime()) -> timediff(). +-spec time_difference(T1, T2) -> {Days, Time} when + T1 :: t_datetime(), + T2 :: t_datetime(), + Days :: integer(), + Time :: t_time(). time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, {{Y2, Mo2, D2}, {H2, Mi2, S2}}) -> Secs = datetime_to_gregorian_seconds({{Y2, Mo2, D2}, {H2, Mi2, S2}}) - @@ -352,7 +389,8 @@ time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, %% %% time_to_seconds(Time) %% --spec time_to_seconds(t_time()) -> secs_per_day(). +-spec time_to_seconds(Time) -> secs_per_day() when + Time :: t_time(). time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> H * ?SECONDS_PER_HOUR + M * ?SECONDS_PER_MINUTE + S. @@ -368,7 +406,8 @@ universal_time() -> %% universal_time_to_local_time(DateTime) %% --spec universal_time_to_local_time(t_datetime()) -> t_datetime(). +-spec universal_time_to_local_time(DateTime) -> t_datetime() when + DateTime :: t_datetime1970(). universal_time_to_local_time(DateTime) -> erlang:universaltime_to_localtime(DateTime). @@ -376,7 +415,10 @@ universal_time_to_local_time(DateTime) -> %% valid_date(Year, Month, Day) = true | false %% valid_date({Year, Month, Day}) = true | false %% --spec valid_date(integer(), integer(), integer()) -> boolean(). +-spec valid_date(Year, Month, Day) -> boolean() when + Year :: integer(), + Month :: integer(), + Day :: integer(). valid_date(Y, M, D) when is_integer(Y), is_integer(M), is_integer(D) -> valid_date1(Y, M, D). @@ -386,7 +428,8 @@ valid_date1(Y, M, D) when Y >= 0, M > 0, M < 13, D > 0 -> valid_date1(_, _, _) -> false. --spec valid_date({integer(),integer(),integer()}) -> boolean(). +-spec valid_date(Date) -> boolean() when + Date :: t_date(). valid_date({Y, M, D}) -> valid_date(Y, M, D). diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 6c91f1efb7..671b5a9dd4 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -97,10 +97,6 @@ -include("dets.hrl"). --type object() :: tuple(). --type pattern() :: atom() | tuple(). --type tab_name() :: atom() | reference(). - %%% This is the implementation of the mnesia file storage. Each (non %%% ram-copy) table is maintained in a corresponding .DAT file. The %%% dat file is organized as a segmented linear hashlist. The head of @@ -179,6 +175,21 @@ %%-define(PROFILE(C), C). -define(PROFILE(C), void). +-type access() :: 'read' | 'read_write'. +-type auto_save() :: 'infinity' | non_neg_integer(). +-opaque bindings_cont() :: #dets_cont{}. +-opaque cont() :: #dets_cont{}. +-type keypos() :: pos_integer(). +-type match_spec() :: ets:match_spec(). +-type object() :: tuple(). +-type no_slots() :: non_neg_integer() | 'default'. +-opaque object_cont() :: #dets_cont{}. +-type pattern() :: atom() | tuple(). +-opaque select_cont() :: #dets_cont{}. +-type tab_name() :: term(). +-type type() :: 'bag' | 'duplicate_bag' | 'set'. +-type version() :: 8 | 9 | 'default'. + %%% Some further debug code was added in R12B-1 (stdlib-1.15.1): %%% - there is a new open_file() option 'debug'; %%% - there is a new OS environment variable 'DETS_DEBUG'; @@ -203,9 +214,13 @@ add_user(Pid, Tab, Args) -> all() -> dets_server:all(). --type cont() :: #dets_cont{}. --spec bchunk(tab_name(), 'start' | cont()) -> - {cont(), binary() | tuple()} | '$end_of_table' | {'error', term()}. +-spec bchunk(Name, Continuation) -> + {Continuation2, Data} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Continuation :: 'start' | cont(), + Continuation2 :: cont(), + Data :: binary() | tuple(), + Reason :: term(). bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]); @@ -214,7 +229,9 @@ bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> bchunk(Tab, Term) -> erlang:error(badarg, [Tab, Term]). --spec close(tab_name()) -> 'ok' | {'error', term()}. +-spec close(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). close(Tab) -> case dets_server:close(Tab) of @@ -224,12 +241,17 @@ close(Tab) -> Reply end. --spec delete(tab_name(), term()) -> 'ok' | {'error', term()}. +-spec delete(Name, Key) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). delete(Tab, Key) -> badarg(treq(Tab, {delete_key, [Key]}), [Tab, Key]). --spec delete_all_objects(tab_name()) -> 'ok' | {'error', term()}. +-spec delete_all_objects(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). delete_all_objects(Tab) -> case treq(Tab, delete_all_objects) of @@ -241,7 +263,10 @@ delete_all_objects(Tab) -> Reply end. --spec delete_object(tab_name(), object()) -> 'ok' | {'error', term()}. +-spec delete_object(Name, Object) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Object :: object(), + Reason :: term(). delete_object(Tab, O) -> badarg(treq(Tab, {delete_object, [O]}), [Tab, O]). @@ -264,23 +289,42 @@ fsck(Fname, Version) -> end end. --spec first(tab_name()) -> term() | '$end_of_table'. +-spec first(Name) -> Key | '$end_of_table' when + Name :: tab_name(), + Key :: term(). first(Tab) -> badarg_exit(treq(Tab, first), [Tab]). --spec foldr(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldr(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab). --spec foldl(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldl(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldl(Fun, Acc, Tab) -> Ref = make_ref(), do_traverse(Fun, Acc, Tab, Ref). --spec from_ets(tab_name(), ets:tab()) -> 'ok' | {'error', term()}. +-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). from_ets(DTab, ETab) -> ets:safe_fixtable(ETab, true), @@ -304,6 +348,15 @@ from_ets_fun(LC, ETab) -> {L, from_ets_fun(ets:select(C), ETab)} end. +-spec info(Name) -> InfoList | 'undefined' when + Name :: tab_name(), + InfoList :: [InfoTuple], + InfoTuple :: {'file_size', non_neg_integer()} + | {'filename', file:name()} + | {'keypos', keypos()} + | {'size', non_neg_integer()} + | {'type', type()}. + info(Tab) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> @@ -312,6 +365,14 @@ info(Tab) -> undefined(req(Pid, info)) end. +-spec info(Name, Item) -> Value | 'undefined' when + Name :: tab_name(), + Item :: 'access' | 'auto_save' | 'bchunk_format' + | 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory' + | 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file' + | 'safe_fixed' | 'size' | 'type' | 'version', + Value :: term(). + info(Tab, owner) -> case catch dets_server:get_pid(Tab) of Pid when is_pid(Pid) -> @@ -334,9 +395,26 @@ info(Tab, Tag) -> undefined(req(Pid, {info, Tag})) end. +-spec init_table(Name, InitFun) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun) -> init_table(Tab, InitFun, []). +-spec init_table(Name, InitFun, Options) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Options :: [{min_no_slots,no_slots()} | {format,term | bchunk}], + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun, Options) when is_function(InitFun) -> case options(Options, [format, min_no_slots]) of {badarg,_} -> @@ -350,11 +428,20 @@ init_table(Tab, InitFun, Options) when is_function(InitFun) -> init_table(Tab, InitFun, Options) -> erlang:error(badarg, [Tab, InitFun, Options]). +-spec insert(Name, Objects) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Objects :: object() | [object()], + Reason :: term(). + insert(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert, Objs}), [Tab, Objs]); insert(Tab, Obj) -> badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]). +-spec insert_new(Name, Objects) -> boolean() when + Name :: tab_name(), + Objects :: object() | [object()]. + insert_new(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]); insert_new(Tab, Obj) -> @@ -366,9 +453,17 @@ internal_close(Pid) -> internal_open(Pid, Ref, Args) -> req(Pid, {internal_open, Ref, Args}). +-spec is_compatible_bchunk_format(Name, BchunkFormat) -> boolean() when + Name :: tab_name(), + BchunkFormat :: binary(). + is_compatible_bchunk_format(Tab, Term) -> badarg(treq(Tab, {is_compatible_bchunk_format, Term}), [Tab, Term]). +-spec is_dets_file(Filename) -> boolean() | {'error', Reason} when + Filename :: file:name(), + Reason :: term(). + is_dets_file(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> @@ -382,6 +477,12 @@ is_dets_file(FileName) -> Other end. +-spec lookup(Name, Key) -> Objects | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Objects :: [object()], + Reason :: term(). + lookup(Tab, Key) -> badarg(treq(Tab, {lookup_keys, [Key]}), [Tab, Key]). @@ -394,19 +495,43 @@ lookup_keys(Tab, Keys) -> erlang:error(badarg, [Tab, Keys]) end. +-spec match(Name, Pattern) -> [Match] | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat) -> badarg(safe_match(Tab, Pat, bindings), [Tab, Pat]). +-spec match(Name, Pattern, N) -> + {[Match], Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). +-spec match(Continuation) -> + {[Match], Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: bindings_cont(), + Continuation2 :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(State) when State#dets_cont.what =:= bindings -> badarg(chunk_match(State), [State]); match(Term) -> erlang:error(badarg, [Term]). --spec match_delete(tab_name(), pattern()) -> - non_neg_integer() | 'ok' | {'error', term()}. +-spec match_delete(Name, Pattern) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Reason :: term(). match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]). @@ -434,23 +559,60 @@ do_match_delete(Tab, _Proc, Error, _What, _N) -> safe_fixtable(Tab, false), Error. +-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat) -> badarg(safe_match(Tab, Pat, object), [Tab, Pat]). +-spec match_object(Name, Pattern, N) -> + {Objects, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). +-spec match_object(Continuation) -> + {Objects, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: object_cont(), + Continuation2 :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(State) when State#dets_cont.what =:= object -> badarg(chunk_match(State), [State]); match_object(Term) -> erlang:error(badarg, [Term]). +-spec member(Name, Key) -> boolean() | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). + member(Tab, Key) -> badarg(treq(Tab, {member, Key}), [Tab, Key]). +-spec next(Name, Key1) -> Key2 | '$end_of_table' when + Name :: tab_name(), + Key1 :: term(), + Key2 :: term(). + next(Tab, Key) -> badarg_exit(treq(Tab, {next, Key}), [Tab, Key]). +-spec open_file(Filename) -> {'ok', Reference} | {'error', Reason} when + Filename :: file:name(), + Reference :: reference(), + Reason :: term(). + %% Assuming that a file already exists, open it with the %% parameters as already specified in the file itself. %% Return a ref leading to the file. @@ -462,6 +624,22 @@ open_file(File) -> einval(Reply, [File]) end. +-spec open_file(Name, Args) -> {'ok', Name} | {'error', Reason} when + Name :: tab_name(), + Args :: [OpenArg], + OpenArg :: {'access', access()} + | {'auto_save', auto_save()} + | {'estimated_no_objects', non_neg_integer()} + | {'file', file:name()} + | {'max_no_slots', no_slots()} + | {'min_no_slots', no_slots()} + | {'keypos', keypos()} + | {'ram_file', boolean()} + | {'repair', boolean() | 'force'} + | {'type', type()} + | {'version', version()}, + Reason :: term(). + open_file(Tab, Args) when is_list(Args) -> case catch defaults(Tab, Args) of OpenArgs when is_record(OpenArgs, open_args) -> @@ -477,12 +655,21 @@ open_file(Tab, Args) when is_list(Args) -> open_file(Tab, Arg) -> open_file(Tab, [Arg]). +-spec pid2name(Pid) -> {'ok', Name} | 'undefined' when + Pid :: pid(), + Name :: tab_name(). + pid2name(Pid) -> dets_server:pid2name(Pid). remove_user(Pid, From) -> req(Pid, {close, From}). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation2 when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + MatchSpec :: match_spec(). + repair_continuation(#dets_cont{match_program = B}=Cont, MS) when is_binary(B) -> case ets:is_compiled_ms(B) of @@ -496,25 +683,63 @@ repair_continuation(#dets_cont{}=Cont, _MS) -> repair_continuation(T, MS) -> erlang:error(badarg, [T, MS]). +-spec safe_fixtable(Name, Fix) -> 'ok' when + Name :: tab_name(), + Fix :: boolean(). + safe_fixtable(Tab, Bool) when Bool; not Bool -> badarg(treq(Tab, {safe_fixtable, Bool}), [Tab, Bool]); safe_fixtable(Tab, Term) -> erlang:error(badarg, [Tab, Term]). +-spec select(Name, MatchSpec) -> Selection | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat) -> badarg(safe_match(Tab, Pat, select), [Tab, Pat]). +-spec select(Name, MatchSpec, N) -> + {Selection, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: 'default' | non_neg_integer(), + Continuation :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]). +-spec select(Continuation) -> + {Selection, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(State) when State#dets_cont.what =:= select -> badarg(chunk_match(State), [State]); select(Term) -> erlang:error(badarg, [Term]). +-spec select_delete(Name, MatchSpec) -> N | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: non_neg_integer(), + Reason :: term(). + select_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, select), [Tab, Pat]). +-spec slot(Name, I) -> '$end_of_table' | Objects | {'error', Reason} when + Name :: tab_name(), + I :: non_neg_integer(), + Objects :: [object()], + Reason :: term(). + slot(Tab, Slot) when is_integer(Slot), Slot >= 0 -> badarg(treq(Tab, {slot, Slot}), [Tab, Slot]); slot(Tab, Term) -> @@ -529,12 +754,29 @@ stop() -> istart_link(Server) -> {ok, proc_lib:spawn_link(dets, init, [self(), Server])}. +-spec sync(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). + sync(Tab) -> badarg(treq(Tab, sync), [Tab]). +-spec table(Name) -> QueryHandle when + Name :: tab_name(), + QueryHandle :: qlc:query_handle(). + table(Tab) -> table(Tab, []). +-spec table(Name, Options) -> QueryHandle when + Name :: tab_name(), + Options :: Option | [Option], + Option :: {'n_objects', Limit} + | {'traverse', TraverseMethod}, + Limit :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'select' | {'select', match_spec()}, + QueryHandle :: qlc:query_handle(). + table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of {badarg,_} -> @@ -612,6 +854,11 @@ table_info(_Tab, _) -> %% End of table/2. +-spec to_ets(Name, EtsTab) -> EtsTab | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). + to_ets(DTab, ETab) -> case ets:info(ETab, protection) of undefined -> @@ -621,6 +868,16 @@ to_ets(DTab, ETab) -> foldl(Fun, ETab, DTab) end. +-spec traverse(Name, Fun) -> Return | {'error', Reason} when + Name :: tab_name(), + Fun :: fun((Object) -> FunReturn), + FunReturn :: 'continue' | {'continue', Val} | {'done', Value}, + Val :: term(), + Value :: term(), + Object :: object(), + Return :: [term()], + Reason :: term(). + traverse(Tab, Fun) -> Ref = make_ref(), TFun = @@ -638,6 +895,14 @@ traverse(Tab, Fun) -> end, do_traverse(TFun, [], Tab, Ref). +-spec update_counter(Name, Key, Increment) -> Result when + Name :: tab_name(), + Key :: term(), + Increment :: {Pos, Incr} | Incr, + Pos :: integer(), + Incr :: integer(), + Result :: integer(). + update_counter(Tab, Key, C) -> badarg(treq(Tab, {update_counter, Key, C}), [Tab, Key, C]). diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 7e51141098..2e9eba4bfa 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -80,7 +80,9 @@ new() -> Empty = mk_seg(?seg_size), #dict{empty=Empty,segs={Empty}}. --spec is_key(term(), dict()) -> boolean(). +-spec is_key(Key, Dict) -> boolean() when + Key :: term(), + Dict :: dict(). is_key(Key, D) -> Slot = get_slot(D, Key), @@ -91,21 +93,29 @@ find_key(K, [?kv(K,_Val)|_]) -> true; find_key(K, [_|Bkt]) -> find_key(K, Bkt); find_key(_, []) -> false. --spec to_list(dict()) -> [{term(), term()}]. +-spec to_list(Dict) -> List when + Dict :: dict(), + List :: [{Key :: term(), Value :: term()}]. to_list(D) -> fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D). --spec from_list([{term(), term()}]) -> dict(). +-spec from_list(List) -> Dict when + List :: [{Key :: term(), Value :: term()}], + Dict :: dict(). from_list(L) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L). --spec size(dict()) -> non_neg_integer(). +-spec size(Dict) -> non_neg_integer() when + Dict :: dict(). size(#dict{size=N}) when is_integer(N), N >= 0 -> N. --spec fetch(term(), dict()) -> term(). +-spec fetch(Key, Dict) -> Value when + Key :: term(), + Dict :: dict(), + Value :: term(). fetch(Key, D) -> Slot = get_slot(D, Key), @@ -119,7 +129,10 @@ fetch_val(K, [?kv(K,Val)|_]) -> Val; fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt); fetch_val(_, []) -> throw(badarg). --spec find(term(), dict()) -> {'ok', term()} | 'error'. +-spec find(Key, Dict) -> {'ok', Value} | 'error' when + Key :: term(), + Dict :: dict(), + Value :: term(). find(Key, D) -> Slot = get_slot(D, Key), @@ -130,12 +143,17 @@ find_val(K, [?kv(K,Val)|_]) -> {ok,Val}; find_val(K, [_|Bkt]) -> find_val(K, Bkt); find_val(_, []) -> error. --spec fetch_keys(dict()) -> [term()]. +-spec fetch_keys(Dict) -> Keys when + Dict :: dict(), + Keys :: [term()]. fetch_keys(D) -> fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D). --spec erase(term(), dict()) -> dict(). +-spec erase(Key, Dict1) -> Dict2 when + Key :: term(), + Dict1 :: dict(), + Dict2 :: dict(). %% Erase all elements with key Key. erase(Key, D0) -> @@ -150,7 +168,11 @@ erase_key(Key, [E|Bkt0]) -> {[E|Bkt1],Dc}; erase_key(_, []) -> {[],0}. --spec store(term(), term(), dict()) -> dict(). +-spec store(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). store(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -166,7 +188,11 @@ store_bkt_val(Key, New, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}. --spec append(term(), term(), dict()) -> dict(). +-spec append(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). append(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -182,7 +208,11 @@ append_bkt(Key, Val, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}. --spec append_list(term(), [term()], dict()) -> dict(). +-spec append_list(Key, ValList, Dict1) -> Dict2 when + Key :: term(), + ValList :: [Value :: term()], + Dict1 :: dict(), + Dict2 :: dict(). append_list(Key, L, D0) -> Slot = get_slot(D0, Key), @@ -252,7 +282,11 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}. %% {Bkt1,Dc} = on_key_bkt(Key, F, Bkt0), %% {[Other|Bkt1],Dc}. --spec update(term(), fun((term()) -> term()), dict()) -> dict(). +-spec update(Key, Fun, Dict1) -> Dict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, D0) -> Slot = get_slot(D0, Key), @@ -271,7 +305,12 @@ update_bkt(Key, F, [Other|Bkt0]) -> update_bkt(_Key, _F, []) -> throw(badarg). --spec update(term(), fun((term()) -> term()), term(), dict()) -> dict(). +-spec update(Key, Fun, Initial, Dict1) -> Dict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, Init, D0) -> Slot = get_slot(D0, Key), @@ -286,7 +325,11 @@ update_bkt(Key, F, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}. --spec update_counter(term(), number(), dict()) -> dict(). +-spec update_counter(Key, Increment, Dict1) -> Dict2 when + Key :: term(), + Increment :: number(), + Dict1 :: dict(), + Dict2 :: dict(). update_counter(Key, Incr, D0) when is_number(Incr) -> Slot = get_slot(D0, Key), @@ -301,20 +344,38 @@ counter_bkt(Key, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}. --spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term(). +-spec fold(Fun, Acc0, Dict) -> Acc1 when + Fun :: fun((Key, Value, AccIn) -> AccOut), + Key :: term(), + Value :: term(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Dict :: dict(). %% Fold function Fun over all "bags" in Table and return Accumulator. fold(F, Acc, D) -> fold_dict(F, Acc, D). --spec map(fun((term(), term()) -> term()), dict()) -> dict(). +-spec map(Fun, Dict1) -> Dict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). map(F, D) -> map_dict(F, D). --spec filter(fun((term(), term()) -> boolean()), dict()) -> dict(). +-spec filter(Pred, Dict1) -> Dict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Dict1 :: dict(), + Dict2 :: dict(). filter(F, D) -> filter_dict(F, D). --spec merge(fun((term(), term(), term()) -> term()), dict(), dict()) -> dict(). +-spec merge(Fun, Dict1, Dict2) -> Dict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Dict1 :: dict(), + Dict2 :: dict(), + Dict3 :: dict(). merge(F, D1, D2) when D1#dict.size < D2#dict.size -> fold_dict(fun (K, V1, D) -> diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl index 5edc868a94..e3f87d2c57 100644 --- a/lib/stdlib/src/digraph.erl +++ b/lib/stdlib/src/digraph.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,7 +53,8 @@ -type label() :: term(). -type vertex() :: term(). --type add_edge_err_rsn() :: {'bad_edge', [vertex()]} | {'bad_vertex', vertex()}. +-type add_edge_err_rsn() :: {'bad_edge', Path :: [vertex()]} + | {'bad_vertex', V :: vertex()}. %% %% Type is a list of @@ -70,7 +71,8 @@ new() -> new([]). --spec new([d_type()]) -> digraph(). +-spec new(Type) -> digraph() when + Type :: [d_type()]. new(Type) -> case check_type(Type, protected, []) of @@ -113,16 +115,20 @@ set_type([], G) -> G. %% Data access functions --spec delete(digraph()) -> 'true'. +-spec delete(G) -> 'true' when + G :: digraph(). delete(G) -> ets:delete(G#digraph.vtab), ets:delete(G#digraph.etab), ets:delete(G#digraph.ntab). --spec info(digraph()) -> [{'cyclicity', d_cyclicity()} | - {'memory', non_neg_integer()} | - {'protection', d_protection()}]. +-spec info(G) -> InfoList when + G :: digraph(), + InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} | + {'memory', NoWords :: non_neg_integer()} | + {'protection', Protection :: d_protection()}]. + info(G) -> VT = G#digraph.vtab, ET = G#digraph.etab, @@ -135,32 +141,45 @@ info(G) -> Memory = ets:info(VT, memory) + ets:info(ET, memory) + ets:info(NT, memory), [{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}]. --spec add_vertex(digraph()) -> vertex(). +-spec add_vertex(G) -> vertex() when + G :: digraph(). add_vertex(G) -> do_add_vertex({new_vertex_id(G), []}, G). --spec add_vertex(digraph(), vertex()) -> vertex(). +-spec add_vertex(G, V) -> vertex() when + G :: digraph(), + V :: vertex(). add_vertex(G, V) -> do_add_vertex({V, []}, G). --spec add_vertex(digraph(), vertex(), label()) -> vertex(). +-spec add_vertex(G, V, Label) -> vertex() when + G :: digraph(), + V :: vertex(), + Label :: label(). add_vertex(G, V, D) -> do_add_vertex({V, D}, G). --spec del_vertex(digraph(), vertex()) -> 'true'. +-spec del_vertex(G, V) -> 'true' when + G :: digraph(), + V :: vertex(). del_vertex(G, V) -> do_del_vertex(V, G). --spec del_vertices(digraph(), [vertex()]) -> 'true'. +-spec del_vertices(G, Vertices) -> 'true' when + G :: digraph(), + Vertices :: [vertex()]. del_vertices(G, Vs) -> do_del_vertices(Vs, G). --spec vertex(digraph(), vertex()) -> {vertex(), label()} | 'false'. +-spec vertex(G, V) -> {V, Label} | 'false' when + G :: digraph(), + V :: vertex(), + Label :: label(). vertex(G, V) -> case ets:lookup(G#digraph.vtab, V) of @@ -168,12 +187,15 @@ vertex(G, V) -> [Vertex] -> Vertex end. --spec no_vertices(digraph()) -> non_neg_integer(). +-spec no_vertices(G) -> non_neg_integer() when + G :: digraph(). no_vertices(G) -> ets:info(G#digraph.vtab, size). --spec vertices(digraph()) -> [vertex()]. +-spec vertices(G) -> Vertices when + G :: digraph(), + Vertices :: [vertex()]. vertices(G) -> ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]). @@ -188,85 +210,125 @@ source_vertices(G) -> sink_vertices(G) -> collect_vertices(G, out). --spec in_degree(digraph(), vertex()) -> non_neg_integer(). +-spec in_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). in_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {in, V})). --spec in_neighbours(digraph(), vertex()) -> [vertex()]. +-spec in_neighbours(G, V) -> Vertex when + G :: digraph(), + V :: vertex(), + Vertex :: [vertex()]. in_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {in, V}), ET, 2). --spec in_edges(digraph(), vertex()) -> [edge()]. +-spec in_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. in_edges(G, V) -> ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]). --spec out_degree(digraph(), vertex()) -> non_neg_integer(). +-spec out_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). out_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {out, V})). --spec out_neighbours(digraph(), vertex()) -> [vertex()]. +-spec out_neighbours(G, V) -> Vertices when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex()]. out_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {out, V}), ET, 3). --spec out_edges(digraph(), vertex()) -> [edge()]. +-spec out_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. out_edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]). --spec add_edge(digraph(), vertex(), vertex()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). add_edge(G, V1, V2) -> do_add_edge({new_edge_id(G), V1, V2, []}, G). --spec add_edge(digraph(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, V1, V2, D) -> do_add_edge({new_edge_id(G), V1, V2, D}, G). --spec add_edge(digraph(), edge(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, E, V1, V2, D) -> do_add_edge({E, V1, V2, D}, G). --spec del_edge(digraph(), edge()) -> 'true'. +-spec del_edge(G, E) -> 'true' when + G :: digraph(), + E :: edge(). del_edge(G, E) -> do_del_edges([E], G). --spec del_edges(digraph(), [edge()]) -> 'true'. +-spec del_edges(G, Edges) -> 'true' when + G :: digraph(), + Edges :: [edge()]. del_edges(G, Es) -> do_del_edges(Es, G). --spec no_edges(digraph()) -> non_neg_integer(). +-spec no_edges(G) -> non_neg_integer() when + G :: digraph(). no_edges(G) -> ets:info(G#digraph.etab, size). --spec edges(digraph()) -> [edge()]. +-spec edges(G) -> Edges when + G :: digraph(), + Edges :: [edge()]. edges(G) -> ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]). --spec edges(digraph(), vertex()) -> [edge()]. +-spec edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V},'$1'}, [], ['$1']}, {{{in, V}, '$1'}, [], ['$1']}]). --spec edge(digraph(), edge()) -> {edge(),vertex(),vertex(),label()} | 'false'. +-spec edge(G, E) -> {E, V1, V2, Label} | 'false' when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). edge(G, E) -> case ets:lookup(G#digraph.etab,E) of @@ -277,7 +339,7 @@ edge(G, E) -> %% %% Generate a "unique" edge identifier (relative to this graph) %% --spec new_edge_id(digraph()) -> nonempty_improper_list('$e', non_neg_integer()). +-spec new_edge_id(digraph()) -> edge(). new_edge_id(G) -> NT = G#digraph.ntab, @@ -289,7 +351,7 @@ new_edge_id(G) -> %% %% Generate a "unique" vertex identifier (relative to this graph) %% --spec new_vertex_id(digraph()) -> nonempty_improper_list('$v', non_neg_integer()). +-spec new_vertex_id(digraph()) -> vertex(). new_vertex_id(G) -> NT = G#digraph.ntab, @@ -444,7 +506,10 @@ acyclic_add_edge(E, V1, V2, Label, G) -> %% Delete all paths from vertex V1 to vertex V2 %% --spec del_path(digraph(), vertex(), vertex()) -> 'true'. +-spec del_path(G, V1, V2) -> 'true' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). del_path(G, V1, V2) -> case get_path(G, V1, V2) of @@ -463,7 +528,10 @@ del_path(G, V1, V2) -> %% been searched. %% --spec get_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_cycle(G, V) -> case one_path(out_neighbours(G, V), V, [], [V], [V], 2, G, 1) of @@ -481,7 +549,11 @@ get_cycle(G, V) -> %% if no path exists false is returned %% --spec get_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_path(G, V1, V2) -> one_path(out_neighbours(G, V1), V2, [], [V1], [V1], 1, G, 1). @@ -516,7 +588,10 @@ one_path([], _, [], _, _, _, _, _Counter) -> false. %% Like get_cycle/2, but a cycle of length one is preferred. %% --spec get_short_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_short_cycle(G, V) -> get_short_path(G, V, V). @@ -526,7 +601,11 @@ get_short_cycle(G, V) -> %% to find a short path. %% --spec get_short_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_short_path(G, V1, V2) -> T = new(), diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index 080cae4742..e221be15a1 100644 --- a/lib/stdlib/src/digraph_utils.erl +++ b/lib/stdlib/src/digraph_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,48 +38,66 @@ %% A convenient type alias %% --type vertices() :: [digraph:vertex()]. - %% %% Exported functions %% --spec components(digraph()) -> vertices(). +-spec components(Digraph) -> [Component] when + Digraph :: digraph(), + Component :: [digraph:vertex()]. components(G) -> forest(G, fun inout/3). --spec strong_components(digraph()) -> vertices(). +-spec strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. strong_components(G) -> forest(G, fun in/3, revpostorder(G)). --spec cyclic_strong_components(digraph()) -> vertices(). +-spec cyclic_strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. cyclic_strong_components(G) -> remove_singletons(strong_components(G), G, []). --spec reachable(vertices(), digraph()) -> vertices(). +-spec reachable(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, first)). --spec reachable_neighbours(vertices(), digraph()) -> vertices(). +-spec reachable_neighbours(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, not_first)). --spec reaching(vertices(), digraph()) -> vertices(). +-spec reaching(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, first)). --spec reaching_neighbours(vertices(), digraph()) -> vertices(). +-spec reaching_neighbours(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, not_first)). --spec topsort(digraph()) -> vertices() | 'false'. +-spec topsort(Digraph) -> Vertices | 'false' when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. topsort(G) -> L = revpostorder(G), @@ -88,12 +106,15 @@ topsort(G) -> false -> false end. --spec is_acyclic(digraph()) -> boolean(). +-spec is_acyclic(Digraph) -> boolean() when + Digraph :: digraph(). is_acyclic(G) -> loop_vertices(G) =:= [] andalso topsort(G) =/= false. --spec arborescence_root(digraph()) -> 'no' | {'yes', digraph:vertex()}. +-spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when + Digraph :: digraph(), + Root :: digraph:vertex(). arborescence_root(G) -> case digraph:no_edges(G) =:= digraph:no_vertices(G) - 1 of @@ -114,23 +135,30 @@ arborescence_root(G) -> no end. --spec is_arborescence(digraph()) -> boolean(). +-spec is_arborescence(Digraph) -> boolean() when + Digraph :: digraph(). is_arborescence(G) -> arborescence_root(G) =/= no. --spec is_tree(digraph()) -> boolean(). +-spec is_tree(Digraph) -> boolean() when + Digraph :: digraph(). is_tree(G) -> (digraph:no_edges(G) =:= digraph:no_vertices(G) - 1) andalso (length(components(G)) =:= 1). --spec loop_vertices(digraph()) -> vertices(). +-spec loop_vertices(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. loop_vertices(G) -> [V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)]. --spec subgraph(digraph(), vertices()) -> digraph(). +-spec subgraph(Digraph, Vertices) -> SubGraph when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + SubGraph :: digraph(). subgraph(G, Vs) -> try @@ -140,10 +168,12 @@ subgraph(G, Vs) -> erlang:error(badarg) end. --type option() :: {'type', 'inherit' | [digraph:d_type()]} - | {'keep_labels', boolean()}. - --spec subgraph(digraph(), vertices(), [option()]) -> digraph(). +-spec subgraph(Digraph, Vertices, Options) -> SubGraph when + Digraph :: digraph(), + SubGraph :: digraph(), + Vertices :: [digraph:vertex()], + Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}], + SubgraphType :: 'inherit' | [digraph:d_type()]. subgraph(G, Vs, Opts) -> try @@ -153,7 +183,9 @@ subgraph(G, Vs, Opts) -> erlang:error(badarg) end. --spec condensation(digraph()) -> digraph(). +-spec condensation(Digraph) -> CondensedDigraph when + Digraph :: digraph(), + CondensedDigraph :: digraph(). condensation(G) -> SCs = strong_components(G), @@ -176,12 +208,16 @@ condensation(G) -> ets:delete(I2C), SCG. --spec preorder(digraph()) -> vertices(). +-spec preorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. preorder(G) -> lists:reverse(revpreorder(G)). --spec postorder(digraph()) -> vertices(). +-spec postorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. postorder(G) -> lists:reverse(revpostorder(G)). diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index e5ccaddbb4..d804c1dee5 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,6 +29,7 @@ %%------------------------------------------------------------------------ -type macros() :: [{atom(), term()}]. +-type epp_handle() :: pid(). %% Epp state record. -record(epp, {file, %Current file @@ -61,14 +62,23 @@ %% parse_file(FileName, IncludePath, PreDefMacros) %% macro_defs(Epp) --spec open(file:name(), [file:name()]) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path) -> open(Name, Path, []). --spec open(file:name(), [file:name()], macros()) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath, PredefMacros) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + PredefMacros :: macros(), + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path, Pdm) -> Self = self(), @@ -80,7 +90,8 @@ open(Name, File, StartLocation, Path, Pdm) -> Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end), epp_request(Epp). --spec close(pid()) -> 'ok'. +-spec close(Epp) -> 'ok' when + Epp :: epp_handle(). close(Epp) -> %% Make sure that close is synchronous as a courtesy to test @@ -93,6 +104,13 @@ close(Epp) -> scan_erl_form(Epp) -> epp_request(Epp, scan_erl_form). +-spec parse_erl_form(Epp) -> + {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when + Epp :: epp_handle(), + AbsForm :: erl_parse:abstract_form(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + parse_erl_form(Epp) -> case epp_request(Epp, scan_erl_form) of {ok,Toks} -> @@ -107,6 +125,9 @@ macro_defs(Epp) -> %% format_error(ErrorDescriptor) -> String %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: term(). + format_error(cannot_parse) -> io_lib:format("cannot parse file, giving up", []); format_error({bad,W}) -> @@ -146,6 +167,16 @@ format_error(E) -> file:format_error(E). %% parse_file(FileName, IncludePath, [PreDefMacro]) -> %% {ok,[Form]} | {error,OpenError} +-spec parse_file(FileName, IncludePath, PredefMacros) -> + {'ok', [Form]} | {error, OpenError} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line}, + PredefMacros :: macros(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), + OpenError :: file:posix() | badarg | system_limit. + parse_file(Ifile, Path, Predefs) -> case open(Ifile, Path, Predefs) of {ok,Epp} -> diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index ea1b179ee5..46288cf467 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,6 +34,37 @@ -import(lists, [reverse/1,foldl/3,member/2]). +-export_type([binding_struct/0]). + +-type(expression() :: erl_parse:abstract_expr()). +-type(expressions() :: [erl_parse:abstract_expr()]). +-type(expression_list() :: [expression()]). +-type(clauses() :: [erl_parse:abstract_clause()]). +-type(name() :: term()). +-type(value() :: term()). +-type(bindings() :: [{name(), value()}]). +-opaque(binding_struct() :: orddict:orddict()). + +-type(lfun_value_handler() :: fun((Name :: atom(), + Arguments :: [term()]) -> + Value :: value())). +-type(lfun_eval_handler() :: fun((Name :: atom(), + Arguments :: expression_list(), + Bindings :: binding_struct()) -> + {value, + Value :: value(), + NewBindings :: binding_struct()})). +-type(local_function_handler() :: {value, lfun_value_handler()} + | {eval, lfun_eval_handler()} + | none). + +-type(func_spec() :: {Module :: module(), Function :: atom()} | function()). +-type(nlfun_handler() :: fun((FuncSpec :: func_spec(), + Arguments :: [term()]) -> + term())). +-type(non_local_function_handler() :: {value, nlfun_handler()} + | none). + %% exprs(ExpressionSeq, Bindings) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler, ExternalFuncHandler) @@ -45,6 +76,11 @@ %% that there are valid constructs in Expression to be taken care of %% by a function handler but considerad errors by erl_lint. +-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs) -> case check_command(Exprs, Bs) of ok -> @@ -53,9 +89,25 @@ exprs(Exprs, Bs) -> erlang:raise(error, Error, [{?MODULE,exprs,2}]) end. +-spec(exprs(Expressions, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf) -> exprs(Exprs, Bs, Lf, none, none). +-spec(exprs(Expressions, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf, Ef) -> exprs(Exprs, Bs, Lf, Ef, none). @@ -75,6 +127,11 @@ exprs([E|Es], Bs0, Lf, Ef, RBs) -> %% %% Only expr/2 checks the command by calling erl_lint. See exprs/2. +-spec(expr(Expression, Bindings) -> {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs) -> case check_command([E], Bs) of ok -> @@ -83,9 +140,25 @@ expr(E, Bs) -> erlang:raise(error, Error, [{?MODULE,expr,2}]) end. +-spec(expr(Expression, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf) -> expr(E, Bs, Lf, none, none). +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf, Ef) -> expr(E, Bs, Lf, Ef, none). @@ -114,6 +187,16 @@ fun_data(F) when is_function(F) -> fun_data(_T) -> false. +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler, ReturnFormat) -> + {value, Value, NewBindings} | Value when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ReturnFormat :: none | value, + Value :: value(), + NewBindings :: binding_struct()). expr({var,_,V}, Bs, _Lf, _Ef, RBs) -> case binding(V, Bs) of {value,Val} -> @@ -384,12 +467,9 @@ local_func(Func, As0, Bs0, {value,F}, value) -> local_func(Func, As0, Bs0, {value,F}, RBs) -> {As1,Bs1} = expr_list(As0, Bs0, {value,F}), ret_expr(F(Func, As1), Bs1, RBs); -local_func(Func, As0, Bs0, {value,F,Eas}, value) -> - {As1,_Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - apply(F, [Func,As1|Eas]); local_func(Func, As0, Bs0, {value,F,Eas}, RBs) -> - {As1,Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - ret_expr(apply(F, [Func,As1|Eas]), Bs1, RBs); + Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end, + local_func(Func, As0, Bs0, {value, Fun}, RBs); local_func(Func, As, Bs, {eval,F}, RBs) -> local_func2(F(Func, As, Bs), RBs); local_func(Func, As, Bs, {eval,F,Eas}, RBs) -> @@ -613,12 +693,33 @@ eval_fun([], As, _Bs, _Lf, _Ef, _RBs) -> %% expr_list(ExpressionList, Bindings, LocalFuncHandler, ExternalFuncHandler) %% Evaluate a list of expressions "in parallel" at the same level. +-spec(expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs) -> expr_list(Es, Bs, none, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf) -> expr_list(Es, Bs, Lf, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf, Ef) -> expr_list(Es, [], Bs, Bs, Lf, Ef). @@ -757,6 +858,15 @@ send_all([], _) -> true. %% match_clause -> {Body, Bindings} or nomatch +-spec(match_clause(Clauses, ValueList, Bindings, LocalFunctionHandler) -> + {Body, NewBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [value()], + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Body :: expression_list(), + NewBindings :: binding_struct()). + match_clause(Cs, Vs, Bs, Lf) -> match_clause(Cs, Vs, Bs, Lf, none). @@ -973,18 +1083,30 @@ match_list(_, _, _Bs, _BBs) -> %% add_binding(Name, Value, Bindings) %% del_binding(Name, Bindings) +-spec(new_bindings() -> binding_struct()). new_bindings() -> orddict:new(). +-spec(bindings(BindingStruct :: binding_struct()) -> bindings()). bindings(Bs) -> orddict:to_list(Bs). +-spec(binding(Name, BindingStruct) -> {value, value()} | unbound when + Name :: name(), + BindingStruct :: binding_struct()). binding(Name, Bs) -> case orddict:find(Name, Bs) of {ok,Val} -> {value,Val}; error -> unbound end. +-spec(add_binding(Name, Value, BindingStruct) -> binding_struct() when + Name :: name(), + Value :: value(), + BindingStruct :: binding_struct()). add_binding(Name, Val, Bs) -> orddict:store(Name, Val, Bs). +-spec(del_binding(Name, BindingStruct) -> binding_struct() when + Name :: name(), + BindingStruct :: binding_struct()). del_binding(Name, Bs) -> orddict:erase(Name, Bs). add_bindings(Bs1, Bs2) -> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 61ce41f714..eada563914 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,6 +38,10 @@ checked_ra=[] % succesfully accessed records }). +-spec(module(AbsForms, CompileOptions) -> AbsForms when + AbsForms :: [erl_parse:abstract_form()], + CompileOptions :: [compile:option()]). + %% Is is assumed that Fs is a valid list of forms. It should pass %% erl_lint without errors. module(Fs0, Opts0) -> diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index b30b02a96f..478f05e792 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,7 +54,9 @@ %%--------------------------------------------------------------------------- %% Erlang builtin functions allowed in guards. --spec guard_bif(Name::atom(), Arity::arity()) -> boolean(). +-spec guard_bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). guard_bif(abs, 1) -> true; guard_bif(float, 1) -> true; @@ -92,7 +94,9 @@ guard_bif(binary_part, 3) -> true; guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false. %% Erlang type tests. --spec type_test(Name::atom(), Arity::arity()) -> boolean(). +-spec type_test(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). type_test(Name, Arity) -> new_type_test(Name, Arity) orelse old_type_test(Name, Arity). @@ -135,7 +139,9 @@ old_type_test(record, 2) -> true; old_type_test(function, 1) -> true; old_type_test(Name, A) when is_atom(Name), is_integer(A) -> false. --spec arith_op(Op::atom(), Arity::arity()) -> boolean(). +-spec arith_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). arith_op('+', 1) -> true; arith_op('-', 1) -> true; @@ -153,7 +159,9 @@ arith_op('bsl', 2) -> true; arith_op('bsr', 2) -> true; arith_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec bool_op(Op::atom(), Arity::arity()) -> boolean(). +-spec bool_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). bool_op('not', 1) -> true; bool_op('and', 2) -> true; @@ -161,7 +169,9 @@ bool_op('or', 2) -> true; bool_op('xor', 2) -> true; bool_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec comp_op(Op::atom(), Arity::arity()) -> boolean(). +-spec comp_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). comp_op('==', 2) -> true; comp_op('/=', 2) -> true; @@ -173,18 +183,25 @@ comp_op('=:=', 2) -> true; comp_op('=/=', 2) -> true; comp_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec list_op(Op::atom(), Arity::arity()) -> boolean(). +-spec list_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). list_op('++', 2) -> true; list_op('--', 2) -> true; list_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec send_op(Op::atom(), Arity::arity()) -> boolean(). +-spec send_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). send_op('!', 2) -> true; send_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec op_type(atom(), arity()) -> 'arith' | 'bool' | 'comp' | 'list' | 'send'. +-spec op_type(OpName, Arity) -> Type when + OpName :: atom(), + Arity :: arity(), + Type :: 'arith' | 'bool' | 'comp' | 'list' | 'send'. op_type('+', 1) -> arith; op_type('-', 1) -> arith; @@ -221,7 +238,9 @@ op_type('!', 2) -> send. bif(erlang, Name, Arity) -> bif(Name, Arity); bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false. --spec bif(Name::atom(), Arity::arity()) -> boolean(). +-spec bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity::arity(). %% Returns true if erlang:Name/Arity is an auto-imported BIF, false otherwise. %% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF %% (meaning implemented in C) or not. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index cfb9f0ca98..dd0b9bc2ab 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -128,10 +128,15 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> }). -type lint_state() :: #lint{}. +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. %% format_error(Error) %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: error_description(). + format_error(undefined_module) -> "no module definition"; format_error({bad_module_name, M}) -> @@ -419,16 +424,39 @@ used_vars(Exprs, BindingsList) -> %% apply_lambda/2 has been called to shut lint up. N.B. these lists are %% really all ordsets! +-spec(module(AbsForms) -> {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms) -> Opts = compiler_options(Forms), St = forms(Forms, start("nofile", Opts)), return_status(St). +-spec(module(AbsForms, FileName) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName) -> Opts = compiler_options(Forms), St = forms(Forms, start(FileName, Opts)), return_status(St). +-spec(module(AbsForms, FileName, CompileOptions) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + CompileOptions :: [compile:option()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName, Opts0) -> %% We want the options given on the command line to take %% precedence over options in the module. @@ -1877,6 +1905,9 @@ gexpr_list(Es, Vt, St) -> %% is_guard_test(Expression) -> boolean(). %% Test if a general expression is a guard test. +-spec is_guard_test(Expr) -> boolean() when + Expr :: erl_parse:abstract_expr(). + is_guard_test(E) -> is_guard_test2(E, dict:new()). diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 15b45d72f4..bd5d65a1e1 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -511,6 +511,15 @@ Erlang code. %% of the generated .erl file by the HiPE compiler. Please do not remove. -compile([{hipe,[{regalloc,linear_scan}]}]). +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +-type abstract_clause() :: term(). +-type abstract_expr() :: term(). +-type abstract_form() :: term(). +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. +-type token() :: {Tag :: atom(), Line :: erl_scan:line()}. %% mkop(Op, Arg) -> {op,Line,Op,Arg}. %% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. @@ -534,11 +543,19 @@ Erlang code. %% These really suck and are only here until Calle gets multiple %% entry points working. +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> parse([{'-',L1},{'spec',L2}|Tokens]); parse_form(Tokens) -> parse(Tokens). +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). parse_exprs(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> @@ -546,6 +563,10 @@ parse_exprs(Tokens) -> {error,_} = Err -> Err end. +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). parse_term(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> @@ -830,6 +851,7 @@ check_clauses(Cs, Name, Arity) -> build_try(L,Es,Scs,{Ccs,As}) -> {'try',L,Es,Scs,Ccs,As}. +-spec ret_err(_, _) -> no_return(). ret_err(L, S) -> {location,Location} = get_attribute(L, location), return_error(Location, S). @@ -846,10 +868,11 @@ mapl(F, [H|T]) -> mapl(_, []) -> []. -%% normalise(AbsTerm) -%% abstract(Term) %% Convert between the abstract form of a term and a term. +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). normalise({char,_,C}) -> C; normalise({integer,_,I}) -> I; normalise({float,_,F}) -> F; @@ -887,6 +910,9 @@ normalise_list([H|T]) -> normalise_list([]) -> []. +-spec abstract(Data) -> AbsTerm when + Data :: term(), + AbsTerm :: abstract_expr(). abstract(T) when is_integer(T) -> {integer,0,T}; abstract(T) when is_float(T) -> {float,0,T}; abstract(T) when is_atom(T) -> {atom,0,T}; @@ -955,13 +981,18 @@ abstract_list([H|T], Line) -> abstract_list([], _Line) -> []. -%% tokens(AbsTerm) -> [Token] -%% tokens(AbsTerm, More) -> [Token] %% Generate a list of tokens representing the abstract term. +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. tokens(Abs) -> tokens(Abs, []). +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. tokens({char,L,C}, More) -> [{char,L,C}|More]; tokens({integer,L,N}, More) -> [{integer,L,N}|More]; tokens({float,L,F}, More) -> [{float,L,F}|More]; diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 66c80a45cb..7dc19f2e9b 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -31,25 +31,53 @@ -define(MAXLINE, 72). +-type(hook_function() :: none + | fun((Expr :: erl_parse:abstract_expr(), + CurrentIndentation :: integer(), + CurrentPrecedence :: non_neg_integer(), + HookFunction :: hook_function()) -> + io_lib:chars())). + %%% %%% Exported functions %%% +-spec(form(Form) -> io_lib:chars() when + Form :: erl_parse:abstract_form()). + form(Thing) -> form(Thing, none). +-spec(form(Form, HookFunction) -> io_lib:chars() when + Form :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + form(Thing, Hook) -> frmt(lform(Thing, Hook)). +-spec(attribute(Attribute) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form()). + attribute(Thing) -> attribute(Thing, none). +-spec(attribute(Attribute, HookFunction) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + attribute(Thing, Hook) -> frmt(lattribute(Thing, Hook)). +-spec(function(Function) -> io_lib:chars() when + Function :: erl_parse:abstract_form()). + function(F) -> function(F, none). +-spec(function(Function, HookFunction) -> io_lib:chars() when + Function :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + function(F, Hook) -> frmt(lfunction(F, Hook)). @@ -59,30 +87,67 @@ rule(R) -> rule(R, Hook) -> frmt(lrule(R, Hook)). +-spec(guard(Guard) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()]). + guard(Gs) -> guard(Gs, none). +-spec(guard(Guard, HookFunction) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + guard(Gs, Hook) -> frmt(lguard(Gs, Hook)). +-spec(exprs(Expressions) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()]). + exprs(Es) -> exprs(Es, 0, none). +-spec(exprs(Expressions, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + exprs(Es, Hook) -> exprs(Es, 0, Hook). +-spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + Indent :: integer(), + HookFunction :: hook_function()). + exprs(Es, I, Hook) -> frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +-spec(expr(Expression) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr()). + expr(E) -> frmt(lexpr(E, 0, none)). +-spec(expr(Expression, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + HookFunction :: hook_function()). + expr(E, Hook) -> frmt(lexpr(E, 0, Hook)). +-spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + HookFunction :: hook_function()). + expr(E, I, Hook) -> frmt(lexpr(E, 0, Hook), I). +-spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + Precedence :: non_neg_integer(), + HookFunction :: hook_function()). + expr(E, I, P, Hook) -> frmt(lexpr(E, P, Hook), I). diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 18f64c46d0..718ca2e91a 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -99,7 +99,8 @@ %%---------------------------------------------------------------------------- --spec format_error(Error :: term()) -> string(). +-spec format_error(ErrorDescriptor) -> string() when + ErrorDescriptor :: error_description(). format_error({string,Quote,Head}) -> lists:flatten(["unterminated " ++ string_thing(Quote) ++ " starting with " ++ @@ -112,20 +113,33 @@ format_error({base,Base}) -> format_error(Other) -> lists:flatten(io_lib:write(Other)). --type string_return() :: {'ok', tokens(), location()} - | {'error', error_info(), location()}. - --spec string(String :: string()) -> string_return(). +-spec string(String) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + EndLocation :: location(), + ErrorLocation :: location(). string(String) -> string(String, 1, []). --spec string(String :: string(), StartLocation :: location()) -> - string_return(). +-spec string(String, StartLocation) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, StartLocation) -> string(String, StartLocation, []). --spec string(String :: string(), StartLocation :: location(), - Options :: options()) -> string_return(). +-spec string(String, StartLocation, Options) -> Return when + String :: string(), + Options :: options(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, Line, Options) when ?STRING(String), ?ALINE(Line) -> string1(String, options(Options), Line, no_col, []); string(String, {Line,Column}, Options) when ?STRING(String), @@ -136,29 +150,37 @@ string(String, {Line,Column}, Options) when ?STRING(String), -type char_spec() :: string() | 'eof'. -type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(), tokens(), any()) -> any()). --opaque return_cont() :: {string(), column(), tokens(), line(), - #erl_scan{}, cont_fun(), any()}. --type cont() :: return_cont() | []. --type tokens_result() :: {'ok', tokens(), location()} - | {'eof', location()} - | {'error', error_info(), location()}. --type tokens_return() :: {'done', tokens_result(), char_spec()} - | {'more', return_cont()}. - --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location()) -> tokens_return(). +-opaque return_cont() :: {erl_scan_continuation, + string(), column(), tokens(), line(), + #erl_scan{}, any(), cont_fun()}. +-type tokens_result() :: {'ok', Tokens :: tokens(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', ErrorInfo :: error_info(), + EndLocation :: location()}. + +-spec tokens(Continuation, CharSpec, StartLocation) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens(Cont, CharSpec, StartLocation) -> tokens(Cont, CharSpec, StartLocation, []). --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location(), Options :: options()) -> - tokens_return(). +-spec tokens(Continuation, CharSpec, StartLocation, Options) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Options :: options(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens([], CharSpec, Line, Options) when ?ALINE(Line) -> tokens1(CharSpec, options(Options), Line, no_col, [], fun scan/6, []); tokens([], CharSpec, {Line,Column}, Options) when ?ALINE(Line), ?COLUMN(Column) -> tokens1(CharSpec, options(Options), Line, Column, [], fun scan/6, []); -tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> +tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, + CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). -type attribute_item() :: 'column' | 'length' | 'line' @@ -172,13 +194,22 @@ tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> -type token_info() :: {'category', category()} | {'symbol', symbol()} | attribute_info(). --spec token_info(token()) -> [token_info()]. +-spec token_info(Token) -> TokenInfo when + Token :: token(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(token(), token_item()) -> token_info() | 'undefined'; - (token(), [token_item()]) -> [token_info()]. +-spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when + Token :: token(), + TokenItem :: token_item(), + TokenInfo :: TokenInfoTuple :: token_info(); + (Token, TokenItems) -> [TokenInfo] when + Token :: token(), + TokenItems :: [TokenItem], + TokenItem :: token_item(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -201,14 +232,23 @@ token_info({_Category,Attrs}, Item) -> token_info({_Category,Attrs,_Symbol}, Item) -> attributes_info(Attrs, Item). --spec attributes_info(attributes()) -> [attribute_info()]. +-spec attributes_info(Attributes) -> AttributesInfo when + Attributes :: attributes(), + AttributesInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(attributes(), attribute_item()) -> - attribute_info() | 'undefined'; - (attributes(), [attribute_item()]) -> [attribute_info()]. +-spec attributes_info(Attributes, AttributeItem) -> + AttributeInfo | 'undefined' when + Attributes :: attributes(), + AttributeItem :: attribute_item(), + AttributeInfo :: AttributeInfoTuple :: attribute_info(); + (Attributes, AttributeItems) -> [AttributeInfo] when + Attributes :: attributes(), + AttributeItems :: [AttributeItem], + AttributeItem :: attribute_item(), + AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -265,9 +305,10 @@ attributes_info(Attrs, text=Item) -> attributes_info(T1, T2) -> erlang:error(badarg, [T1,T2]). --type setlineattr_fun() :: fun((info_line()) -> info_line()). - --spec set_attribute('line', attributes(), setlineattr_fun()) -> attributes(). +-spec set_attribute(AttributeItem, Attributes, SetAttributeFun) -> Attributes when + AttributeItem :: 'line', + Attributes :: attributes(), + SetAttributeFun :: fun((info_line()) -> info_line()). set_attribute(Tag, Attributes, Fun) when ?SETATTRFUN(Fun) -> set_attr(Tag, Attributes, Fun). @@ -374,7 +415,7 @@ set_attr(T1, T2, T3) -> tokens1(Cs, St, Line, Col, Toks, Fun, Any) when ?STRING(Cs); Cs =:= eof -> case Fun(Cs, St, Line, Col, Toks, Any) of {more,{Cs0,Ncol,Ntoks,Nline,Nany,Nfun}} -> - {more,{Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; + {more,{erl_scan_continuation,Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; {ok,Toks0,eof,Nline,Ncol} -> Res = case Toks0 of [] -> @@ -1285,7 +1326,7 @@ tabs(8) -> "\t\t\t\t\t\t\t\t"; tabs(9) -> "\t\t\t\t\t\t\t\t\t"; tabs(10) -> "\t\t\t\t\t\t\t\t\t\t". --spec reserved_word(atom()) -> boolean(). +-spec reserved_word(Atom :: atom()) -> boolean(). reserved_word('after') -> true; reserved_word('begin') -> true; reserved_word('case') -> true; diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 6e6e949e2c..afa914a456 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0]). +-export_type([tab/0, tid/0, match_spec/0]). %%----------------------------------------------------------------------------- @@ -51,22 +51,9 @@ %% a similar definition is also in erl_types -opaque tid() :: integer(). --type ext_info() :: 'md5sum' | 'object_count'. --type protection() :: 'private' | 'protected' | 'public'. --type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'. - --type table_info() :: {'name', atom()} - | {'type', type()} - | {'protection', protection()} - | {'named_table', boolean()} - | {'keypos', non_neg_integer()} - | {'size', non_neg_integer()} - | {'extended_info', [ext_info()]} - | {'version', {non_neg_integer(), non_neg_integer()}}. - %% these ones are also defined in erl_bif_types -type match_pattern() :: atom() | tuple(). --type match_specs() :: [{match_pattern(), [_], [_]}]. +-type match_spec() :: [{match_pattern(), [_], [_]}]. %%----------------------------------------------------------------------------- @@ -86,6 +73,7 @@ %% insert/2 %% is_compiled_ms/1 %% last/1 +%% member/2 %% next/2 %% prev/2 %% rename/2 @@ -101,11 +89,14 @@ %% select/1 %% select/2 %% select/3 +%% select_count/2 %% select_reverse/1 %% select_reverse/2 %% select_reverse/3 %% select_delete/2 +%% setopts/2 %% update_counter/3 +%% update_element/3 %% -opaque comp_match_spec() :: any(). %% this one is REALLY opaque @@ -119,7 +110,9 @@ match_spec_run(List, CompiledMS) -> | {tab(),integer(),integer(),binary(),list(),integer()} | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. --spec repair_continuation(continuation(), match_specs()) -> continuation(). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation when + Continuation :: continuation(), + MatchSpec :: match_spec(). %% $end_of_table is an allowed continuation in ets... repair_continuation('$end_of_table', _) -> @@ -153,7 +146,9 @@ repair_continuation(Untouched = {Table,N1,N2,Bin,L,N3}, MS) {Table,N1,N2,ets:match_spec_compile(MS),L,N3} end. --spec fun2ms(function()) -> match_specs(). +-spec fun2ms(LiteralFun) -> MatchSpec when + LiteralFun :: function(), + MatchSpec :: match_spec(). fun2ms(ShellFun) when is_function(ShellFun) -> %% Check that this is really a shell fun... @@ -177,7 +172,13 @@ fun2ms(ShellFun) when is_function(ShellFun) -> shell]}}) end. --spec foldl(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldl(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldl(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -198,7 +199,13 @@ do_foldl(F, Accu0, Key, T) -> ets:next(T, Key), T) end. --spec foldr(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldr(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldr(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -219,7 +226,9 @@ do_foldr(F, Accu0, Key, T) -> ets:prev(T, Key), T) end. --spec from_dets(tab(), dets:tab_name()) -> 'true'. +-spec from_dets(Tab, DetsTab) -> 'true' when + Tab :: tab(), + DetsTab :: dets:tab_name(). from_dets(EtsTable, DetsTable) -> case (catch dets:to_ets(DetsTable, EtsTable)) of @@ -235,7 +244,9 @@ from_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec to_dets(tab(), dets:tab_name()) -> dets:tab_name(). +-spec to_dets(Tab, DetsTab) -> DetsTab when + Tab :: tab(), + DetsTab :: dets:tab_name(). to_dets(EtsTable, DetsTable) -> case (catch dets:from_ets(DetsTable, EtsTable)) of @@ -251,8 +262,11 @@ to_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec test_ms(tuple(), match_specs()) -> - {'ok', term()} | {'error', [{'warning'|'error', string()}]}. +-spec test_ms(Tuple, MatchSpec) -> {'ok', Result} | {'error', Errors} when + Tuple :: tuple(), + MatchSpec :: match_spec(), + Result :: term(), + Errors :: [{'warning'|'error', string()}]. test_ms(Term, MS) -> case erlang:match_spec_test(Term, MS, table) of @@ -262,7 +276,11 @@ test_ms(Term, MS) -> Error end. --spec init_table(tab(), fun(('read' | 'close') -> term())) -> 'true'. +-spec init_table(Tab, InitFun) -> 'true' when + Tab :: tab(), + InitFun :: fun((Arg) -> Res), + Arg :: 'read' | 'close', + Res :: 'end_of_input' | {Objects :: [term()], InitFun} | term(). init_table(Table, Fun) -> ets:delete_all_objects(Table), @@ -287,7 +305,9 @@ init_table_sub(Table, [H|T]) -> ets:insert(Table, H), init_table_sub(Table, T). --spec match_delete(tab(), match_pattern()) -> 'true'. +-spec match_delete(Tab, Pattern) -> 'true' when + Tab :: tab(), + Pattern :: match_pattern(). match_delete(Table, Pattern) -> ets:select_delete(Table, [{Pattern,[],[true]}]), @@ -295,7 +315,9 @@ match_delete(Table, Pattern) -> %% Produce a list of tuples from a table --spec tab2list(tab()) -> [tuple()]. +-spec tab2list(Tab) -> [Object] when + Tab :: tab(), + Object :: tuple(). tab2list(T) -> ets:match_object(T, '_'). @@ -334,15 +356,21 @@ do_filter(Tab, Key, F, A, Ack) -> md5sum = false :: boolean() }). --type fname() :: string() | atom(). --type t2f_option() :: {'extended_info', [ext_info()]}. - --spec tab2file(tab(), fname()) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Reason :: term(). tab2file(Tab, File) -> tab2file(Tab, File, []). --spec tab2file(tab(), fname(), [t2f_option()]) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename, Options) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Options :: [Option], + Option :: {'extended_info', [ExtInfo]}, + ExtInfo :: 'md5sum' | 'object_count', + Reason :: term(). tab2file(Tab, File, Options) -> try @@ -501,14 +529,20 @@ parse_ft_info_options(_,Malformed) -> %% Opt := {verify,boolean()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --type f2t_option() :: {'verify', boolean()}. - --spec file2tab(fname()) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Reason :: term(). file2tab(File) -> file2tab(File, []). --spec file2tab(fname(), [f2t_option()]) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename, Options) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Options :: [Option], + Option :: {'verify', boolean()}, + Reason :: term(). file2tab(File, Opts) -> try @@ -895,7 +929,22 @@ named_table(false) -> []. %% information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec tabfile_info(fname()) -> {'ok', [table_info()]} | {'error', term()}. +-spec tabfile_info(Filename) -> {'ok', TableInfo} | {'error', Reason} when + Filename :: file:name(), + TableInfo :: [InfoItem], + InfoItem :: {'name', atom()} + | {'type', Type} + | {'protection', Protection} + | {'named_table', boolean()} + | {'keypos', non_neg_integer()} + | {'size', non_neg_integer()} + | {'extended_info', [ExtInfo]} + | {'version', {Major :: non_neg_integer(), + Minor :: non_neg_integer()}}, + ExtInfo :: 'md5sum' | 'object_count', + Type :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set', + Protection :: 'private' | 'protected' | 'public', + Reason :: term(). tabfile_info(File) when is_list(File) ; is_atom(File) -> try @@ -932,20 +981,22 @@ tabfile_info(File) when is_list(File) ; is_atom(File) -> {error,ExReason} end. --type qlc__query_handle() :: term(). %% XXX: belongs in 'qlc' - --type num_objects() :: 'default' | pos_integer(). --type trav_method() :: 'first_next' | 'last_prev' - | 'select' | {'select', match_specs()}. --type table_option() :: {'n_objects', num_objects()} - | {'traverse', trav_method()}. - --spec table(tab()) -> qlc__query_handle(). +-spec table(Tab) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(). table(Tab) -> table(Tab, []). --spec table(tab(), table_option() | [table_option()]) -> qlc__query_handle(). +-spec table(Tab, Options) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(), + Options :: [Option] | Option, + Option :: {'n_objects', NObjects} + | {'traverse', TraverseMethod}, + NObjects :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'last_prev' + | 'select' | {'select', MatchSpec :: match_spec()}. table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of @@ -1135,7 +1186,8 @@ to_string(X) -> lists:flatten(io_lib:format("~p", [X])). %% view a specific table --spec i(tab()) -> 'ok'. +-spec i(Tab) -> 'ok' when + Tab :: tab(). i(Tab) -> i(Tab, 40). diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index 2a5b08b581..3f31852afc 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,12 +50,68 @@ %%% Exported functions %%% +-export_type([reason/0]). + +-type(file_name() :: file:name()). +-type(file_names() :: [file:name()]). +-type(i_command() :: read | close). +-type(i_reply() :: end_of_input | {end_of_input, value()} + | {[object()], infun()} | input_reply()). +-type(infun() :: fun((i_command()) -> i_reply())). +-type(input() :: file_names() | infun()). +-type(input_reply() :: term()). +-type(o_command() :: {value, value()} | [object()] | close). +-type(o_reply() :: outfun() | output_reply()). +-type(object() :: term() | binary()). +-type(outfun() :: fun((o_command()) -> o_reply())). +-type(output() :: file_name() | outfun()). +-type(output_reply() :: term()). +-type(value() :: term()). + +-type(options() :: [option()] | option()). +-type(option() :: {compressed, boolean()} + | {header, header_length()} + | {format, format()} + | {no_files, no_files()} + | {order, order()} + | {size, size()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(format() :: binary_term | term | binary | format_fun()). +-type(format_fun() :: fun((binary()) -> term())). +-type(header_length() :: pos_integer()). +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(no_files() :: pos_integer()). % > 1 +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(size() :: non_neg_integer()). +-type(tmp_directory() :: [] | file:name()). + +-type(reason() :: bad_object + | {bad_object, file_name()} + | {bad_term, file_name()} + | {file_error, file_name(), + file:posix() | badarg | system_limit} + | {premature_eof, file_name()}). + +-spec(sort(FileName) -> Reply when + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(FileName) -> sort([FileName], FileName). +-spec(sort(Input, Output) -> Reply when + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input, Output) -> sort(Input, Output, []). +-spec(sort(Input, Output, Options) -> Reply when + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input0, Output0, Options) -> case {is_input(Input0), maybe_output(Output0), options(Options)} of {{true,Input}, {true,Output}, #opts{}=Opts} -> @@ -64,12 +120,27 @@ sort(Input0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Input0, Output0, Options]) end. +-spec(keysort(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, FileName) -> keysort(KeyPos, [FileName], FileName). +-spec(keysort(KeyPos, Input, Output) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input, Output) -> keysort(KeyPos, Input, Output, []). +-spec(keysort(KeyPos, Input, Output, Options) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input0, Output0, Options) -> R = case {is_keypos(KeyPos), is_input(Input0), maybe_output(Output0), options(Options)} of @@ -89,9 +160,18 @@ keysort(KeyPos, Input0, Output0, Options) -> badarg(culprit(O), [KeyPos, Input0, Output0, Options]) end. +-spec(merge(FileNames, Output) -> Reply when + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files, Output) -> merge(Files, Output, []). +-spec(merge(FileNames, Output, Options) -> Reply when + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files0, Output0, Options) -> case {is_files(Files0), maybe_output(Output0), options(Options)} of %% size not used @@ -101,9 +181,20 @@ merge(Files0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Output0, Options]) end. +-spec(keymerge(KeyPos, FileNames, Output) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files, Output) -> keymerge(KeyPos, Files, Output, []). +-spec(keymerge(KeyPos, FileNames, Output, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files0, Output0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), maybe_output(Output0), options(Options)} of @@ -123,9 +214,21 @@ keymerge(KeyPos, Files0, Output0, Options) -> badarg(culprit(O), [KeyPos, Files0, Output0, Options]) end. +-spec(check(FileName) -> Reply when + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). check(FileName) -> check([FileName], []). +-spec(check(FileNames, Options) -> Reply when + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). check(Files0, Options) -> case {is_files(Files0), options(Options)} of {{true,Files}, #opts{}=Opts} -> @@ -134,9 +237,23 @@ check(Files0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Options]) end. +-spec(keycheck(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). keycheck(KeyPos, FileName) -> keycheck(KeyPos, [FileName], []). +-spec(keycheck(KeyPos, FileNames, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). keycheck(KeyPos, Files0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), options(Options)} of {_, _, #opts{format = binary}} -> diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index c845b61204..d532cea187 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,13 +40,19 @@ erlang:error(UnUsUalVaRiAbLeNaMe) end). +-type filename() :: file:name(). +-type dirname() :: filename(). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec wildcard(file:name()) -> [file:filename()]. +-spec wildcard(Wildcard) -> [file:filename()] when + Wildcard :: filename() | dirname(). wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). --spec wildcard(file:name(), file:name() | atom()) -> [file:filename()]. +-spec wildcard(Wildcard, Cwd) -> [file:filename()] when + Wildcard :: filename() | dirname(), + Cwd :: dirname(). wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> @@ -57,7 +63,8 @@ wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). --spec is_dir(file:name()) -> boolean(). +-spec is_dir(Name) -> boolean() when + Name :: filename() | dirname(). is_dir(Dir) -> do_is_dir(Dir, file). @@ -65,7 +72,8 @@ is_dir(Dir) -> is_dir(Dir, Mod) when is_atom(Mod) -> do_is_dir(Dir, Mod). --spec is_file(file:name()) -> boolean(). +-spec is_file(Name) -> boolean() when + Name :: filename() | dirname(). is_file(File) -> do_is_file(File, file). @@ -73,7 +81,8 @@ is_file(File) -> is_file(File, Mod) when is_atom(Mod) -> do_is_file(File, Mod). --spec is_regular(file:name()) -> boolean(). +-spec is_regular(Name) -> boolean() when + Name :: filename(). is_regular(File) -> do_is_regular(File, file). @@ -81,7 +90,13 @@ is_regular(File) -> is_regular(File, Mod) when is_atom(Mod) -> do_is_regular(File, Mod). --spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _) -> _. +-spec fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut when + Dir :: dirname(), + RegExp :: string(), + Recursive :: boolean(), + Fun :: fun((F :: file:filename(), AccIn) -> AccOut), + AccIn :: term(), + AccOut :: term(). fold_files(Dir, RegExp, Recursive, Fun, Acc) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file). @@ -89,7 +104,8 @@ fold_files(Dir, RegExp, Recursive, Fun, Acc) -> fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod). --spec last_modified(file:name()) -> file:date_time() | 0. +-spec last_modified(Name) -> file:date_time() | 0 when + Name :: filename() | dirname(). last_modified(File) -> do_last_modified(File, file). @@ -97,7 +113,8 @@ last_modified(File) -> last_modified(File, Mod) when is_atom(Mod) -> do_last_modified(File, Mod). --spec file_size(file:name()) -> non_neg_integer(). +-spec file_size(Filename) -> non_neg_integer() when + Filename :: filename(). file_size(File) -> do_file_size(File, file). @@ -237,7 +254,9 @@ do_file_size(File, Mod) -> %% +type X = filename() | dirname() %% ensures that the directory name required to create D exists --spec ensure_dir(file:name()) -> 'ok' | {'error', file:posix()}. +-spec ensure_dir(Name) -> 'ok' | {'error', Reason} when + Name :: filename() | dirname(), + Reason :: file:posix(). ensure_dir("/") -> ok; ensure_dir(F) -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index 24abf1e977..1cb9e4a25e 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -61,12 +61,15 @@ %% (for WIN32): absname("/") -> "D:/" --spec absname(file:name()) -> file:filename(). +-spec absname(Filename) -> file:filename() when + Filename :: file:name(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(file:name(), file:filename()) -> file:filename(). +-spec absname(Filename, Dir) -> file:filename() when + Filename :: file:name(), + Dir :: file:filename(). absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) -> absname(Name,filename_string_to_binary(AbsBase)); absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) -> @@ -119,7 +122,9 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> %% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(file:filename(), file:name()) -> file:filename(). +-spec absname_join(Dir, Filename) -> file:filename() when + Dir :: file:filename(), + Filename :: file:name(). absname_join(AbsBase, Name) -> join(AbsBase, flatten(Name)). @@ -131,7 +136,8 @@ absname_join(AbsBase, Name) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(file:name()) -> file:filename(). +-spec basename(Filename) -> file:filename() when + Filename :: file:name(). basename(Name) when is_binary(Name) -> case os:type() of {win32,_} -> @@ -192,7 +198,9 @@ skip_prefix(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(file:name(), file:name()) -> file:filename(). +-spec basename(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). basename(Name, Ext) when is_binary(Name), is_list(Ext) -> basename(Name,filename_string_to_binary(Ext)); basename(Name, Ext) when is_list(Name), is_binary(Ext) -> @@ -240,7 +248,8 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(file:name()) -> file:filename(). +-spec dirname(Filename) -> file:filename() when + Filename :: file:name(). dirname(Name) when is_binary(Name) -> {Dsep,Drivesep} = separators(), SList = case Dsep of @@ -332,7 +341,8 @@ dirjoin1([H|T],Acc,Sep) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(file:name()) -> file:filename(). +-spec extension(Filename) -> file:filename() when + Filename :: file:name(). extension(Name) when is_binary(Name) -> {Dsep,_} = separators(), SList = case Dsep of @@ -374,7 +384,8 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join([file:filename()]) -> file:filename(). +-spec join(Components) -> file:filename() when + Components :: [file:filename()]. join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> @@ -386,7 +397,9 @@ join([Name]) when is_atom(Name) -> %% Joins two filenames with directory separators. --spec join(file:filename(), file:filename()) -> file:filename(). +-spec join(Name1, Name2) -> file:filename() when + Name1 :: file:filename(), + Name2 :: file:filename(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of @@ -494,7 +507,8 @@ append(Dir, Name) -> %% current working volume. (Windows only) %% Example: a:bar.erl, /temp/foo.erl --spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'. +-spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when + Path :: file:name(). pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); pathtype(Name) when is_list(Name) or is_binary(Name) -> @@ -547,7 +561,8 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(file:name()) -> file:filename(). +-spec rootname(Filename) -> file:filename() when + Filename :: file:name(). rootname(Name) when is_binary(Name) -> list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> @@ -576,7 +591,9 @@ rootname([], Root, _Ext, _OsType) -> %% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam" %% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo" --spec rootname(file:name(), file:name()) -> file:filename(). +-spec rootname(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). rootname(Name, Ext) when is_binary(Name), is_binary(Ext) -> list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext))); rootname(Name, Ext) when is_binary(Name) -> @@ -602,7 +619,9 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("foo/bar") -> ["foo", "bar"] %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] --spec split(file:name()) -> [file:filename()]. +-spec split(Filename) -> Components when + Filename :: file:name(), + Components :: [file:filename()]. split(Name) when is_binary(Name) -> case os:type() of {win32, _} -> win32_splitb(Name); @@ -695,7 +714,8 @@ split([], Comp, Components, OsType) -> %% will be converted to backslashes. On all platforms, the %% name will be normalized as done by join/1. --spec nativename(file:filename()) -> file:filename(). +-spec nativename(Path) -> file:filename() when + Path :: file:filename(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -747,12 +767,17 @@ separators() -> %% The paths in the {outdir, Path} and {i, Path} options are guaranteed %% to be absolute. --type rule() :: {string(), string()}. --type ecode() :: 'non_existing' | 'preloaded' | 'interpreted'. --type option() :: {'i', string()} | {'outdir', string()} | {'d', atom()}. - --spec find_src(atom() | string()) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod) -> Default = [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}], Rules = @@ -763,8 +788,18 @@ find_src(Mod) -> end, find_src(Mod, Rules). --spec find_src(atom() | string(), [rule()]) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam, Rules) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Rules :: [{BinSuffix :: string(), SourceSuffix :: string()}], + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod, Rules) when is_atom(Mod) -> find_src(atom_to_list(Mod), Rules); find_src(File0, Rules) when is_list(File0) -> @@ -890,7 +925,8 @@ major_os_type() -> %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(file:name()) -> file:filename(). +-spec flatten(Filename) -> file:filename() when + Filename :: file:name(). flatten(Bin) when is_binary(Bin) -> Bin; flatten(List) -> diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index fc5beb28b0..91d21d869c 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -197,6 +197,7 @@ %% Some types. -type gb_set_node() :: 'nil' | {term(), _, _}. +-opaque iter() :: [gb_set_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -205,38 +206,47 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec empty() -> gb_set(). +-spec empty() -> Set when + Set :: gb_set(). empty() -> {0, nil}. --spec new() -> gb_set(). +-spec new() -> Set when + Set :: gb_set(). new() -> empty(). --spec is_empty(gb_set()) -> boolean(). +-spec is_empty(Set) -> boolean() when + Set :: gb_set(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: gb_set(). size({Size, _}) -> Size. --spec singleton(term()) -> gb_set(). +-spec singleton(Element) -> gb_set() when + Element :: term(). singleton(Key) -> {1, {Key, nil, nil}}. --spec is_element(term(), gb_set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_element(Key, S) -> is_member(Key, S). --spec is_member(term(), gb_set()) -> boolean(). +-spec is_member(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_member(Key, {_, T}) -> is_member_1(Key, T). @@ -250,7 +260,10 @@ is_member_1(_, {_, _, _}) -> is_member_1(_, nil) -> false. --spec insert(term(), gb_set()) -> gb_set(). +-spec insert(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). insert(Key, {S, T}) -> S1 = S + 1, @@ -306,7 +319,9 @@ count({_, Sm, Bi}) -> count(nil) -> {1, 0}. --spec balance(gb_set()) -> gb_set(). +-spec balance(Set1) -> Set2 when + Set1 :: gb_set(), + Set2 :: gb_set(). balance({S, T}) -> {S, balance(T, S)}. @@ -331,12 +346,18 @@ balance_list_1([Key | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec add_element(term(), gb_set()) -> gb_set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add_element(X, S) -> add(X, S). --spec add(term(), gb_set()) -> gb_set(). +-spec add(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add(X, S) -> case is_member(X, S) of @@ -346,23 +367,33 @@ add(X, S) -> insert(X, S) end. --spec from_list([term()]) -> gb_set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: gb_set(). from_list(L) -> from_ordset(ordsets:from_list(L)). --spec from_ordset([term()]) -> gb_set(). +-spec from_ordset(List) -> Set when + List :: [term()], + Set :: gb_set(). from_ordset(L) -> S = length(L), {S, balance_list(L, S)}. --spec del_element(term(), gb_set()) -> gb_set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). del_element(Key, S) -> delete_any(Key, S). --spec delete_any(term(), gb_set()) -> gb_set(). +-spec delete_any(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete_any(Key, S) -> case is_member(Key, S) of @@ -372,7 +403,10 @@ delete_any(Key, S) -> S end. --spec delete(term(), gb_set()) -> gb_set(). +-spec delete(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}. @@ -394,7 +428,10 @@ merge(Smaller, Larger) -> {Key, Larger1} = take_smallest1(Larger), {Key, Smaller, Larger1}. --spec take_smallest(gb_set()) -> {term(), gb_set()}. +-spec take_smallest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T), @@ -406,7 +443,8 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --spec smallest(gb_set()) -> term(). +-spec smallest(Set) -> term() when + Set :: gb_set(). smallest({_, T}) -> smallest_1(T). @@ -416,7 +454,10 @@ smallest_1({Key, nil, _Larger}) -> smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_set()) -> {term(), gb_set()}. +-spec take_largest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_largest({S, T}) -> {Key, Smaller} = take_largest1(T), @@ -428,7 +469,8 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --spec largest(gb_set()) -> term(). +-spec largest(Set) -> term() when + Set :: gb_set(). largest({_, T}) -> largest_1(T). @@ -438,7 +480,9 @@ largest_1({Key, _Smaller, nil}) -> largest_1({_Key, _Smaller, Larger}) -> largest_1(Larger). --spec to_list(gb_set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: gb_set(), + List :: [term()]. to_list({_, T}) -> to_list(T, []). @@ -449,7 +493,9 @@ to_list({Key, Small, Big}, L) -> to_list(Small, [Key | to_list(Big, L)]); to_list(nil, L) -> L. --spec iterator(gb_set()) -> [term()]. +-spec iterator(Set) -> Iter when + Set :: gb_set(), + Iter :: iter(). iterator({_, T}) -> iterator(T, []). @@ -464,7 +510,10 @@ iterator({_, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([term()]) -> {term(), [term()]} | 'none'. +-spec next(Iter1) -> {Element, Iter2} | 'none' when + Iter1 :: iter(), + Iter2 :: iter(), + Element :: term(). next([{X, _, T} | As]) -> {X, iterator(T, As)}; @@ -494,7 +543,10 @@ next([]) -> %% traversing the elements can be devised, but they all have higher %% overhead. --spec union(gb_set(), gb_set()) -> gb_set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). union({N1, T1}, {N2, T2}) when N2 < N1 -> union(to_list_1(T2), N2, T1, N1); @@ -596,7 +648,9 @@ balance_revlist_1([Key | L], 1) -> balance_revlist_1(L, 0) -> {nil, L}. --spec union([gb_set()]) -> gb_set(). +-spec union(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). union([S | Ss]) -> union_list(S, Ss); @@ -609,7 +663,10 @@ union_list(S, []) -> S. %% The rest is modelled on the above. --spec intersection(gb_set(), gb_set()) -> gb_set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). intersection({N1, T1}, {N2, T2}) when N2 < N1 -> intersection(to_list_1(T2), N2, T1, N1); @@ -657,7 +714,9 @@ intersection_2([], _, As, S) -> intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. --spec intersection([gb_set(),...]) -> gb_set(). +-spec intersection(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). intersection([S | Ss]) -> intersection_list(S, Ss). @@ -666,7 +725,9 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(intersection(S, S1), Ss); intersection_list(S, []) -> S. --spec is_disjoint(gb_set(), gb_set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 -> is_disjoint_1(T1, T2); @@ -694,12 +755,18 @@ is_disjoint_1(_, nil) -> %% the sets. Therefore, we always build a new tree, and thus we need to %% traverse the whole element list of the left operand. --spec subtract(gb_set(), gb_set()) -> gb_set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). subtract(S1, S2) -> difference(S1, S2). --spec difference(gb_set(), gb_set()) -> gb_set(). +-spec difference(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). difference({N1, T1}, {N2, T2}) -> difference(to_list_1(T1), N1, T2, N2). @@ -747,7 +814,9 @@ difference_2(Xs, [], As, S) -> %% Subset testing is much the same thing as set difference, but %% without the construction of a new set. --spec is_subset(gb_set(), gb_set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_subset({N1, T1}, {N2, T2}) -> is_subset(to_list_1(T1), N1, T2, N2). @@ -788,18 +857,28 @@ is_subset_2(_, []) -> %% For compatibility with `sets': --spec is_set(term()) -> boolean(). +-spec is_set(Term) -> boolean() when + Term :: term(). is_set({0, nil}) -> true; is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. --spec filter(fun((term()) -> boolean()), gb_set()) -> gb_set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: gb_set(), + Set2 :: gb_set(). filter(F, S) -> from_ordset([X || X <- to_list(S), F(X)]). --spec fold(fun((term(), term()) -> term()), term(), gb_set()) -> term(). +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Set :: gb_set(). fold(F, A, {_, T}) when is_function(F, 2) -> fold_1(F, A, T). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index d37786a100..6ad861ff5b 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -153,6 +153,7 @@ %% Some types. -type gb_tree_node() :: 'nil' | {_, _, _, _}. +-opaque iter() :: [gb_tree_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -166,21 +167,26 @@ empty() -> {0, nil}. --spec is_empty(gb_tree()) -> boolean(). +-spec is_empty(Tree) -> boolean() when + Tree :: gb_tree(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_tree()) -> non_neg_integer(). +-spec size(Tree) -> non_neg_integer() when + Tree :: gb_tree(). size({Size, _}) when is_integer(Size), Size >= 0 -> Size. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec lookup(term(), gb_tree()) -> 'none' | {'value', term()}. +-spec lookup(Key, Tree) -> 'none' | {'value', Val} when + Key :: term(), + Val :: term(), + Tree :: gb_tree(). lookup(Key, {_, T}) -> lookup_1(Key, T). @@ -205,7 +211,9 @@ lookup_1(_, nil) -> %% This is a specialized version of `lookup'. --spec is_defined(term(), gb_tree()) -> boolean(). +-spec is_defined(Key, Tree) -> boolean() when + Key :: term(), + Tree :: gb_tree(). is_defined(Key, {_, T}) -> is_defined_1(Key, T). @@ -223,7 +231,10 @@ is_defined_1(_, nil) -> %% This is a specialized version of `lookup'. --spec get(term(), gb_tree()) -> term(). +-spec get(Key, Tree) -> Val when + Key :: term(), + Tree :: gb_tree(), + Val :: term(). get(Key, {_, T}) -> get_1(Key, T). @@ -237,7 +248,11 @@ get_1(_, {_, Value, _, _}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec update(term(), term(), gb_tree()) -> gb_tree(). +-spec update(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). update(Key, Val, {S, T}) -> T1 = update_1(Key, Val, T), @@ -254,7 +269,11 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec insert(term(), term(), gb_tree()) -> gb_tree(). +-spec insert(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). insert(Key, Val, {S, T}) when is_integer(S) -> S1 = S+1, @@ -303,7 +322,11 @@ insert_1(Key, _, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec enter(term(), term(), gb_tree()) -> gb_tree(). +-spec enter(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). enter(Key, Val, T) -> case is_defined(Key, T) of @@ -326,7 +349,9 @@ count(nil) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec balance(gb_tree()) -> gb_tree(). +-spec balance(Tree1) -> Tree2 when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). balance({S, T}) -> {S, balance(T, S)}. @@ -351,7 +376,9 @@ balance_list_1([{Key, Val} | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec from_orddict([{_,_}]) -> gb_tree(). +-spec from_orddict(List) -> Tree when + List :: [{Key :: term(), Val :: term()}], + Tree :: gb_tree(). from_orddict(L) -> S = length(L), @@ -359,7 +386,10 @@ from_orddict(L) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec delete_any(term(), gb_tree()) -> gb_tree(). +-spec delete_any(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete_any(Key, T) -> case is_defined(Key, T) of @@ -371,7 +401,10 @@ delete_any(Key, T) -> %%% delete. Assumes that key is present. --spec delete(term(), gb_tree()) -> gb_tree(). +-spec delete(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete(Key, {S, T}) when is_integer(S), S >= 0 -> {S - 1, delete_1(Key, T)}. @@ -397,7 +430,11 @@ merge(Smaller, Larger) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec take_smallest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_smallest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Larger} = take_smallest1(Tree), @@ -409,7 +446,10 @@ take_smallest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Smaller1} = take_smallest1(Smaller), {Key1, Value1, {Key, Value, Smaller1, Larger}}. --spec smallest(gb_tree()) -> {term(), term()}. +-spec smallest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). smallest({_, Tree}) -> smallest_1(Tree). @@ -419,7 +459,11 @@ smallest_1({Key, Value, nil, _Larger}) -> smallest_1({_Key, _Value, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_largest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_largest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Smaller} = take_largest1(Tree), @@ -431,7 +475,10 @@ take_largest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Larger1} = take_largest1(Larger), {Key1, Value1, {Key, Value, Smaller, Larger1}}. --spec largest(gb_tree()) -> {term(), term()}. +-spec largest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). largest({_, Tree}) -> largest_1(Tree). @@ -443,7 +490,10 @@ largest_1({_Key, _Value, _Smaller, Larger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec to_list(gb_tree()) -> [{term(), term()}]. +-spec to_list(Tree) -> [{Key, Val}] when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). to_list({_, T}) -> to_list(T, []). @@ -456,7 +506,9 @@ to_list(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec keys(gb_tree()) -> [term()]. +-spec keys(Tree) -> [Key] when + Tree :: gb_tree(), + Key :: term(). keys({_, T}) -> keys(T, []). @@ -467,7 +519,9 @@ keys(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec values(gb_tree()) -> [term()]. +-spec values(Tree) -> [Val] when + Tree :: gb_tree(), + Val :: term(). values({_, T}) -> values(T, []). @@ -478,7 +532,9 @@ values(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec iterator(gb_tree()) -> [gb_tree_node()]. +-spec iterator(Tree) -> Iter when + Tree :: gb_tree(), + Iter :: iter(). iterator({_, T}) -> iterator_1(T). @@ -496,7 +552,11 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([gb_tree_node()]) -> 'none' | {term(), term(), [gb_tree_node()]}. +-spec next(Iter1) -> 'none' | {Key, Val, Iter2} when + Iter1 :: iter(), + Iter2 :: iter(), + Key :: term(), + Val :: term(). next([{X, V, _, T} | As]) -> {X, V, iterator(T, As)}; @@ -505,7 +565,10 @@ next([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec map(fun((term(), term()) -> term()), gb_tree()) -> gb_tree(). +-spec map(Function, Tree1) -> Tree2 when + Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). map(F, {Size, Tree}) when is_function(F, 2) -> {Size, map_1(F, Tree)}. diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index b00910771f..1c4a73680b 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -44,6 +44,9 @@ system_code_change/4, format_status/2]). +-export_type([handler/0, handler_args/0, add_handler_ret/0, + del_handler_ret/0]). + -import(error_logger, [error_msg/2]). -define(reply(X), From ! {element(2,Tag), X}). @@ -113,7 +116,11 @@ behaviour_info(_Other) -> %%--------------------------------------------------------------------------- --type handler() :: atom() | {atom(), term()}. +-type handler() :: atom() | {atom(), term()}. +-type handler_args() :: term(). +-type add_handler_ret() :: ok | term() | {'EXIT',term()}. +-type del_handler_ret() :: ok | term() | {'EXIT',term()}. + -type emgr_name() :: {'local', atom()} | {'global', atom()}. -type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 6aeb076a0b..9f65bbfa3a 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -41,6 +41,7 @@ -type error_description() :: term(). % Whatever the io-server sends. -type request_error() :: {'error',error_description()}. + %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -66,12 +67,15 @@ o_request(Io, Request, Func) -> end. %% Put chars takes mixed *unicode* list from R13 onwards. --spec put_chars(iodata()) -> 'ok'. +-spec put_chars(CharData) -> 'ok' when + CharData :: unicode:chardata(). put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(device(), iodata()) -> 'ok'. +-spec put_chars(IoDevice, IoData) -> 'ok' when + IoDevice :: device(), + IoData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -81,7 +85,8 @@ put_chars(Io, Chars) -> nl() -> nl(default_output()). --spec nl(device()) -> 'ok'. +-spec nl(IoDevice) -> 'ok' when + IoDevice :: device(). nl(Io) -> % o_request(Io, {put_chars,io_lib:nl()}). @@ -92,7 +97,8 @@ nl(Io) -> columns() -> columns(default_output()). --spec columns(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). columns(Io) -> case request(Io, {get_geometry,columns}) of @@ -107,7 +113,8 @@ columns(Io) -> rows() -> rows(default_output()). --spec rows(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). rows(Io) -> case request(Io,{get_geometry,rows}) of @@ -117,22 +124,36 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(Prompt, Count) -> Data | 'eof' when + Prompt :: prompt(), + Count :: non_neg_integer(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(device(), prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when + IoDevice :: device(), + Prompt :: prompt(), + Count :: non_neg_integer(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Io, Prompt, N) when is_integer(N), N >= 0 -> request(Io, {get_chars,unicode,Prompt,N}). --spec get_line(prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when + Prompt :: prompt(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(device(), prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when + IoDevice :: device(), + Prompt :: prompt(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Io, Prompt) -> request(Io, {get_line,unicode,Prompt}). @@ -156,46 +177,62 @@ get_password(Io) -> getopts() -> getopts(default_input()). --spec getopts(device()) -> [opt_pair()]. +-spec getopts(IoDevice) -> [opt_pair()] when + IoDevice :: device(). getopts(Io) -> request(Io, getopts). -type setopt() :: 'binary' | 'list' | opt_pair(). --spec setopts([setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(Opts) -> 'ok' | {'error', Reason} when + Opts :: [setopt()], + Reason :: term(). setopts(Opts) -> setopts(default_input(), Opts). --spec setopts(device(), [setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when + IoDevice :: device(), + Opts :: [setopt()], + Reason :: term(). setopts(Io, Opts) -> request(Io, {setopts, Opts}). %% Writing and reading Erlang terms. --spec write(term()) -> 'ok'. +-spec write(Term) -> 'ok' when + Term :: term(). write(Term) -> write(default_output(), Term). --spec write(device(), term()) -> 'ok'. +-spec write(IoDevice, Term) -> 'ok' when + IoDevice :: device(), + Term :: term(). write(Io, Term) -> o_request(Io, {write,Term}, write). --spec read(prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(Prompt) -> Result when + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. % Read does not use get_until as erl_scan does not work with unicode % XXX:PaN fixme? read(Prompt) -> read(default_input(), Prompt). --spec read(device(), prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -211,9 +248,13 @@ read(Io, Prompt) -> Other end. --spec read(device(), prompt(), line()) -> - {'ok', term(), line()} | {'eof', line()} | - {'error', erl_scan:error_info(), line()}. +-spec read(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: {'ok', Term :: term(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}. read(Io, Prompt, StartLine) when is_integer(StartLine) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of @@ -239,28 +280,40 @@ conv_reason(_, _Reason) -> badarg. -type format() :: atom() | string() | binary(). --spec fwrite(format()) -> 'ok'. +-spec fwrite(Format) -> 'ok' when + Format :: format(). fwrite(Format) -> format(Format). --spec fwrite(format(), [term()]) -> 'ok'. +-spec fwrite(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fwrite(device(), format(), [term()]) -> 'ok'. +-spec fwrite(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. fwrite(Io, Format, Args) -> format(Io, Format, Args). --spec fread(prompt(), format()) -> {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(Prompt, Format) -> Result when + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Prompt, Format) -> fread(default_input(), Prompt, Format). --spec fread(device(), prompt(), format()) -> - {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(IoDevice, Prompt, Format) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -270,73 +323,104 @@ fread(Io, Prompt, Format) -> Other end. --spec format(format()) -> 'ok'. +-spec format(Format) -> 'ok' when + Format :: format(). format(Format) -> format(Format, []). --spec format(format(), [term()]) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. format(Format, Args) -> format(default_output(), Format, Args). --spec format(device(), format(), [term()]) -> 'ok'. +-spec format(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. format(Io, Format, Args) -> o_request(Io, {format,Format,Args}, format). %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt) -> Result when + Device :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). %% Parsing Erlang code. --type erl_parse_expr_list() :: [_]. %% XXX: should be imported from erl_parse - --type parse_ret() :: {'ok', erl_parse_expr_list(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_exprs(prompt()) -> parse_ret(). +-spec parse_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Prompt) -> parse_erl_exprs(default_input(), Prompt, 1). --spec parse_erl_exprs(device(), prompt()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(device(), prompt(), line()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of @@ -349,24 +433,31 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type erl_parse_absform() :: _. %% XXX: should be imported from erl_parse - --type parse_form_ret() :: {'ok', erl_parse_absform(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_form(prompt()) -> parse_form_ret(). +-spec parse_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Prompt) -> parse_erl_form(default_input(), Prompt, 1). --spec parse_erl_form(device(), prompt()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(device(), prompt(), line()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 4ca9d079b7..54c7283abf 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,35 +75,57 @@ collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). --export_type([chars/0]). +-export_type([chars/0, continuation/0]). %%---------------------------------------------------------------------- - %% XXX: overapproximates a deep list of (unicode) characters --type chars() :: [_]. +-type chars() :: [char() | chars()]. -type depth() :: -1 | non_neg_integer(). +-opaque continuation() :: {_, _, _, _}. % XXX: refine + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. --spec fwrite(io:format(), [term()]) -> chars(). +-spec fwrite(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()], + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fread(string(), string()) -> io_lib_fread:fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: chars()} + | {'error', What :: term()}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). --spec fread(io_lib_fread:continuation(), string(), string()) -> - io_lib_fread:fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: chars()} + | 'eof' + | {'error', What :: term()}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). --spec format(io:format(), [term()]) -> chars(). +-spec format(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()]. format(Format, Args) -> case catch io_lib_format:fwrite(Format, Args) of @@ -113,17 +135,24 @@ format(Format, Args) -> Other end. --spec print(term()) -> chars(). +-spec print(Term) -> chars() when + Term :: term(). print(Term) -> io_lib_pretty:print(Term). --spec print(term(), non_neg_integer(), non_neg_integer(), depth()) -> chars(). +-spec print(Term, Column, LineLength, Depth) -> chars() when + Term :: term(), + Column :: non_neg_integer(), + LineLength :: non_neg_integer(), + Depth :: depth(). print(Term, Column, LineLength, Depth) -> io_lib_pretty:print(Term, Column, LineLength, Depth). --spec indentation(string(), integer()) -> integer(). +-spec indentation(String, StartIndent) -> integer() when + String :: string(), + StartIndent :: integer(). indentation(Chars, Current) -> io_lib_format:indentation(Chars, Current). @@ -158,7 +187,8 @@ format_prompt(Format, Args) -> %% Return a (non-flattened) list of characters giving a printed %% representation of the term. write/3 is for backward compatibility. --spec write(term()) -> chars(). +-spec write(Term) -> chars() when + Term :: term(). write(Term) -> write(Term, -1). @@ -169,7 +199,9 @@ write(Term, D, true) -> write(Term, D, false) -> write(Term, D). --spec write(term(), depth()) -> chars(). +-spec write(Term, Depth) -> chars() when + Term :: term(), + Depth :: depth(). write(_Term, 0) -> "..."; write(Term, _D) when is_integer(Term) -> integer_to_list(Term); @@ -234,7 +266,8 @@ write_binary_body(B, _D) -> %% write_atom(Atom) -> [Char] %% Generate the list of characters needed to print an atom. --spec write_atom(atom()) -> chars(). +-spec write_atom(Atom) -> chars() when + Atom :: atom(). write_atom(Atom) -> Chars = atom_to_list(Atom), @@ -283,7 +316,8 @@ name_char(_) -> false. %% write_string([Char]) -> [Char] %% Generate the list of characters needed to print a string. --spec write_string(string()) -> chars(). +-spec write_string(String) -> chars() when + String :: string(). write_string(S) -> write_string(S, $"). %" @@ -330,7 +364,8 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. %% Generate the list of characters needed to print a character constant. %% Must special case SPACE, $\s, here. --spec write_char(char()) -> chars(). +-spec write_char(Char) -> chars() when + Char :: char(). write_char($\s) -> "$\\s"; %Must special case this. write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> @@ -346,7 +381,8 @@ write_unicode_char(Uni) -> %% Return true if CharList is a (possibly deep) list of characters, else %% false. --spec char_list(term()) -> boolean(). +-spec char_list(Term) -> boolean() when + Term :: term(). char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> char_list(Cs); @@ -362,7 +398,8 @@ unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; unicode_char_list([]) -> true; unicode_char_list(_) -> false. %Everything else is false --spec deep_char_list(term()) -> boolean(). +-spec deep_char_list(Term) -> boolean() when + Term :: term(). deep_char_list(Cs) -> deep_char_list(Cs, []). @@ -399,7 +436,8 @@ deep_unicode_char_list(_, _More) -> %Everything else is false %% Return true if CharList is a list of printable characters, else %% false. --spec printable_list(term()) -> boolean(). +-spec printable_list(Term) -> boolean() when + Term :: term(). printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_list(Cs); diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 33553692bc..52aa4d073c 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,36 +22,24 @@ -export([fread/2,fread/3]). --export_type([continuation/0, fread_2_ret/0, fread_3_ret/0]). - -import(lists, [reverse/1,reverse/2]). %%----------------------------------------------------------------------- -%% Local types -%%----------------------------------------------------------------------- - --type done_arg2() :: {'ok', io_lib:chars()} | 'eof' | {'error', term()}. - -%%----------------------------------------------------------------------- -%% Types also used in other files -%%----------------------------------------------------------------------- - --type continuation() :: [] | {_, _, _, _}. % XXX: refine - --type fread_2_ret() :: {'ok', io_lib:chars(), string()} - | {'more', string(), non_neg_integer(), io_lib:chars()} - | {'error', term()}. --type fread_3_ret() :: {'more', continuation()} - | {'done', done_arg2(), string()}. - -%%----------------------------------------------------------------------- %% fread(Continuation, CharList, FormatString) %% This is the main function into the re-entrant formatted reader. It %% repeatedly collects lines and calls fread/2 to format the input until %% all the format string has been used. And it counts the characters. --spec fread(io_lib_fread:continuation(), string(), string()) -> fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: io_lib:continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: io_lib:continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: io_lib:chars()} + | 'eof' + | {'error', What :: term()}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), @@ -106,7 +94,14 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> %% WHITE Skip white space %% X Literal X --spec fread(string(), string()) -> fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: io_lib:chars()} + | {'error', What :: term()}. fread(Format, Line) -> fread(Format, Line, 0, []). diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index b2cfb00de9..c303ae60b5 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,7 +38,9 @@ flush_receive() -> %% %% Functions for doing standard system format i/o. %% --spec error_message(atom() | string() | binary(), [term()]) -> 'ok'. +-spec error_message(Format, Args) -> 'ok' when + Format :: io:format(), + Args :: [term()]. error_message(Format, Args) -> io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]). @@ -55,17 +57,23 @@ progname() -> no_prog_name end. --spec nonl(string()) -> string(). +-spec nonl(String1) -> String2 when + String1 :: string(), + String2 :: string(). nonl([10]) -> []; nonl([]) -> []; nonl([H|T]) -> [H|nonl(T)]. --spec send(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec send(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). send(To, Msg) -> To ! Msg. --spec sendw(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec sendw(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). sendw(To, Msg) -> To ! {self(), Msg}, @@ -89,7 +97,7 @@ eval_str(Str) when is_list(Str) -> true -> case erl_parse:parse_exprs(Toks) of {ok, Exprs} -> - case catch erl_eval:exprs(Exprs, []) of + case catch erl_eval:exprs(Exprs, erl_eval:new_bindings()) of {value, Val, _} -> {ok, Val}; Other -> diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index c669c1f7c1..bba46e4cb6 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,13 +54,21 @@ %% append(X, Y) appends lists X and Y --spec append([T], [T]) -> [T]. +-spec append(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). append(L1, L2) -> L1 ++ L2. %% append(L) appends the list of lists L --spec append([[T]]) -> [T]. +-spec append(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). append([E]) -> E; append([H|T]) -> H ++ append(T); @@ -68,13 +76,20 @@ append([]) -> []. %% subtract(List1, List2) subtract elements in List2 form List1. --spec subtract([T], [T]) -> [T]. +-spec subtract(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). subtract(L1, L2) -> L1 -- L2. %% reverse(L) reverse all elements in the list L. Is now a BIF! --spec reverse([T]) -> [T]. +-spec reverse(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). reverse([] = L) -> L; @@ -93,13 +108,21 @@ reverse([A, B | L]) -> %% nth(N, L) returns the N`th element of the list L %% nthtail(N, L) returns the N`th tail of the list L --spec nth(pos_integer(), [T,...]) -> T. +-spec nth(N, List) -> Elem when + N :: pos_integer(), + List :: [T,...], + Elem :: T, + T :: term(). nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). --spec nthtail(non_neg_integer(), [T,...]) -> [T]. +-spec nthtail(N, List) -> Tail when + N :: non_neg_integer(), + List :: [T,...], + Tail :: [T], + T :: term(). nthtail(1, [_|T]) -> T; nthtail(N, [_|T]) when N > 1 -> @@ -108,7 +131,10 @@ nthtail(0, L) when is_list(L) -> L. %% prefix(Prefix, List) -> (true | false) --spec prefix([T], [T]) -> boolean(). +-spec prefix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). prefix([X|PreTail], [X|Tail]) -> prefix(PreTail, Tail); @@ -117,7 +143,10 @@ prefix([_|_], List) when is_list(List) -> false. %% suffix(Suffix, List) -> (true | false) --spec suffix([T], [T]) -> boolean(). +-spec suffix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). suffix(Suffix, List) -> Delta = length(List) - length(Suffix), @@ -125,7 +154,10 @@ suffix(Suffix, List) -> %% last(List) returns the last element in a list. --spec last([T,...]) -> T. +-spec last(List) -> Last when + List :: [T,...], + Last :: T, + T :: term(). last([E|Es]) -> last(E, Es). @@ -137,7 +169,10 @@ last(E, []) -> E. %% returns the sequence Min..Max %% Min <= Max and Min and Max must be integers --spec seq(integer(), integer()) -> [integer()]. +-spec seq(From, To) -> Seq when + From :: integer(), + To :: integer(), + Seq :: [integer()]. seq(First, Last) when is_integer(First), is_integer(Last), First-1 =< Last -> @@ -152,7 +187,11 @@ seq_loop(1, X, L) -> seq_loop(0, _, L) -> L. --spec seq(integer(), integer(), integer()) -> [integer()]. +-spec seq(From, To, Incr) -> Seq when + From :: integer(), + To :: integer(), + Incr :: integer(), + Seq :: [integer()]. seq(First, Last, Inc) when is_integer(First), is_integer(Last), is_integer(Inc) -> @@ -178,7 +217,8 @@ seq_loop(0, _, _, L) -> %% sum(L) returns the sum of the elements in L --spec sum([number()]) -> number(). +-spec sum(List) -> number() when + List :: [number()]. sum(L) -> sum(L, 0). @@ -188,7 +228,11 @@ sum([], Sum) -> Sum. %% duplicate(N, X) -> [X,X,X,.....,X] (N times) %% return N copies of X --spec duplicate(non_neg_integer(), T) -> [T]. +-spec duplicate(N, Elem) -> List when + N :: non_neg_integer(), + Elem :: T, + List :: [T], + T :: term(). duplicate(N, X) when is_integer(N), N >= 0 -> duplicate(N, X, []). @@ -197,7 +241,10 @@ duplicate(N, X, L) -> duplicate(N-1, X, [X|L]). %% min(L) -> returns the minimum element of the list L --spec min([T,...]) -> T. +-spec min(List) -> Min when + List :: [T,...], + Min :: T, + T :: term(). min([H|T]) -> min(T, H). @@ -207,7 +254,10 @@ min([], Min) -> Min. %% max(L) -> returns the maximum element of the list L --spec max([T,...]) -> T. +-spec max(List) -> Max when + List :: [T,...], + Max :: T, + T :: term(). max([H|T]) -> max(T, H). @@ -218,12 +268,21 @@ max([], Max) -> Max. %% sublist(List, Start, Length) %% Returns the sub-list starting at Start of length Length. --spec sublist([T], pos_integer(), non_neg_integer()) -> [T]. +-spec sublist(List1, Start, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Start :: pos_integer(), + Len :: non_neg_integer(), + T :: term(). sublist(List, S, L) when is_integer(L), L >= 0 -> sublist(nthtail(S-1, List), L). --spec sublist([T], non_neg_integer()) -> [T]. +-spec sublist(List1, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Len :: non_neg_integer(), + T :: term(). sublist(List, L) when is_integer(L), is_list(List) -> sublist_2(List, L). @@ -238,7 +297,11 @@ sublist_2(List, L) when is_list(List), L > 0 -> %% delete(Item, List) -> List' %% Delete the first occurrence of Item from the list L. --spec delete(T, [T]) -> [T]. +-spec delete(Elem, List1) -> List2 when + Elem :: T, + List1 :: [T], + List2 :: [T], + T :: term(). delete(Item, [Item|Rest]) -> Rest; delete(Item, [H|Rest]) -> @@ -248,7 +311,12 @@ delete(_, []) -> []. %% Return [{X0, Y0}, {X1, Y1}, ..., {Xn, Yn}] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zip([A], [B]) -> [{A, B}]. +-spec zip(List1, List2) -> List3 when + List1 :: [A], + List2 :: [B], + List3 :: [{A, B}], + A :: term(), + B :: term(). zip([X | Xs], [Y | Ys]) -> [{X, Y} | zip(Xs, Ys)]; zip([], []) -> []. @@ -256,7 +324,12 @@ zip([], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn]}, for a list [{X0, Y0}, %% {X1, Y1}, ..., {Xn, Yn}]. --spec unzip([{A, B}]) -> {[A], [B]}. +-spec unzip(List1) -> {List2, List3} when + List1 :: [{A, B}], + List2 :: [A], + List3 :: [B], + A :: term(), + B :: term(). unzip(Ts) -> unzip(Ts, [], []). @@ -266,7 +339,14 @@ unzip([], Xs, Ys) -> {reverse(Xs), reverse(Ys)}. %% Return [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}] for lists [X0, %% X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zip3([A], [B], [C]) -> [{A, B, C}]. +-spec zip3(List1, List2, List3) -> List4 when + List1 :: [A], + List2 :: [B], + List3 :: [C], + List4 :: [{A, B, C}], + A :: term(), + B :: term(), + C :: term(). zip3([X | Xs], [Y | Ys], [Z | Zs]) -> [{X, Y, Z} | zip3(Xs, Ys, Zs)]; zip3([], [], []) -> []. @@ -274,7 +354,14 @@ zip3([], [], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn], [Z0, Z1, ..., Zn]}, for %% a list [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}]. --spec unzip3([{A, B, C}]) -> {[A], [B], [C]}. +-spec unzip3(List1) -> {List2, List3, List4} when + List1 :: [{A, B, C}], + List2 :: [A], + List3 :: [B], + List4 :: [C], + A :: term(), + B :: term(), + C :: term(). unzip3(Ts) -> unzip3(Ts, [], [], []). @@ -286,7 +373,14 @@ unzip3([], Xs, Ys, Zs) -> %% Return [F(X0, Y0), F(X1, Y1), ..., F(Xn, Yn)] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zipwith(fun((X, Y) -> R), [X], [Y]) -> [R]. +-spec zipwith(Combine, List1, List2) -> List3 when + Combine :: fun((X, Y) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [T], + X :: term(), + Y :: term(), + T :: term(). zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)]; zipwith(F, [], []) when is_function(F, 2) -> []. @@ -294,7 +388,16 @@ zipwith(F, [], []) when is_function(F, 2) -> []. %% Return [F(X0, Y0, Z0), F(X1, Y1, Z1), ..., F(Xn, Yn, Zn)] for lists %% [X0, X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zipwith3(fun((X, Y, Z) -> R), [X], [Y], [Z]) -> [R]. +-spec zipwith3(Combine, List1, List2, List3) -> List4 when + Combine :: fun((X, Y, Z) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [T], + X :: term(), + Y :: term(), + Z :: term(), + T :: term(). zipwith3(F, [X | Xs], [Y | Ys], [Z | Zs]) -> [F(X, Y, Z) | zipwith3(F, Xs, Ys, Zs)]; @@ -303,7 +406,10 @@ zipwith3(F, [], [], []) when is_function(F, 3) -> []. %% sort(List) -> L %% sorts the list L --spec sort([T]) -> [T]. +-spec sort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). sort([X, Y | L] = L0) when X =< Y -> case L of @@ -350,7 +456,11 @@ sort_1(X, [], R) -> %% merge(List) -> L %% merges a list of sorted lists --spec merge([[T]]) -> [T]. +-spec merge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). merge(L) -> mergel(L, []). @@ -358,7 +468,14 @@ merge(L) -> %% merge3(X, Y, Z) -> L %% merges three sorted lists X, Y and Z --spec merge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec merge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). merge3(L1, [], L3) -> merge(L1, L3); @@ -382,7 +499,12 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) -> %% merge(X, Y) -> L %% merges two sorted lists X and Y --spec merge([X], [Y]) -> [(X | Y)]. +-spec merge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). merge(T1, []) -> T1; @@ -405,8 +527,9 @@ rmerge(T1, [H2 | T2]) -> %% in L - the elements in L can be atoms, numbers of strings. %% Returns a list of characters. --type concat_thing() :: atom() | integer() | float() | string(). --spec concat([concat_thing()]) -> string(). +-spec concat(Things) -> string() when + Things :: [Thing], + Thing :: atom() | integer() | float() | string(). concat(List) -> flatmap(fun thing_to_list/1, List). @@ -420,12 +543,17 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string %% flatten(List, Tail) %% Flatten a list, adding optional tail. --spec flatten([term()]) -> [term()]. +-spec flatten(DeepList) -> List when + DeepList :: [term() | DeepList], + List :: [term()]. flatten(List) when is_list(List) -> do_flatten(List, []). --spec flatten([term()], [term()]) -> [term()]. +-spec flatten(DeepList, Tail) -> List when + DeepList :: [term() | DeepList], + Tail :: [term()], + List :: [term()]. flatten(List, Tail) when is_list(List), is_list(Tail) -> do_flatten(List, Tail). @@ -440,7 +568,8 @@ do_flatten([], Tail) -> %% flatlength(List) %% Calculate the length of a list of lists. --spec flatlength([term()]) -> non_neg_integer(). +-spec flatlength(DeepList) -> non_neg_integer() when + DeepList :: [term() | DeepList]. flatlength(List) -> flatlength(List, 0). @@ -481,7 +610,12 @@ flatlength([], L) -> L. % keysearch3(Key, N, T); %keysearch3(Key, N, []) -> false. --spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keydelete(Key, N, TupleList1) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keydelete(K, N, L) when is_integer(N), N > 0 -> keydelete3(K, N, L). @@ -491,7 +625,12 @@ keydelete3(Key, N, [H|T]) -> [H|keydelete3(Key, N, T)]; keydelete3(_, _, []) -> []. --spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()]. +-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + NewTuple :: tuple(). keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keyreplace3(K, N, L, New). @@ -502,8 +641,12 @@ keyreplace3(Key, Pos, [H|T], New) -> [H|keyreplace3(Key, Pos, T, New)]; keyreplace3(_, _, [], _) -> []. --spec keytake(term(), pos_integer(), [tuple()]) -> - {'value', tuple(), [tuple()]} | 'false'. +-spec keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} | false when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + Tuple :: tuple(). keytake(Key, N, L) when is_integer(N), N > 0 -> keytake(Key, N, L, []). @@ -514,7 +657,12 @@ keytake(Key, N, [H|T], L) -> keytake(Key, N, T, [H|L]); keytake(_K, _N, [], _L) -> false. --spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...]. +-spec keystore(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple(), ...], + NewTuple :: tuple(). keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keystore2(K, N, L, New). @@ -526,7 +674,11 @@ keystore2(Key, N, [H|T], New) -> keystore2(_Key, _N, [], New) -> [New]. --spec keysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keysort(I, L) when is_integer(I), I > 0 -> case L of @@ -583,8 +735,13 @@ keysort_1(I, X, EX, [Y | L], R) -> keysort_1(_I, X, _EX, [], R) -> lists:reverse(R, [X]). --spec keymerge(pos_integer(), [X], [Y]) -> - [R] when X :: tuple(), Y :: tuple(), R :: tuple(). +-spec keymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -611,7 +768,11 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec ukeysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). ukeysort(I, L) when is_integer(I), I > 0 -> case L of @@ -676,8 +837,13 @@ ukeysort_1(I, X, EX, [Y | L]) -> ukeysort_1(_I, X, _EX, []) -> [X]. --spec ukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when X :: tuple(), Y :: tuple(). +-spec ukeymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> case L1 of @@ -704,7 +870,11 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()]. +-spec keymap(Fun, N, TupleList1) -> TupleList2 when + Fun :: fun((Term1 :: term()) -> Term2 :: term()), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()]. keymap(Fun, Index, [Tup|Tail]) -> [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)]; @@ -713,7 +883,11 @@ keymap(Fun, Index, []) when is_integer(Index), Index >= 1, %%% Suggestion from OTP-2948: sort and merge with Fun. --spec sort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec sort(Fun, List1) -> List2 when + Fun :: fun((A :: T, B :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). sort(Fun, []) when is_function(Fun, 2) -> []; @@ -727,7 +901,13 @@ sort(Fun, [X, Y | T]) -> fsplit_2(Y, X, Fun, T, [], []) end. --spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec merge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); @@ -743,7 +923,11 @@ rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> rmerge(Fun, T1, []) when is_function(Fun, 2) -> T1. --spec usort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec usort(Fun, List1) -> List2 when + Fun :: fun((T, T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). usort(Fun, [_] = L) when is_function(Fun, 2) -> L; @@ -770,7 +954,13 @@ usort_1(Fun, X, [Y | L]) -> ufsplit_2(Y, L, Fun, [X]) end. --spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec umerge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). umerge(Fun, [], T2) when is_function(Fun, 2) -> T2; @@ -789,7 +979,10 @@ rumerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% usort(List) -> L %% sorts the list L, removes duplicates --spec usort([T]) -> [T]. +-spec usort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). usort([X, Y | L] = L0) when X < Y -> case L of @@ -844,7 +1037,11 @@ usort_1(X, []) -> %% umerge(List) -> L %% merges a list of sorted lists without duplicates, removes duplicates --spec umerge([[T]]) -> [T]. +-spec umerge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). umerge(L) -> umergel(L). @@ -853,7 +1050,14 @@ umerge(L) -> %% merges three sorted lists X, Y and Z without duplicates, %% removes duplicates --spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec umerge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). umerge3(L1, [], L3) -> umerge(L1, L3); @@ -878,7 +1082,12 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) -> %% umerge(X, Y) -> L %% merges two sorted lists X and Y without duplicates, removes duplicates --spec umerge([X], [Y]) -> [(X | Y)]. +-spec umerge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). umerge([], T2) -> T2; @@ -924,7 +1133,10 @@ rumerge(T1, [H2 | T2]) -> %% There are also versions with an extra argument, ExtraArgs, which is a %% list of extra arguments to each call. --spec all(fun((T) -> boolean()), [T]) -> boolean(). +-spec all(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). all(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -933,7 +1145,10 @@ all(Pred, [Hd|Tail]) -> end; all(Pred, []) when is_function(Pred, 1) -> true. --spec any(fun((T) -> boolean()), [T]) -> boolean(). +-spec any(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). any(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -942,31 +1157,59 @@ any(Pred, [Hd|Tail]) -> end; any(Pred, []) when is_function(Pred, 1) -> false. --spec map(fun((D) -> R), [D]) -> [R]. +-spec map(Fun, List1) -> List2 when + Fun :: fun((A) -> B), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> []. --spec flatmap(fun((D) -> [R]), [D]) -> [R]. +-spec flatmap(Fun, List1) -> List2 when + Fun :: fun((A) -> [B]), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). flatmap(F, [Hd|Tail]) -> F(Hd) ++ flatmap(F, Tail); flatmap(F, []) when is_function(F, 1) -> []. --spec foldl(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldl(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) when is_function(F, 2) -> Accu. --spec foldr(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldr(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); foldr(F, Accu, []) when is_function(F, 2) -> Accu. --spec filter(Pred :: fun((T) -> boolean()), List :: [T]) -> [T]. +-spec filter(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). filter(Pred, List) when is_function(Pred, 1) -> [ E || E <- List, Pred(E) ]. @@ -974,7 +1217,12 @@ filter(Pred, List) when is_function(Pred, 1) -> %% Equivalent to {filter(F, L), filter(NotF, L)}, if NotF = 'fun(X) -> %% not F(X) end'. --spec partition(Pred :: fun((T) -> boolean()), List :: [T]) -> {[T], [T]}. +-spec partition(Pred, List) -> {Satisfying, NotSatisfying} when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + Satisfying :: [T], + NotSatisfying :: [T], + T :: term(). partition(Pred, L) -> partition(Pred, L, [], []). @@ -1000,14 +1248,26 @@ zf(F, [Hd|Tail]) -> end; zf(F, []) when is_function(F, 1) -> []. --spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'. +-spec foreach(Fun, List) -> ok when + Fun :: fun((Elem :: T) -> term()), + List :: [T], + T :: term(). foreach(F, [Hd|Tail]) -> F(Hd), foreach(F, Tail); foreach(F, []) when is_function(F, 1) -> ok. --spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), @@ -1015,7 +1275,16 @@ mapfoldl(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldr(F, Accu0, [Hd|Tail]) -> {Rs,Accu1} = mapfoldr(F, Accu0, Tail), @@ -1023,7 +1292,11 @@ mapfoldr(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec takewhile(fun((T) -> boolean()), [T]) -> [T]. +-spec takewhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). takewhile(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -1032,7 +1305,11 @@ takewhile(Pred, [Hd|Tail]) -> end; takewhile(Pred, []) when is_function(Pred, 1) -> []. --spec dropwhile(fun((T) -> boolean()), [T]) -> [T]. +-spec dropwhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). dropwhile(Pred, [Hd|Tail]=Rest) -> case Pred(Hd) of @@ -1041,7 +1318,12 @@ dropwhile(Pred, [Hd|Tail]=Rest) -> end; dropwhile(Pred, []) when is_function(Pred, 1) -> []. --spec splitwith(fun((T) -> boolean()), [T]) -> {[T], [T]}. +-spec splitwith(Pred, List) -> {List1, List2} when + Pred :: fun((T) -> boolean()), + List :: [T], + List1 :: [T], + List2 :: [T], + T :: term(). splitwith(Pred, List) when is_function(Pred, 1) -> splitwith(Pred, List, []). @@ -1054,7 +1336,12 @@ splitwith(Pred, [Hd|Tail], Taken) -> splitwith(Pred, [], Taken) when is_function(Pred, 1) -> {reverse(Taken),[]}. --spec split(non_neg_integer(), [T]) -> {[T], [T]}. +-spec split(N, List1) -> {List2, List3} when + N :: non_neg_integer(), + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). split(N, List) when is_integer(N), N >= 0, is_list(List) -> case split(N, List, []) of diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index 5fa5360fa1..f7f128dac7 100644 --- a/lib/stdlib/src/log_mf_h.erl +++ b/lib/stdlib/src/log_mf_h.erl @@ -27,14 +27,13 @@ %%----------------------------------------------------------------- --type dir() :: file:filename(). -type b() :: non_neg_integer(). -type f() :: 1..255. -type pred() :: fun((term()) -> boolean()). %%----------------------------------------------------------------- --record(state, {dir :: dir(), +-record(state, {dir :: file:filename(), maxB :: b(), maxF :: f(), curB :: b(), @@ -67,11 +66,23 @@ %% EventMgr = pid() | atom(). %%----------------------------------------------------------------- --spec init(dir(), b(), f()) -> {dir(), b(), f(), pred()}. +-opaque args() :: {file:filename(), b(), f(), pred()}. + + +-spec init(Dir, MaxBytes, MaxFiles) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Args :: args(). init(Dir, MaxB, MaxF) -> init(Dir, MaxB, MaxF, fun(_) -> true end). --spec init(dir(), b(), f(), pred()) -> {dir(), b(), f(), pred()}. +-spec init(Dir, MaxBytes, MaxFiles, Pred) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Pred :: fun((Event :: term()) -> boolean()), % pred() + Args :: args(). init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. @@ -79,7 +90,7 @@ init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. %% Call-back functions from gen_event %%----------------------------------------------------------------- --spec init({dir(), b(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. +-spec init({file:filename(), non_neg_integer(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. init({Dir, MaxB, MaxF, Pred}) when is_integer(MaxF), MaxF > 0, MaxF < 256 -> First = diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index b565eb20f4..48e22e53fa 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -66,6 +66,11 @@ %% %% Called by compiler or ets/dbg:fun2ms when errors/warnings occur %% + +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). + format_error({?WARN_SHADOW_VAR,Name}) -> lists:flatten( io_lib:format("variable ~p shadowed in ms_transform fun head", @@ -186,6 +191,12 @@ format_error(Else) -> %% %% Called when translating in shell %% + +-spec transform_from_shell(Dialect, Clauses, BoundEnvironment) -> term() when + Dialect :: ets | dbg, + Clauses :: [erl_parse:abstract_clause()], + BoundEnvironment :: erl_eval:binding_struct(). + transform_from_shell(Dialect, Clauses, BoundEnvironment) -> SaveFilename = setup_filename(), case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of @@ -211,6 +222,11 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) -> %% %% Called when translating during compiling %% + +-spec parse_transform(Forms, Options) -> Forms when + Forms :: [erl_parse:abstract_form()], + Options :: term(). + parse_transform(Forms, _Options) -> SaveFilename = setup_filename(), %io:format("Forms: ~p~n",[Forms]), diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl index 4e30c9eefd..45d3c84b3e 100644 --- a/lib/stdlib/src/orddict.erl +++ b/lib/stdlib/src/orddict.erl @@ -25,9 +25,11 @@ -export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). +-export_type([orddict/0]). + %%--------------------------------------------------------------------------- --type orddict() :: [{term(), term()}]. +-type orddict() :: [{Key :: term(), Value :: term()}]. %%--------------------------------------------------------------------------- @@ -35,45 +37,63 @@ new() -> []. --spec is_key(Key::term(), Dictionary::orddict()) -> boolean(). +-spec is_key(Key, Orddict) -> boolean() when + Key :: term(), + Orddict :: orddict(). is_key(Key, [{K,_}|_]) when Key < K -> false; is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict); is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K is_key(_, []) -> false. --spec to_list(orddict()) -> [{term(), term()}]. +-spec to_list(Orddict) -> List when + Orddict :: orddict(), + List :: [{Key :: term(), Value :: term()}]. to_list(Dict) -> Dict. --spec from_list([{term(), term()}]) -> orddict(). +-spec from_list(List) -> Orddict when + List :: [{Key :: term(), Value :: term()}], + Orddict :: orddict(). from_list(Pairs) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, [], Pairs). --spec size(orddict()) -> non_neg_integer(). +-spec size(Orddict) -> non_neg_integer() when + Orddict :: orddict(). size(D) -> length(D). --spec fetch(Key::term(), Dictionary::orddict()) -> term(). +-spec fetch(Key, Orddict) -> Value when + Key :: term(), + Value :: term(), + Orddict :: orddict(). fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D); fetch(Key, [{K,Value}|_]) when Key == K -> Value. --spec find(Key::term(), Dictionary::orddict()) -> {'ok', term()} | 'error'. +-spec find(Key, Orddict) -> {'ok', Value} | 'error' when + Key :: term(), + Orddict :: orddict(), + Value :: term(). find(Key, [{K,_}|_]) when Key < K -> error; find(Key, [{K,_}|D]) when Key > K -> find(Key, D); find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K find(_, []) -> error. --spec fetch_keys(Dictionary::orddict()) -> [term()]. +-spec fetch_keys(Orddict) -> Keys when + Orddict :: orddict(), + Keys :: [term()]. fetch_keys([{Key,_}|Dict]) -> [Key|fetch_keys(Dict)]; fetch_keys([]) -> []. --spec erase(Key::term(), Dictionary::orddict()) -> orddict(). +-spec erase(Key, Orddict1) -> Orddict2 when + Key :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict]; erase(Key, [{K,_}=E|Dict]) when Key > K -> @@ -81,7 +101,11 @@ erase(Key, [{K,_}=E|Dict]) when Key > K -> erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K erase(_, []) -> []. --spec store(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec store(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). store(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,New},E|Dict]; @@ -91,7 +115,11 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K [{Key,New}|Dict]; store(Key, New, []) -> [{Key,New}]. --spec append(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec append(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). append(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,[New]},E|Dict]; @@ -101,7 +129,11 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K [{Key,Old ++ [New]}|Dict]; append(Key, New, []) -> [{Key,[New]}]. --spec append_list(Key::term(), ValueList::[term()], orddict()) -> orddict(). +-spec append_list(Key, ValList, Orddict1) -> Orddict2 when + Key :: term(), + ValList :: [Value :: term()], + Orddict1 :: orddict(), + Orddict2 :: orddict(). append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K -> [{Key,NewList},E|Dict]; @@ -112,14 +144,23 @@ append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K append_list(Key, NewList, []) -> [{Key,NewList}]. --spec update(Key::term(), Fun::fun((term()) -> term()), orddict()) -> orddict(). +-spec update(Key, Fun, Orddict1) -> Orddict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, Fun, [{K,_}=E|Dict]) when Key > K -> [E|update(Key, Fun, Dict)]; update(Key, Fun, [{K,Val}|Dict]) when Key == K -> [{Key,Fun(Val)}|Dict]. --spec update(term(), fun((term()) -> term()), term(), orddict()) -> orddict(). +-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, _, Init, [{K,_}=E|Dict]) when Key < K -> [{Key,Init},E|Dict]; @@ -129,7 +170,11 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K [{Key,Fun(Val)}|Dict]; update(Key, _, Init, []) -> [{Key,Init}]. --spec update_counter(Key::term(), Incr::number(), orddict()) -> orddict(). +-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when + Key :: term(), + Increment :: number(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K -> [{Key,Incr},E|Dict]; @@ -139,19 +184,29 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K [{Key,Val+Incr}|Dict]; update_counter(Key, Incr, []) -> [{Key,Incr}]. --spec fold(fun((term(),term(),term()) -> term()), term(), orddict()) -> term(). +-spec fold(Fun, Acc0, Orddict) -> Acc1 when + Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()), + Acc0 :: term(), + Acc1 :: term(), + Orddict :: orddict(). fold(F, Acc, [{Key,Val}|D]) -> fold(F, F(Key, Val, Acc), D); fold(F, Acc, []) when is_function(F, 3) -> Acc. --spec map(fun((term(), term()) -> term()), orddict()) -> orddict(). +-spec map(Fun, Orddict1) -> Orddict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). map(F, [{Key,Val}|D]) -> [{Key,F(Key, Val)}|map(F, D)]; map(F, []) when is_function(F, 2) -> []. --spec filter(fun((term(), term()) -> boolean()), orddict()) -> orddict(). +-spec filter(Pred, Orddict1) -> Orddict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). filter(F, [{Key,Val}=E|D]) -> case F(Key, Val) of @@ -160,8 +215,11 @@ filter(F, [{Key,Val}=E|D]) -> end; filter(F, []) when is_function(F, 2) -> []. --spec merge(fun((term(), term(), term()) -> term()), orddict(), orddict()) -> - orddict(). +-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(), + Orddict3 :: orddict(). merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 -> [E1|merge(F, D1, [E2|D2])]; diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 5a1c260703..4a8b1275b2 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -40,7 +40,8 @@ new() -> []. %% is_set(Term) -> boolean(). %% Return 'true' if Set is an ordered set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Ordset) -> boolean() when + Ordset :: term(). is_set([E|Es]) -> is_set(Es, E); is_set([]) -> true; @@ -54,21 +55,26 @@ is_set([], _) -> true. %% size(OrdSet) -> int(). %% Return the number of elements in OrdSet. --spec size(ordset(_)) -> non_neg_integer(). +-spec size(Ordset) -> non_neg_integer() when + Ordset :: ordset(_). size(S) -> length(S). %% to_list(OrdSet) -> [Elem]. %% Return the elements in OrdSet as a list. --spec to_list(ordset(T)) -> [T]. +-spec to_list(Ordset) -> List when + Ordset :: ordset(T), + List :: [T]. to_list(S) -> S. %% from_list([Elem]) -> Set. %% Build an ordered set from the elements in List. --spec from_list([T]) -> ordset(T). +-spec from_list(List) -> Ordset when + List :: [T], + Ordset :: ordset(T). from_list(L) -> lists:usort(L). @@ -76,7 +82,9 @@ from_list(L) -> %% is_element(Element, OrdSet) -> boolean(). %% Return 'true' if Element is an element of OrdSet, else 'false'. --spec is_element(term(), ordset(_)) -> boolean(). +-spec is_element(Element, Ordset) -> boolean() when + Element :: term(), + Ordset :: ordset(_). is_element(E, [H|Es]) when E > H -> is_element(E, Es); is_element(E, [H|_]) when E < H -> false; @@ -86,7 +94,12 @@ is_element(_, []) -> false. %% add_element(Element, OrdSet) -> OrdSet. %% Return OrdSet with Element inserted in it. --spec add_element(E, ordset(T)) -> [T | E,...]. +-spec add_element(Element, Ordset1) -> Ordset2 when + Element :: E, + Ordset1 :: ordset(T), + Ordset2 :: ordset(T | E). + +%-spec add_element(E, ordset(T)) -> [T | E,...]. add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; @@ -96,7 +109,10 @@ add_element(E, []) -> [E]. %% del_element(Element, OrdSet) -> OrdSet. %% Return OrdSet but with Element removed. --spec del_element(term(), ordset(T)) -> ordset(T). +-spec del_element(Element, Ordset1) -> Ordset2 when + Element :: term(), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)]; del_element(E, [H|_]=Set) when E < H -> Set; @@ -106,7 +122,10 @@ del_element(_, []) -> []. %% union(OrdSet1, OrdSet2) -> OrdSet %% Return the union of OrdSet1 and OrdSet2. --spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2). +-spec union(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(T1), + Ordset2 :: ordset(T2), + Ordset3 :: ordset(T1 | T2). union([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|union(Es1, Set2)]; @@ -120,7 +139,9 @@ union(Es1, []) -> Es1. %% union([OrdSet]) -> OrdSet %% Return the union of the list of ordered sets. --spec union([ordset(T)]) -> ordset(T). +-spec union(OrdsetList) -> Ordset when + OrdsetList :: [ordset(T)], + Ordset :: ordset(T). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); @@ -133,7 +154,10 @@ union1(S1, []) -> S1. %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. --spec intersection(ordset(_), ordset(_)) -> ordset(_). +-spec intersection(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). intersection([E1|Es1], [E2|_]=Set2) when E1 < E2 -> intersection(Es1, Set2); @@ -149,7 +173,9 @@ intersection(_, []) -> %% intersection([OrdSet]) -> OrdSet. %% Return the intersection of the list of ordered sets. --spec intersection([ordset(_),...]) -> ordset(_). +-spec intersection(OrdsetList) -> Ordset when + OrdsetList :: [ordset(_),...], + Ordset :: ordset(_). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); @@ -162,7 +188,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(OrdSet1, OrdSet2) -> boolean(). %% Check whether OrdSet1 and OrdSet2 are disjoint. --spec is_disjoint(ordset(_), ordset(_)) -> boolean(). +-spec is_disjoint(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_disjoint([E1|Es1], [E2|_]=Set2) when E1 < E2 -> is_disjoint(Es1, Set2); @@ -179,7 +207,10 @@ is_disjoint(_, []) -> %% Return all and only the elements of OrdSet1 which are not also in %% OrdSet2. --spec subtract(ordset(_), ordset(_)) -> ordset(_). +-spec subtract(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). subtract([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|subtract(Es1, Set2)]; @@ -194,7 +225,9 @@ subtract(Es1, []) -> Es1. %% Return 'true' when every element of OrdSet1 is also a member of %% OrdSet2, else 'false'. --spec is_subset(ordset(_), ordset(_)) -> boolean(). +-spec is_subset(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_subset([E1|_], [E2|_]) when E1 < E2 -> %E1 not in Set2 false; @@ -208,7 +241,11 @@ is_subset(_, []) -> false. %% fold(Fun, Accumulator, OrdSet) -> Accumulator. %% Fold function Fun over all elements in OrdSet and return Accumulator. --spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term(). +-spec fold(Function, Acc0, Ordset) -> Acc1 when + Function :: fun((Element :: T, AccIn :: term()) -> AccOut :: term()), + Ordset :: ordset(T), + Acc0 :: term(), + Acc1 :: term(). fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). @@ -216,7 +253,10 @@ fold(F, Acc, Set) -> %% filter(Fun, OrdSet) -> OrdSet. %% Filter OrdSet with Fun. --spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T). +-spec filter(Pred, Ordset1) -> Ordset2 when + Pred :: fun((Element :: T) -> boolean()), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). filter(F, Set) -> lists:filter(F, Set). diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index 5c52dfcbf0..39d017d430 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -180,6 +180,9 @@ obsolete_1(calendar, local_time_to_universal_time, 1) -> obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> {deprecated, {rpc, multi_server_call, A}}; + +%% *** SNMP *** + obsolete_1(snmp, N, A) -> case is_snmp_agent_function(N, A) of false -> @@ -189,9 +192,100 @@ obsolete_1(snmp, N, A) -> integer_to_list(A)++" instead"} end; +obsolete_1(snmpm, agent_info, 3) -> + {deprecated, {snmpm, agent_info, 2}, "R16B"}; +obsolete_1(snmpm, update_agent_info, 5) -> + {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; +obsolete_1(snmpm, g, 3) -> + {deprecated, {snmpm, sync_get, 3}, "R16B"}; +obsolete_1(snmpm, g, 4) -> + {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, g, 5) -> + {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, g, 6) -> + {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, g, 7) -> + {deprecated, {snmpm, sync_get, 6}, "R16B"}; +obsolete_1(snmpm, ag, 3) -> + {deprecated, {snmpm, async_get, 3}, "R16B"}; +obsolete_1(snmpm, ag, 4) -> + {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, ag, 5) -> + {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, ag, 6) -> + {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, ag, 7) -> + {deprecated, {snmpm, async_get, 6}, "R16B"}; +obsolete_1(snmpm, gn, 3) -> + {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; +obsolete_1(snmpm, gn, 4) -> + {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, gn, 5) -> + {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, gn, 6) -> + {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, gn, 7) -> + {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; +obsolete_1(snmpm, agn, 3) -> + {deprecated, {snmpm, async_get_next, 3}, "R16B"}; +obsolete_1(snmpm, agn, 4) -> + {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, agn, 5) -> + {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, agn, 6) -> + {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, agn, 7) -> + {deprecated, {snmpm, async_get_next, 6}, "R16B"}; +obsolete_1(snmpm, s, 3) -> + {deprecated, {snmpm, sync_set, 3}, "R16B"}; +obsolete_1(snmpm, s, 4) -> + {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, s, 5) -> + {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, s, 6) -> + {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, s, 7) -> + {deprecated, {snmpm, sync_set, 6}, "R16B"}; +obsolete_1(snmpm, as, 3) -> + {deprecated, {snmpm, async_set, 3}, "R16B"}; +obsolete_1(snmpm, as, 4) -> + {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, as, 5) -> + {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, as, 6) -> + {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, as, 7) -> + {deprecated, {snmpm, async_set, 6}, "R16B"}; +obsolete_1(snmpm, gb, 5) -> + {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, gb, 6) -> + {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, gb, 7) -> + {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, gb, 8) -> + {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, gb, 9) -> + {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; +obsolete_1(snmpm, agb, 5) -> + {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, agb, 6) -> + {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, agb, 7) -> + {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, agb, 8) -> + {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, agb, 9) -> + {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + + +%% *** MEGACO *** + obsolete_1(megaco, format_versions, 1) -> {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"}; + +%% *** OS-MON-MIB *** + obsolete_1(os_mon_mib, init, 1) -> {deprecated, {os_mon_mib, load, 1}}; obsolete_1(os_mon_mib, stop, 1) -> diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl index 503654e706..ee177e4e0b 100644 --- a/lib/stdlib/src/pg.erl +++ b/lib/stdlib/src/pg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,9 @@ %% Create a brand new empty process group with the master residing %% at the local node --spec create(term()) -> 'ok' | {'error', term()}. +-spec create(PgName) -> 'ok' | {'error', Reason} when + PgName :: term(), + Reason :: 'already_created' | term(). create(PgName) -> catch begin check(PgName), @@ -46,7 +48,10 @@ create(PgName) -> %% Create a brand new empty process group with the master %% residing at Node --spec create(term(), node()) -> 'ok' | {'error', term()}. +-spec create(PgName, Node) -> 'ok' | {'error', Reason} when + PgName :: term(), + Node :: node(), + Reason :: 'already_created' | term(). create(PgName, Node) -> catch begin check(PgName), @@ -66,7 +71,10 @@ standby(_PgName, _Node) -> %% Tell process group PgName that Pid is a new member of the group %% synchronously return a list of all old members in the group --spec join(atom(), pid()) -> [pid()]. +-spec join(PgName, Pid) -> Members when + PgName :: term(), + Pid :: pid(), + Members :: [pid()]. join(PgName, Pid) when is_atom(PgName) -> global:send(PgName, {join,self(),Pid}), @@ -77,7 +85,9 @@ join(PgName, Pid) when is_atom(PgName) -> %% Multi cast Mess to all members in the group --spec send(atom() | pid(), term()) -> 'ok'. +-spec send(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). send(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {send, self(), Mess}), @@ -89,7 +99,9 @@ send(Pg, Mess) when is_pid(Pg) -> %% multi cast a message to all members in the group but ourselves %% If we are a member --spec esend(atom() | pid(), term()) -> 'ok'. +-spec esend(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). esend(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {esend,self(),Mess}), @@ -100,7 +112,9 @@ esend(Pg, Mess) when is_pid(Pg) -> %% Return the members of the group --spec members(atom() | pid()) -> [pid()]. +-spec members(PgName) -> Members when + PgName :: term(), + Members :: [pid()]. members(PgName) when is_atom(PgName) -> global:send(PgName, {self() ,members}), diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl index a3c9927ee9..a5eb191ab2 100644 --- a/lib/stdlib/src/pool.erl +++ b/lib/stdlib/src/pool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -52,11 +52,16 @@ %% Start up using the .hosts.erlang file --spec start(atom()) -> [node()]. +-spec start(Name) -> Nodes when + Name :: atom(), + Nodes :: [node()]. start(Name) -> start(Name,[]). --spec start(atom(), string()) -> [node()]. +-spec start(Name, Args) -> Nodes when + Name :: atom(), + Args :: string(), + Nodes :: [node()]. start(Name, Args) when is_atom(Name) -> gen_server:start({global, pool_master}, pool, [], []), Hosts = net_adm:host_file(), @@ -71,7 +76,8 @@ start(Name, Args) when is_atom(Name) -> get_nodes() -> get_elements(2, get_nodes_and_load()). --spec attach(node()) -> 'already_attached' | 'attached'. +-spec attach(Node) -> 'already_attached' | 'attached' when + Node :: node(). attach(Node) -> gen_server:call({global, pool_master}, {attach, Node}). @@ -82,11 +88,17 @@ get_nodes_and_load() -> get_node() -> gen_server:call({global, pool_master}, get_node). --spec pspawn(module(), atom(), [term()]) -> pid(). +-spec pspawn(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn(M, F, A) -> gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}). --spec pspawn_link(module(), atom(), [term()]) -> pid(). +-spec pspawn_link(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn_link(M, F, A) -> P = pspawn(M, F, A), link(P), diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 4fb64a3353..02bcbb5a60 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,77 +40,108 @@ -type priority_level() :: 'high' | 'low' | 'max' | 'normal'. -type spawn_option() :: 'link' + | 'monitor' | {'priority', priority_level()} | {'min_heap_size', non_neg_integer()} + | {'min_bin_vheap_size', non_neg_integer()} | {'fullsweep_after', non_neg_integer()}. --type dict_or_pid() :: pid() | [_] | {integer(), integer(), integer()}. +-type dict_or_pid() :: pid() + | (ProcInfo :: [_]) + | {X :: integer(), Y :: integer(), Z :: integer()}. %%----------------------------------------------------------------------------- --spec spawn(function()) -> pid(). +-spec spawn(Fun) -> pid() when + Fun :: function(). spawn(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(atom(), atom(), [term()]) -> pid(). +-spec spawn(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(function()) -> pid(). +-spec spawn_link(Fun) -> pid() when + Fun :: function(). spawn_link(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn(node(), function()) -> pid(). +-spec spawn(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. + spawn(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(node(), function()) -> pid(). +-spec spawn_link(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn_link(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_opt(function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Fun, SpawnOpts) -> pid() when + Fun :: function(), + SpawnOpts :: [spawn_option()]. + spawn_opt(F, Opts) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts). --spec spawn_opt(node(), function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Function, SpawnOpts) -> pid() when + Node :: node(), + Function :: function(), + SpawnOpts :: [spawn_option()]. spawn_opt(Node, F, Opts) when is_function(F) -> Parent = get_my_name(), @@ -118,7 +149,11 @@ spawn_opt(Node, F, Opts) when is_function(F) -> check_for_monitor(Opts), erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts). --spec spawn_opt(atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -126,7 +161,12 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts). --spec spawn_opt(node(), atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -144,7 +184,10 @@ check_for_monitor(SpawnOpts) -> false end. --spec hibernate(module(), atom(), [term()]) -> no_return(). +-spec hibernate(Module, Function, Args) -> no_return() when + Module :: module(), + Function :: atom(), + Args :: [term()]. hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:hibernate(?MODULE, wake_up, [M, F, A]). @@ -210,35 +253,65 @@ exit_p(Class, Reason) -> exit(Reason) end. --spec start(atom(), atom(), [term()]) -> term(). +-spec start(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start(M, F, A, infinity). --spec start(atom(), atom(), [term()], timeout()) -> term(). +-spec start(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn(M, F, A), sync_wait(Pid, Timeout). --spec start(atom(), atom(), [term()], timeout(), [spawn_option()]) -> term(). +-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, SpawnOpts), sync_wait(Pid, Timeout). --spec start_link(atom(), atom(), [term()]) -> term(). +-spec start_link(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start_link(M, F, A, infinity). --spec start_link(atom(), atom(), [term()], timeout()) -> term(). +-spec start_link(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_link(M, F, A), sync_wait(Pid, Timeout). --spec start_link(atom(),atom(),[term()],timeout(),[spawn_option()]) -> term(). +-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)), @@ -267,13 +340,17 @@ flush(Pid) -> true end. --spec init_ack(pid(), term()) -> 'ok'. +-spec init_ack(Parent, Ret) -> 'ok' when + Parent :: pid(), + Ret :: term(). init_ack(Parent, Return) -> Parent ! {ack, self(), Return}, ok. --spec init_ack(term()) -> 'ok'. +-spec init_ack(Ret) -> 'ok' when + Ret :: term(). + init_ack(Return) -> [Parent|_] = get('$ancestors'), init_ack(Parent, Return). @@ -282,7 +359,11 @@ init_ack(Return) -> %% Fetch the initial call of a proc_lib spawned process. %% ----------------------------------------------------- --spec initial_call(dict_or_pid()) -> {atom(), atom(), [atom()]} | 'false'. +-spec initial_call(Process) -> {Module, Function, Args} | 'false' when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Args :: [atom()]. initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -305,7 +386,11 @@ make_dummy_args(N, Acc) -> %% This function is typically called from c:i() and c:regs(). %% ----------------------------------------------------- --spec translate_initial_call(dict_or_pid()) -> mfa(). +-spec translate_initial_call(Process) -> {Module, Function, Arity} when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Arity :: byte(). translate_initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -577,7 +662,8 @@ check(Res) -> Res. %%% Format (and write) a generated crash info structure. %%% ----------------------------------------------------------- --spec format([term()]) -> string(). +-spec format(CrashReport) -> string() when + CrashReport :: [term()]. format([OwnReport,LinkReport]) -> OwnFormat = format_report(OwnReport), diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index 6a45e0f868..68697d0da2 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ %% overriding the default settings, object properties, annotations, %% etc.</p> %% -%% @type property() = atom() | tuple() +%% % @type property() = atom() | tuple() -module(proplists). @@ -53,14 +53,8 @@ -type property() :: atom() | tuple(). --type aliases() :: [{any(), any()}]. --type negations() :: [{any(), any()}]. --type expansions() :: [{property(), [any()]}]. - %% --------------------------------------------------------------------- -%% @spec property(P::property()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a property. If %% <code>P</code> is <code>{Key, true}</code> where <code>Key</code> is %% an atom, this returns <code>Key</code>, otherwise the whole term @@ -68,7 +62,8 @@ %% %% @see property/2 --spec property(property()) -> property(). +-spec property(Property) -> Property when + Property :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -76,8 +71,6 @@ property(Property) -> Property. -%% @spec property(Key::term(), Value::term()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a simple %% key/value property. Returns <code>Key</code> if <code>Value</code> is %% <code>true</code> and <code>Key</code> is an atom, otherwise a tuple @@ -85,7 +78,10 @@ property(Property) -> %% %% @see property/1 --spec property(Key::term(), Value::term()) -> atom() | {term(), term()}. +-spec property(Key, Value) -> Property when + Key :: term(), + Value :: term(), + Property :: atom() | {term(), term()}. property(Key, true) when is_atom(Key) -> Key; @@ -95,14 +91,13 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @spec unfold(List::[term()]) -> [term()] -%% %% @doc Unfolds all occurences of atoms in <code>List</code> to tuples %% <code>{Atom, true}</code>. %% %% @see compact/1 --spec unfold(List::[term()]) -> [term()]. +-spec unfold(List) -> List when + List :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -113,15 +108,14 @@ unfold([P | Ps]) -> unfold([]) -> []. -%% @spec compact(List::[term()]) -> [term()] -%% %% @doc Minimizes the representation of all entries in the list. This is %% equivalent to <code>[property(P) || P <- List]</code>. %% %% @see unfold/1 %% @see property/1 --spec compact(List::[property()]) -> [property()]. +-spec compact(List) -> List when + List :: [property()]. compact(List) -> [property(P) || P <- List]. @@ -129,8 +123,6 @@ compact(List) -> %% --------------------------------------------------------------------- -%% @spec lookup(Key::term(), List::[term()]) -> none | tuple() -%% %% @doc Returns the first entry associated with <code>Key</code> in %% <code>List</code>, if one exists, otherwise returns %% <code>none</code>. For an atom <code>A</code> in the list, the tuple @@ -140,7 +132,9 @@ compact(List) -> %% @see get_value/2 %% @see get_bool/2 --spec lookup(Key::term(), List::[term()]) -> 'none' | tuple(). +-spec lookup(Key, List) -> 'none' | tuple() when + Key :: term(), + List :: [term()]. lookup(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -154,15 +148,15 @@ lookup(Key, [P | Ps]) -> lookup(_Key, []) -> none. -%% @spec lookup_all(Key::term(), List::[term()]) -> [tuple()] -%% %% @doc Returns the list of all entries associated with <code>Key</code> %% in <code>List</code>. If no such entry exists, the result is the %% empty list. %% %% @see lookup/2 --spec lookup_all(Key::term(), List::[term()]) -> [tuple()]. +-spec lookup_all(Key, List) -> [tuple()] when + Key :: term(), + List :: [term()]. lookup_all(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -178,13 +172,13 @@ lookup_all(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec is_defined(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns <code>true</code> if <code>List</code> contains at least %% one entry associated with <code>Key</code>, otherwise %% <code>false</code> is returned. --spec is_defined(Key::term(), List::[term()]) -> boolean(). +-spec is_defined(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. is_defined(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -200,17 +194,15 @@ is_defined(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_value(Key::term(), List::[term()]) -> term() %% @equiv get_value(Key, List, undefined) --spec get_value(Key::term(), List::[term()]) -> term(). +-spec get_value(Key, List) -> term() when + Key :: term(), + List :: List::[term()]. get_value(Key, List) -> get_value(Key, List, undefined). -%% @spec get_value(Key::term(), List::[term()], Default::term()) -> -%% term() -%% %% @doc Returns the value of a simple key/value property in %% <code>List</code>. If <code>lookup(Key, List)</code> would yield %% <code>{Key, Value}</code>, this function returns the corresponding @@ -221,7 +213,10 @@ get_value(Key, List) -> %% @see get_all_values/2 %% @see get_bool/2 --spec get_value(Key::term(), List::[term()], Default::term()) -> term(). +-spec get_value(Key, List, Default) -> term() when + Key :: term(), + List :: [term()], + Default :: term(). get_value(Key, [P | Ps], Default) -> if is_atom(P), P =:= Key -> @@ -240,8 +235,6 @@ get_value(Key, [P | Ps], Default) -> get_value(_Key, [], Default) -> Default. -%% @spec get_all_values(Key, List) -> [term()] -%% %% @doc Similar to <code>get_value/2</code>, but returns the list of %% values for <em>all</em> entries <code>{Key, Value}</code> in %% <code>List</code>. If no such entry exists, the result is the empty @@ -249,7 +242,9 @@ get_value(_Key, [], Default) -> %% %% @see get_value/2 --spec get_all_values(Key::term(), List::[term()]) -> [term()]. +-spec get_all_values(Key, List) -> [term()] when + Key :: term(), + List :: [term()]. get_all_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -267,8 +262,6 @@ get_all_values(Key, [P | Ps]) -> get_all_values(_Key, []) -> []. -%% @spec append_values(Key::term(), List::[term()]) -> [term()] -%% %% @doc Similar to <code>get_all_values/2</code>, but each value is %% wrapped in a list unless it is already itself a list, and the %% resulting list of lists is concatenated. This is often useful for @@ -278,7 +271,9 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key::term(), List::[term()]) -> [term()]. +-spec append_values(Key, List) -> List when + Key :: term(), + List :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -301,8 +296,6 @@ append_values(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_bool(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns the value of a boolean key/value option. If %% <code>lookup(Key, List)</code> would yield <code>{Key, true}</code>, %% this function returns <code>true</code>; otherwise <code>false</code> @@ -311,7 +304,9 @@ append_values(_Key, []) -> %% @see lookup/2 %% @see get_value/2 --spec get_bool(Key::term(), List::[term()]) -> boolean(). +-spec get_bool(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. get_bool(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -333,12 +328,11 @@ get_bool(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_keys(List::[term()]) -> [term()] -%% %% @doc Returns an unordered list of the keys used in <code>List</code>, %% not containing duplicates. --spec get_keys(List::[term()]) -> [term()]. +-spec get_keys(List) -> [term()] when + List :: [term()]. get_keys(Ps) -> sets:to_list(get_keys(Ps, sets:new())). @@ -357,12 +351,12 @@ get_keys([], Keys) -> %% --------------------------------------------------------------------- -%% @spec delete(Key::term(), List::[term()]) -> [term()] -%% %% @doc Deletes all entries associated with <code>Key</code> from %% <code>List</code>. --spec delete(Key::term(), List::[term()]) -> [term()]. +-spec delete(Key, List) -> List when + Key :: term(), + List::[term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -378,11 +372,6 @@ delete(_, []) -> %% --------------------------------------------------------------------- -%% @spec substitute_aliases(Aliases, List::[term()]) -> [term()] -%% -%% Aliases = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of properties. For each entry in %% <code>List</code>, if it is associated with some key <code>K1</code> %% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the @@ -398,7 +387,10 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(aliases(), List::[term()]) -> [term()]. +-spec substitute_aliases(Aliases, List) -> List when + Aliases :: [{Key, Key}], + Key :: term(), + List::[term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -417,11 +409,6 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- -%% @spec substitute_negations(Negations, List::[term()]) -> [term()] -%% -%% Negations = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of boolean-valued properties and simultaneously %% negates their values. For each entry in <code>List</code>, if it is %% associated with some key <code>K1</code> such that <code>{K1, @@ -443,7 +430,10 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(negations(), List::[term()]) -> [term()]. +-spec substitute_negations(Negations, List) -> List when + Negations :: [{Key, Key}], + Key :: term(), + List :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -472,10 +462,6 @@ substitute_negations_1([], P) -> %% --------------------------------------------------------------------- -%% @spec expand(Expansions, List::[term()]) -> [term()] -%% -%% Expansions = [{property(), [term()]}] -%% %% @doc Expands particular properties to corresponding sets of %% properties (or other terms). For each pair <code>{Property, %% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is @@ -510,7 +496,9 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions::expansions(), [term()]) -> [term()]. +-spec expand(Expansions, List) -> List when + Expansions :: [{Property :: property(), Expansion :: [term()]}], + List :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -589,15 +577,6 @@ flatten([]) -> %% --------------------------------------------------------------------- -%% @spec normalize(List::[term()], Stages::[Operation]) -> [term()] -%% -%% Operation = {aliases, Aliases} | {negations, Negations} -%% | {expand, Expansions} -%% Aliases = [{Key, Key}] -%% Negations = [{Key, Key}] -%% Key = term() -%% Expansions = [{property(), [term()]}] -%% %% @doc Passes <code>List</code> through a sequence of %% substitution/expansion stages. For an <code>aliases</code> operation, %% the function <code>substitute_aliases/2</code> is applied using the @@ -619,11 +598,15 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --type operation() :: {'aliases', aliases()} - | {'negations', negations()} - | {'expand', expansions()}. - --spec normalize(List::[term()], Stages::[operation()]) -> [term()]. +-spec normalize(List, Stages) -> List when + List :: [term()], + Stages :: [Operation], + Operation :: {'aliases', Aliases} + | {'negations', Negations} + | {'expand', Expansions}, + Aliases :: [{Key, Key}], + Negations :: [{Key, Key}], + Expansions :: [{Property :: property(), Expansion :: [term()]}]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); @@ -636,10 +619,6 @@ normalize(L, []) -> %% --------------------------------------------------------------------- -%% @spec split(List::[term()], Keys::[term()]) -> {Lists, Rest} -%% Lists = [[term()]] -%% Rest = [term()] -%% %% @doc Partitions <code>List</code> into a list of sublists and a %% remainder. <code>Lists</code> contains one sublist for each key in %% <code>Keys</code>, in the corresponding order. The relative order of @@ -654,7 +633,11 @@ normalize(L, []) -> %% {[[a], [{b, 5}, b],[{c, 2}, {c, 3, 4}]], [{e, 1}, d]}</pre> %% </p> --spec split(List::[term()], Keys::[term()]) -> {[[term()]], [term()]}. +-spec split(List, Keys) -> {Lists, Rest} when + List :: [term()], + Keys :: [term()], + Lists :: [[term()]], + Rest :: [term()]. split(List, Keys) -> {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []), diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index bc6944e520..5ca04ff023 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -60,7 +60,7 @@ -record(qlc_table, % qlc:table/2 {trav_fun, % traverse fun - trav_MS, % bool(); true iff traverse fun takes a match spec + trav_MS, % boolean(); true iff traverse fun takes a match spec pre_fun, post_fun, info_fun, @@ -110,12 +110,12 @@ -record(qlc_cursor, {c}). -record(qlc_opt, - {unique = false, % bool() - cache = false, % bool() | list (true~ets, false~no) + {unique = false, % boolean() + cache = false, % boolean() | list (true~ets, false~no) max_lookup = -1, % int() >= 0 | -1 (represents infinity) join = any, % any | nested_loop | merge | lookup tmpdir = "", % global tmpdir - lookup = any, % any | bool() + lookup = any, % any | boolean() max_list = ?MAX_LIST_SIZE, % int() >= 0 tmpdir_usage = allowed % allowed | not_allowed % | warning_msg | error_msg | info_msg @@ -125,6 +125,8 @@ -define(THROWN_ERROR, {?MODULE, throw_error, _}). +-export_type([query_handle/0]). + %%% A query handle is a tuple {qlc_handle, Handle} where Handle is one %%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc. @@ -144,6 +146,35 @@ get_handle(_) -> %%% Exported functions %%% +-type(query_list_comprehension() :: term()). +-opaque(query_cursor() :: {qlc_cursor, term()}). +-opaque(query_handle() :: {qlc_handle, term()}). +-type(query_handle_or_list() :: query_handle() | list()). +-type(answers() :: [answer()]). +-type(answer() :: term()). +-type(abstract_expr() :: erl_parse:abstract_expr()). +-type(match_expression() :: ets:match_spec()). +-type(spawn_options() :: default | [proc_lib:spawn_option()]). +-type(sort_options() :: [sort_option()] | sort_option()). +-type(sort_option() :: {compressed, boolean()} + | {no_files, no_files()} + | {order, order()} + | {size, pos_integer()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(tmp_directory() :: [] | file:name()). +-type(no_files() :: pos_integer()). % > 1 +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(max_list_size() :: non_neg_integer()). +-type(cache() :: ets | list | no). +-type(tmp_file_usage() :: allowed | not_allowed | info_msg + | warning_msg | error_msg). + +-spec(append(QHL) -> QH when + QHL :: [query_handle_or_list()], + QH :: query_handle()). append(QHs) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QHs]); @@ -151,6 +182,10 @@ append(QHs) -> end || QH <- QHs], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(append(QH1, QH2) -> QH3 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle_or_list(), + QH3 :: query_handle()). append(QH1, QH2) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QH1, QH2]); @@ -158,9 +193,22 @@ append(QH1, QH2) -> end || QH <- [QH1, QH2]], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(cursor(QH) -> Cursor when + QH :: query_handle_or_list(), + Cursor :: query_cursor()). cursor(QH) -> cursor(QH, []). +-spec(cursor(QH, Options) -> Cursor when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {spawn_options, spawn_options()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Cursor :: query_cursor()). cursor(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, spawn_options, max_list_size, @@ -179,6 +227,8 @@ cursor(QH, Options) -> end end. +-spec(delete_cursor(QueryCursor) -> ok when + QueryCursor :: query_cursor()). delete_cursor(#qlc_cursor{c = {_, Owner}}=C) when Owner =/= self() -> erlang:error(not_cursor_owner, [C]); delete_cursor(#qlc_cursor{c = {Pid, _}}) -> @@ -186,15 +236,47 @@ delete_cursor(#qlc_cursor{c = {Pid, _}}) -> delete_cursor(T) -> erlang:error(badarg, [T]). +-spec(e(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH) -> eval(QH, []). +-spec(e(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH, Options) -> eval(QH, Options). +-spec(eval(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH) -> eval(QH, []). +-spec(eval(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -226,9 +308,35 @@ eval(QH, Options) -> end end. +-spec(fold(Function, Acc0, QH) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH) -> fold(Fun, Acc0, QH, []). +-spec(fold(Function, Acc0, QH, Options) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -258,6 +366,9 @@ fold(Fun, Acc0, QH, Options) -> end end. +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). format_error(not_a_query_list_comprehension) -> io_lib:format("argument is not a query list comprehension", []); format_error({used_generator_variable, V}) -> @@ -295,9 +406,29 @@ format_error({error, Module, Reason}) -> format_error(E) -> io_lib:format("~p~n", [E]). +-spec(info(QH) -> Info when + QH :: query_handle_or_list(), + Info :: abstract_expr() | string()). info(QH) -> info(QH, []). +-spec(info(QH, Options) -> Info when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: EvalOption | ReturnOption, + EvalOption :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + ReturnOption :: {depth, Depth} + | {flat, boolean()} + | {format, Format} + | {n_elements, NElements}, + Depth :: infinity | non_neg_integer(), + Format :: abstract_code | string, + NElements :: infinity | pos_integer(), + Info :: abstract_expr() | string()). info(QH, Options) -> case {options(Options, [unique_all, cache_all, flat, format, n_elements, depth, tmpdir, max_list_size, tmpdir_usage]), @@ -333,9 +464,18 @@ info(QH, Options) -> end end. +-spec(keysort(KeyPos, QH1) -> QH2 when + KeyPos :: key_pos(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH) -> keysort(KeyPos, QH, []). +-spec(keysort(KeyPos, QH1, SortOptions) -> QH2 when + KeyPos :: key_pos(), + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH, Options) -> case {is_keypos(KeyPos), options(Options, [tmpdir, order, unique, compressed, @@ -354,9 +494,22 @@ keysort(KeyPos, QH, Options) -> -define(DEFAULT_NUM_OF_ANSWERS, 10). +-spec(next_answers(QueryCursor) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(C) -> next_answers(C, ?DEFAULT_NUM_OF_ANSWERS). +-spec(next_answers(QueryCursor, NumberOfAnswers) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + NumberOfAnswers :: all_remaining | pos_integer(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(#qlc_cursor{c = {_, Owner}}=C, NumOfAnswers) when Owner =/= self() -> erlang:error(not_cursor_owner, [C, NumOfAnswers]); @@ -370,14 +523,35 @@ next_answers(#qlc_cursor{c = {Pid, _}}=C, NumOfAnswers) -> next_answers(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> qlc_pt:parse_transform(Forms, Options). %% The funcspecs qlc:q/1 and qlc:q/2 are known by erl_eval.erl and %% erl_lint.erl. +-spec(q(QLC) -> QH when + QLC :: query_list_comprehension(), + QH :: query_handle()). q(QLC_lc) -> q(QLC_lc, []). +-spec(q(QLC, Options) -> QH when + QH :: query_handle(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QLC :: query_list_comprehension()). q(#qlc_lc{}=QLC_lc, Options) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of [Unique, Cache, Max, Join, Lookup] -> @@ -390,9 +564,16 @@ q(#qlc_lc{}=QLC_lc, Options) -> q(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(sort(QH1) -> QH2 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH) -> sort(QH, []). +-spec(sort(QH1, SortOptions) -> QH2 when + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH, Options) -> case {options(Options, [tmpdir, order, unique, compressed, size, no_files]), get_handle(QH)} of @@ -406,14 +587,47 @@ sort(QH, Options) -> end. %% Note that the generated code is evaluated by (the slow) erl_eval. +-spec(string_to_handle(QueryString) -> QH | Error when + QueryString :: string(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str) -> string_to_handle(Str, []). +-spec(string_to_handle(QueryString, Options) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str, Options) -> - string_to_handle(Str, Options, []). - -string_to_handle(Str, Options, Bindings) when is_list(Str), - is_list(Bindings) -> + string_to_handle(Str, Options, erl_eval:new_bindings()). + +-spec(string_to_handle(QueryString, Options, Bindings) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + Bindings :: erl_eval:binding_struct(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). +string_to_handle(Str, Options, Bindings) when is_list(Str) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of badarg -> erlang:error(badarg, [Str, Options, Bindings]); @@ -447,6 +661,51 @@ string_to_handle(Str, Options, Bindings) when is_list(Str), string_to_handle(T1, T2, T3) -> erlang:error(badarg, [T1, T2, T3]). +-spec(table(TraverseFun, Options) -> QH when + TraverseFun :: TraverseFun0 | TraverseFun1, + TraverseFun0 :: fun(() -> TraverseResult), + TraverseFun1 :: fun((match_expression()) -> TraverseResult), + TraverseResult :: Objects | term(), + Objects :: [] | [term() | ObjectList], + ObjectList :: TraverseFun0 | Objects, + Options :: [Option] | Option, + Option :: {format_fun, FormatFun} + | {info_fun, InfoFun} + | {lookup_fun, LookupFun} + | {parent_fun, ParentFun} + | {post_fun, PostFun} + | {pre_fun, PreFun} + | {key_equality, KeyComparison}, + FormatFun :: undefined | fun((SelectedObjects) -> FormatedTable), + SelectedObjects :: all + | {all, NElements, DepthFun} + | {match_spec, match_expression()} + | {lookup, Position, Keys} + | {lookup, Position, Keys, NElements, DepthFun}, + NElements :: infinity | pos_integer(), + DepthFun :: fun((term()) -> term()), + FormatedTable :: {Mod, Fun, Args} + | abstract_expr() + | string(), + InfoFun :: undefined | fun((InfoTag) -> InfoValue), + InfoTag :: indices | is_unique_objects | keypos | num_of_objects, + InfoValue :: undefined | term(), + LookupFun :: undefined | fun((Position, Keys) -> LookupResult), + LookupResult :: [term()] | term(), + ParentFun :: undefined | fun(() -> ParentFunValue), + PostFun :: undefined | fun(() -> term()), + PreFun :: undefined | fun((PreArgs) -> term()), + PreArgs :: [PreArg], + PreArg :: {parent_value, ParentFunValue} | {stop_fun, StopFun}, + ParentFunValue :: undefined | term(), + StopFun :: undefined | fun(() -> term()), + KeyComparison :: '=:=' | '==', + Position :: pos_integer(), + Keys :: [term()], + Mod :: atom(), + Fun :: atom(), + Args :: [term()], + QH :: query_handle()). table(TraverseFun, Options) when is_function(TraverseFun) -> case {is_function(TraverseFun, 0), IsFun1 = is_function(TraverseFun, 1)} of @@ -472,6 +731,11 @@ table(TraverseFun, Options) when is_function(TraverseFun) -> table(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: abstract_expr(), + Expr :: abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bs0) -> qlc_pt:transform_from_evaluator(LC, Bs0). @@ -722,8 +986,8 @@ listify(T) -> %% Optimizations to be carried out. -record(optz, - {unique = false, % bool() - cache = false, % bool() | list + {unique = false, % boolean() + cache = false, % boolean() | list join_option = any, % constraint set by the 'join' option fast_join = no, % no | #qlc_join. 'no' means nested loop. opt % #qlc_opt @@ -756,8 +1020,8 @@ listify(T) -> lu_skip_quals = [], % qualifiers to skip due to lookup join = {[],[]}, % {Lookup, Merge} n_objs = undefined, % for join (not used yet) - is_unique_objects = false, % bool() - is_cached = false % bool() (true means 'ets' or 'list') + is_unique_objects = false, % boolean() + is_cached = false % boolean() (true means 'ets' or 'list') }). %%% Cursor process functions. @@ -1143,20 +1407,20 @@ monitor_request(Pid, Req) -> %% QueryDesc = {qlc, TemplateDesc, [QualDesc], [QueryOpt]} %% | {table, TableDesc} %% | {append, [QueryDesc]} -%% | {sort, QueryDesc, [SortOption]} -%% | {keysort, KeyPos, QueryDesc, [SortOption]} +%% | {sort, QueryDesc, [sort_option()]} +%% | {keysort, key_pos(), QueryDesc, [sort_option()]} %% | {list, list()} -%% | {list, QueryDesc, MatchExpression} +%% | {list, QueryDesc, match_expression()} %% TableDesc = {Mod, Fun, Args} -%% | AbstractExpression -%% | character_list() +%% | erl_parse:abstract_expr() +%% | string() %% Mod = module() %% Fun = atom() %% Args = [term()] %% QualDesc = FilterDesc %% | {generate, PatternDesc, QueryDesc} -%% QueryOpt = {cache, bool()} | cache -%% | {unique, bool()} | unique +%% QueryOpt = {cache, boolean()} | cache +%% | {unique, boolean()} | unique %% FilterDesc = PatternDesc = TemplateDesc = binary() le_info(#prepared{qh = #simple_qlc{le = LE, p = P, line = L, optz = Optz}}, diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 24378a0698..21504d707b 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -63,6 +63,12 @@ %%% Exported functions %%% +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> ?DEBUG("qlc Parse Transform~n", []), State = #state{imp = is_qlc_q_imported(Forms), @@ -96,10 +102,20 @@ parse_transform(Forms, Options) -> end end. +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bindings) -> ?DEBUG("qlc Parse Transform (Evaluator Version)~n", []), transform_expression(LC, Bindings, false). +-spec(transform_expression(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_expression(LC, Bindings) -> transform_expression(LC, Bindings, true). diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index c09079e8d2..4c6b4d710b 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -56,14 +56,16 @@ new() -> {[],[]}. %{RearList,FrontList} %% O(1) --spec is_queue(term()) -> boolean(). +-spec is_queue(Term) -> boolean() when + Term :: term(). is_queue({R,F}) when is_list(R), is_list(F) -> true; is_queue(_) -> false. %% O(1) --spec is_empty(queue()) -> boolean(). +-spec is_empty(Q) -> boolean() when + Q :: queue(). is_empty({[],[]}) -> true; is_empty({In,Out}) when is_list(In), is_list(Out) -> @@ -72,14 +74,16 @@ is_empty(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec len(queue()) -> non_neg_integer(). +-spec len(Q) -> non_neg_integer() when + Q :: queue(). len({R,F}) when is_list(R), is_list(F) -> length(R)+length(F); len(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec to_list(queue()) -> list(). +-spec to_list(Q) -> list() when + Q :: queue(). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -88,7 +92,8 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(list()) -> queue(). +-spec from_list(L) -> queue() when + L :: list(). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -97,7 +102,9 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(term(), queue()) -> boolean(). +-spec member(Item, Q) -> boolean() when + Item :: term(), + Q :: queue(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -110,7 +117,10 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(term(), queue()) -> queue(). +-spec in(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -122,7 +132,10 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(term(), queue()) -> queue(). +-spec in_r(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -133,7 +146,10 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -151,7 +167,10 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out_r(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -172,7 +191,9 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(queue()) -> term(). +-spec get(Q) -> Item when + Q :: queue(), + Item :: term(). get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -191,7 +212,9 @@ get([_|R], []) -> % malformed queue -> O(len(Q)) %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get_r(queue()) -> term(). +-spec get_r(Q) -> Item when + Q :: queue(), + Item :: term(). get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -206,7 +229,9 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(queue()) -> 'empty' | {'value',term()}. +-spec peek(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -221,7 +246,9 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(queue()) -> 'empty' | {'value',term()}. +-spec peek_r(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -236,7 +263,9 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(queue()) -> queue(). +-spec drop(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -254,7 +283,9 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(queue()) -> queue(). +-spec drop_r(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -275,7 +306,9 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(queue()) -> queue(). +-spec reverse(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -285,7 +318,10 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(queue(), queue()) -> queue(). +-spec join(Q1, Q2) -> Q3 when + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) -> Q; join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) -> @@ -299,7 +335,11 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(non_neg_integer(), queue()) -> {queue(),queue()}. +-spec split(N, Q1) -> {Q2,Q3} when + N :: non_neg_integer(), + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). split(0, {R,F}=Q) when is_list(R), is_list(F) -> {{[],[]},Q}; split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) -> @@ -340,7 +380,10 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(fun((term()) -> boolean() | list()), queue()) -> queue(). +-spec filter(Fun, Q1) -> Q2 when + Fun :: fun((Item :: term()) -> boolean() | list()), + Q1 :: queue(), + Q2 :: queue(). filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) -> F = filter_f(Fun, F0), R = filter_r(Fun, R0), @@ -416,7 +459,10 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(term(), queue()) -> queue(). +-spec cons(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). cons(X, Q) -> in_r(X, Q). @@ -425,7 +471,9 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(queue()) -> term(). +-spec head(Q) -> Item when + Q :: queue(), + Item :: term(). head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -435,7 +483,9 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(queue()) -> queue(). +-spec tail(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). tail(Q) -> drop(Q). @@ -443,22 +493,35 @@ tail(Q) -> %% Cons to tail %% --spec snoc(queue(), term()) -> queue(). +-spec snoc(Q1, Item) -> Q2 when + Q1 :: queue(), + Q2 :: queue(), + Item :: term(). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(queue()) -> term(). +-spec daeh(Q) -> Item when + Q :: queue(), + Item :: term(). daeh(Q) -> get_r(Q). --spec last(queue()) -> term(). +-spec last(Q) -> Item when + Q :: queue(), + Item :: term(). last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(queue()) -> queue(). +-spec liat(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). liat(Q) -> drop_r(Q). --spec lait(queue()) -> queue(). +-spec lait(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(queue()) -> queue(). +-spec init(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl index 01227c29b4..dbb524cc74 100644 --- a/lib/stdlib/src/random.erl +++ b/lib/stdlib/src/random.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,7 +49,10 @@ seed() -> %% seed({A1, A2, A3}) %% Seed random number generation --spec seed({integer(), integer(), integer()}) -> 'undefined' | ran(). +-spec seed({A1, A2, A3}) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed({A1, A2, A3}) -> seed(A1, A2, A3). @@ -57,7 +60,10 @@ seed({A1, A2, A3}) -> %% seed(A1, A2, A3) %% Seed random number generation --spec seed(integer(), integer(), integer()) -> 'undefined' | ran(). +-spec seed(A1, A2, A3) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed(A1, A2, A3) -> put(random_seed, @@ -93,7 +99,8 @@ uniform() -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform(pos_integer()) -> pos_integer(). +-spec uniform(N) -> pos_integer() when + N :: pos_integer(). uniform(N) when is_integer(N), N >= 1 -> trunc(uniform() * N) + 1. @@ -104,7 +111,9 @@ uniform(N) when is_integer(N), N >= 1 -> %% uniform_s(State) -> {F, NewState} %% Returns a random float between 0 and 1. --spec uniform_s(ran()) -> {float(), ran()}. +-spec uniform_s(State0) -> {float(), State1} when + State0 :: ran(), + State1 :: ran(). uniform_s({A1, A2, A3}) -> B1 = (A1*171) rem 30269, @@ -117,7 +126,10 @@ uniform_s({A1, A2, A3}) -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform_s(pos_integer(), ran()) -> {integer(), ran()}. +-spec uniform_s(N, State0) -> {integer(), State1} when + N :: pos_integer(), + State0 :: ran(), + State1 :: ran(). uniform_s(N, State0) when is_integer(N), N >= 1 -> {F, State1} = uniform_s(State0), diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index e2cc9f57ce..02dbe60741 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -19,15 +19,46 @@ -module(re). -export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]). +%-opaque mp() :: {re_pattern, _, _, _}. +-type mp() :: {re_pattern, _, _, _}. + +-type nl_spec() :: cr | crlf | lf | anycrlf | any. + +-type compile_option() :: unicode | anchored | caseless | dollar_endonly + | dotall | extended | firstline | multiline + | no_auto_capture | dupnames | ungreedy + | {newline, nl_spec()}| bsr_anycrlf + | bsr_unicode. + %% Emulator builtins in this module: %% re:compile/1 %% re:compile/2 %% re:run/2 %% re:run/3 +-spec split(Subject, RE) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + SplitList :: [iodata() | unicode:charlist()]. + split(Subject,RE) -> split(Subject,RE,[]). +-spec split(Subject, RE, Options) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Options :: [ Option ], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, nl_spec()} + | bsr_anycrlf | bsr_unicode | {return, ReturnType} + | {parts, NumParts} | group | trim | CompileOpt, + NumParts :: non_neg_integer() | infinity, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + SplitList :: [RetData] | [GroupedRetData], + GroupedRetData :: [RetData], + RetData :: iodata() | unicode:charlist() | binary() | list(). + split(Subject,RE,Options) -> try {NewOpt,Convert,Unicode,Limit,Strip,Group} = @@ -197,10 +228,26 @@ compile_split(Pat,Options0) when not is_tuple(Pat) -> compile_split(_,_) -> throw(badre). +-spec replace(Subject, RE, Replacement) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(). replace(Subject,RE,Replacement) -> replace(Subject,RE,Replacement,[]). +-spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, NLSpec} | bsr_anycrlf + | bsr_unicode | {return, ReturnType} | CompileOpt, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + NLSpec :: cr | crlf | lf | anycrlf | any. + replace(Subject,RE,Replacement,Options) -> try {NewOpt,Convert,Unicode} = diff --git a/lib/stdlib/src/regexp.erl b/lib/stdlib/src/regexp.erl index 8f5994bbee..65f9ca247d 100644 --- a/lib/stdlib/src/regexp.erl +++ b/lib/stdlib/src/regexp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,6 +37,9 @@ -import(string, [substr/2,substr/3]). -import(lists, [reverse/1]). +-type errordesc() :: term(). +-opaque regexp() :: term(). + %% -type matchres() = {match,Start,Length} | nomatch | {error,E}. %% -type subres() = {ok,RepString,RepCount} | {error,E}. %% -type splitres() = {ok,[SubString]} | {error,E}. @@ -287,6 +290,10 @@ re_apply_or(R1, nomatch) -> R1. %% Convert a sh style regexp into a full AWK one. The main difficulty is %% getting character sets right as the conventions are different. +-spec sh_to_awk(ShRegExp) -> AwkRegExp when + ShRegExp :: string(), + AwkRegExp :: string(). + sh_to_awk(Sh) -> "^(" ++ sh_to_awk_1(Sh). %Fix the beginning sh_to_awk_1([$*|Sh]) -> %This matches any string @@ -336,6 +343,12 @@ special_char(_C) -> false. %% parse(RegExp) -> {ok,RE} | {error,E}. %% Parse the regexp described in the string RegExp. +-spec parse(RegExp) -> ParseRes when + RegExp :: string(), + ParseRes :: {ok, RE} | {error, Error}, + RE :: regexp(), + Error :: errordesc(). + parse(S) -> case catch reg(S) of {R,[]} -> {ok,R}; @@ -345,6 +358,10 @@ parse(S) -> %% format_error(Error) -> String. +-spec format_error(ErrorDescriptor) -> Chars when + ErrorDescriptor :: errordesc(), + Chars :: io_lib:chars(). + format_error({illegal,What}) -> ["illegal character `",What,"'"]; format_error({unterminated,What}) -> ["unterminated `",What,"'"]; format_error({char_class,What}) -> @@ -353,6 +370,14 @@ format_error({char_class,What}) -> %% -type match(String, RegExp) -> matchres(). %% Find the longest match of RegExp in String. +-spec match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> match(S, RE); @@ -378,6 +403,14 @@ match(RE, S, St, Pos, L) -> %% -type first_match(String, RegExp) -> matchres(). %% Find the first match of RegExp in String. +-spec first_match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + first_match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> first_match(S, RE); @@ -400,6 +433,15 @@ first_match(_RE, [], _St) -> nomatch. %% -type matches(String, RegExp) -> {match,[{Start,Length}]} | {error,E}. %% Return the all the non-overlapping matches of RegExp in String. +-spec matches(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Matches} | {error, Error}, + Matches :: [{Start, Length}], + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + matches(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> matches(S, RE); @@ -420,6 +462,15 @@ matches(S, RE, St) -> %% the string Replace in String. Accept pre-parsed regular %% expressions. +-spec sub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: 0 | 1, + Error :: errordesc(). + sub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> sub(String, RE, Rep); @@ -449,6 +500,15 @@ sub_repl([], _M, Rest) -> Rest. %% Substitute every match of the regular expression RegExp with the %% string New in String. Accept pre-parsed regular expressions. +-spec gsub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: non_neg_integer(), + Error :: errordesc(). + gsub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> gsub(String, RE, Rep); @@ -462,6 +522,13 @@ gsub(String, RE, Rep) -> %% Split a string into substrings where the RegExp describes the %% field seperator. The RegExp " " is specially treated. +-spec split(String, RegExp) -> SplitRes when + String :: string(), + RegExp :: string() | regexp(), + SplitRes :: {ok, FieldList} | {error, Error}, + FieldList :: [string()], + Error :: errordesc(). + split(String, " ") -> %This is really special {ok,RE} = parse("[ \t]+"), case split_apply(String, RE, true) of diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index bcddca2567..3fd6c81e5f 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -84,30 +84,38 @@ new() -> %% is_set(Set) -> boolean(). %% Return 'true' if Set is a set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Set) -> boolean() when + Set :: term(). is_set(#set{}) -> true; is_set(_) -> false. %% size(Set) -> int(). %% Return the number of elements in Set. --spec size(set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: set(). size(S) -> S#set.size. %% to_list(Set) -> [Elem]. %% Return the elements in Set as a list. --spec to_list(set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: set(), + List :: [term()]. to_list(S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). %% from_list([Elem]) -> Set. %% Build a set from the elements in List. --spec from_list([term()]) -> set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: set(). from_list(L) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L). %% is_element(Element, Set) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. --spec is_element(term(), set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: set(). is_element(E, S) -> Slot = get_slot(S, E), Bkt = get_bucket(S, Slot), @@ -115,7 +123,10 @@ is_element(E, S) -> %% add_element(Element, Set) -> Set. %% Return Set with Element inserted in it. --spec add_element(term(), set()) -> set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). add_element(E, S0) -> Slot = get_slot(S0, E), {S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot), @@ -129,7 +140,10 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}. %% del_element(Element, Set) -> Set. %% Return Set but with Element removed. --spec del_element(term(), set()) -> set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). del_element(E, S0) -> Slot = get_slot(S0, E), {S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot), @@ -144,7 +158,10 @@ del_bkt_el(_, []) -> {[],0}. %% union(Set1, Set2) -> Set %% Return the union of Set1 and Set2. --spec union(set(), set()) -> set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). union(S1, S2) when S1#set.size < S2#set.size -> fold(fun (E, S) -> add_element(E, S) end, S2, S1); union(S1, S2) -> @@ -152,7 +169,9 @@ union(S1, S2) -> %% union([Set]) -> Set %% Return the union of the list of sets. --spec union([set()]) -> set(). +-spec union(SetList) -> Set when + SetList :: [set()], + Set :: set(). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); union([S]) -> S; @@ -165,7 +184,10 @@ union1(S1, []) -> S1. %% intersection(Set1, Set2) -> Set. %% Return the intersection of Set1 and Set2. --spec intersection(set(), set()) -> set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). intersection(S1, S2) when S1#set.size < S2#set.size -> filter(fun (E) -> is_element(E, S2) end, S1); intersection(S1, S2) -> @@ -173,7 +195,9 @@ intersection(S1, S2) -> %% intersection([Set]) -> Set. %% Return the intersection of the list of sets. --spec intersection([set(),...]) -> set(). +-spec intersection(SetList) -> Set when + SetList :: [set(),...], + Set :: set(). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection([S]) -> S. @@ -185,7 +209,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(Set1, Set2) -> boolean(). %% Check whether Set1 and Set2 are disjoint. --spec is_disjoint(set(), set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_disjoint(S1, S2) when S1#set.size < S2#set.size -> fold(fun (_, false) -> false; (E, true) -> not is_element(E, S2) @@ -198,25 +224,39 @@ is_disjoint(S1, S2) -> %% subtract(Set1, Set2) -> Set. %% Return all and only the elements of Set1 which are not also in %% Set2. --spec subtract(set(), set()) -> set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). subtract(S1, S2) -> filter(fun (E) -> not is_element(E, S2) end, S1). %% is_subset(Set1, Set2) -> boolean(). %% Return 'true' when every element of Set1 is also a member of %% Set2, else 'false'. --spec is_subset(set(), set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_subset(S1, S2) -> fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). %% fold(Fun, Accumulator, Set) -> Accumulator. %% Fold function Fun over all elements in Set and return Accumulator. --spec fold(fun((_,_) -> _), T, set()) -> T. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(),AccIn) -> AccOut), + Set :: set(), + Acc0 :: T, + Acc1 :: T, + AccIn :: T, + AccOut :: T. fold(F, Acc, D) -> fold_set(F, Acc, D). %% filter(Fun, Set) -> Set. %% Filter Set with Fun. --spec filter(fun((_) -> boolean()), set()) -> set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: set(), + Set2 :: set(). filter(F, D) -> filter_set(F, D). %% get_slot(Hashdb, Key) -> Slot. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index ebb221c151..e3e23e09bc 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -115,7 +115,9 @@ whereis_evaluator(Shell) -> %% Call this function to start a user restricted shell %% from a normal shell session. --spec start_restricted(module()) -> {'error', code:load_error_rsn()}. +-spec start_restricted(Module) -> {'error', Reason} when + Module :: module(), + Reason :: code:load_error_rsn(). start_restricted(RShMod) when is_atom(RShMod) -> case code:ensure_loaded(RShMod) of @@ -465,7 +467,7 @@ getc(N) -> {get({command,N}), get({result,N}), N}. get_cmd(Num, C) -> - case catch erl_eval:expr(Num, []) of + case catch erl_eval:expr(Num, erl_eval:new_bindings()) of {value,N,_} when N < 0 -> getc(C+N); {value,N,_} -> getc(N); _Other -> {undefined,undefined,undefined} @@ -1184,6 +1186,8 @@ expr(E, Bs, Lf, Ef) -> expr_list(Es, Bs, Lf, Ef) -> erl_eval:expr_list(Es, strip_bindings(Bs), Lf, Ef). +-spec strip_bindings(erl_eval:binding_struct()) -> erl_eval:binding_struct(). + strip_bindings(Bs) -> Bs -- [B || {{module,_},_}=B <- Bs]. @@ -1468,23 +1472,26 @@ set_env(App, Name, Val, Default) -> application_controller:set_env(App, Name, Val), Prev. --spec history(non_neg_integer()) -> non_neg_integer(). +-spec history(N) -> non_neg_integer() when + N :: non_neg_integer(). history(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_history_length, L, ?DEF_HISTORY). --spec results(non_neg_integer()) -> non_neg_integer(). +-spec results(N) -> non_neg_integer() when + N :: non_neg_integer(). results(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). --spec catch_exception(boolean()) -> boolean(). +-spec catch_exception(Bool) -> Bool when + Bool :: boolean(). catch_exception(Bool) -> set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). --type prompt_func() :: 'default' | {module(),atom()}. --spec prompt_func(prompt_func()) -> prompt_func(). +-spec prompt_func(PromptFunc) -> PromptFunc when + PromptFunc :: 'default' | {module(),atom()}. prompt_func(String) -> set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl index 196b659938..d79ee676d9 100644 --- a/lib/stdlib/src/slave.erl +++ b/lib/stdlib/src/slave.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,6 +54,10 @@ pseudo([Master | ServerList]) -> pseudo(_) -> error_msg("No master node given to slave:pseudo/1~n",[]). +-spec pseudo(Master, ServerList) -> ok when + Master :: node(), + ServerList :: [atom()]. + pseudo(_, []) -> ok; pseudo(Master, [S|Tail]) -> start_pseudo(S, whereis(S), Master), @@ -68,6 +72,9 @@ start_pseudo(_,_,_) -> ok. %% It's already there %% This relay can be used to relay all messages directed to a process. +-spec relay(Pid) -> none() when + Pid :: pid(). + relay({badrpc,Reason}) -> error_msg(" ** exiting relay server ~w :~w **~n", [self(),Reason]), exit(Reason); @@ -120,25 +127,61 @@ relay1(Pid) -> %% {error, no_rsh} | %% {error, {already_running, Name@Host}} +-spec start(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start(Host, Name). + start(Host, Name, [], no_link). + +-spec start(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start(Host, Name) -> start(Host, Name, []). +-spec start(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host, Name, Args) -> start(Host, Name, Args, no_link). +-spec start_link(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start_link(Host, Name). + start(Host, Name, [], self()). + +-spec start_link(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start_link(Host, Name) -> start_link(Host, Name, []). +-spec start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host, Name, Args) -> start(Host, Name, Args, self()). @@ -163,6 +206,9 @@ start(Host0, Name, Args, LinkTo, Prog) -> %% Stops a running node. +-spec stop(Node) -> ok when + Node :: node(). + stop(Node) -> % io:format("stop(~p)~n", [Node]), rpc:call(Node, erlang, halt, []), diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl index a83f803330..d38b8ab37a 100644 --- a/lib/stdlib/src/sofs.erl +++ b/lib/stdlib/src/sofs.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% -module(sofs). @@ -40,11 +40,11 @@ substitution/2, projection/2, partition/1, partition/2, partition/3, multiple_relative_product/2, join/4]). --export([family_to_relation/1, family_specification/2, +-export([family_to_relation/1, family_specification/2, union_of_family/1, intersection_of_family/1, family_union/1, family_intersection/1, family_domain/1, family_range/1, family_field/1, - family_union/2, family_intersection/2, family_difference/2, + family_union/2, family_intersection/2, family_difference/2, partition_family/2, family_projection/2]). -export([family_to_digraph/1, family_to_digraph/2, @@ -64,9 +64,9 @@ -compile({inline, [{external_fun,1},{element_type,1}]}). --compile({inline, +-compile({inline, [{unify_types,2}, {match_types,2}, - {test_rel,3}, {symdiff,3}, + {test_rel,3}, {symdiff,3}, {subst,3}]}). -compile({inline, [{fam_binop,3}]}). @@ -80,13 +80,13 @@ -define(TAG, 'Set'). -define(ORDTAG, 'OrdSet'). --record(?TAG, {data = [], type = type}). --record(?ORDTAG, {orddata = {}, ordtype = type}). +-record(?TAG, {data = [] :: list(), type = type :: term()}). +-record(?ORDTAG, {orddata = {} :: tuple(), ordtype = type :: term()}). -define(LIST(S), (S)#?TAG.data). -define(TYPE(S), (S)#?TAG.type). -%%-define(SET(L, T), -%% case is_type(T) of +%%-define(SET(L, T), +%% case is_type(T) of %% true -> #?TAG{data = L, type = T}; %% false -> erlang:error(badtype, [T]) %% end @@ -113,14 +113,40 @@ -define(IS_SET_OF(X), is_list(X)). -define(FAMILY(X, Y), ?BINREL(X, ?SET_OF(Y))). +-export_type([anyset/0, binary_relation/0, external_set/0, a_function/0, + family/0, relation/0, set_of_sets/0, set_fun/0, spec_fun/0, + type/0]). +-export_type([ordset/0, a_set/0]). + +-type(anyset() :: ordset() | a_set()). +-type(binary_relation() :: relation()). +-type(external_set() :: term()). +-type(a_function() :: relation()). +-type(family() :: a_function()). +-opaque(ordset() :: #?ORDTAG{}). +-type(relation() :: a_set()). +-opaque(a_set() :: #?TAG{}). +-type(set_of_sets() :: a_set()). +-type(set_fun() :: pos_integer() + | {external, fun((external_set()) -> external_set())} + | fun((anyset()) -> anyset())). +-type(spec_fun() :: {external, fun((external_set()) -> boolean())} + | fun((anyset()) -> boolean())). +-type(type() :: term()). + +-type(tuple_of(_T) :: tuple()). + %% %% Exported functions %% -%%% +%%% %%% Create sets -%%% +%%% +-spec(from_term(Term) -> AnySet when + AnySet :: anyset(), + Term :: term()). from_term(T) -> Type = case T of _ when is_list(T) -> [?ANYTYPE]; @@ -133,6 +159,10 @@ from_term(T) -> Set end. +-spec(from_term(Term, Type) -> AnySet when + AnySet :: anyset(), + Term :: term(), + Type :: type()). from_term(L, T) -> case is_type(T) of true -> @@ -146,14 +176,23 @@ from_term(L, T) -> erlang:error(badarg, [L, T]) end. +-spec(from_external(ExternalSet, Type) -> AnySet when + ExternalSet :: external_set(), + AnySet :: anyset(), + Type :: type()). from_external(L, ?SET_OF(Type)) -> ?SET(L, Type); from_external(T, Type) -> ?ORDSET(T, Type). +-spec(empty_set() -> Set when + Set :: a_set()). empty_set() -> ?SET([], ?ANYTYPE). +-spec(is_type(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_type(Atom) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE -> true; is_type(?SET_OF(T)) -> @@ -163,19 +202,26 @@ is_type(T) when tuple_size(T) > 0 -> is_type(_T) -> false. +-spec(set(Terms) -> Set when + Set :: a_set(), + Terms :: [term()]). set(L) -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L]); - SL -> + SL -> ?SET(SL, ?ATOM_TYPE) end. +-spec(set(Terms, Type) -> Set when + Set :: a_set(), + Terms :: [term()], + Type :: type()). set(L, ?SET_OF(Type) = T) when ?IS_ATOM_TYPE(Type), Type =/= ?ANYTYPE -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L, T]); - SL -> + SL -> ?SET(SL, Type) end; set(L, ?SET_OF(_) = T) -> @@ -188,6 +234,12 @@ set(L, ?SET_OF(_) = T) -> set(L, T) -> erlang:error(badarg, [L, T]). +-spec(from_sets(ListOfSets) -> Set when + Set :: a_set(), + ListOfSets :: [anyset()]; + (TupleOfSets) -> Ordset when + Ordset :: ordset(), + TupleOfSets :: tuple_of(anyset())). from_sets(Ss) when is_list(Ss) -> case set_of_sets(Ss, [], ?ANYTYPE) of {error, Error} -> @@ -205,6 +257,9 @@ from_sets(Tuple) when is_tuple(Tuple) -> from_sets(T) -> erlang:error(badarg, [T]). +-spec(relation(Tuples) -> Relation when + Relation :: relation(), + Tuples :: [tuple()]). relation([]) -> ?SET([], ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)); relation(Ts = [T | _]) when is_tuple(T) -> @@ -217,6 +272,11 @@ relation(Ts = [T | _]) when is_tuple(T) -> relation(E) -> erlang:error(badarg, [E]). +-spec(relation(Tuples, Type) -> Relation when + N :: integer(), + Type :: N | type(), + Relation :: relation(), + Tuples :: [tuple()]). relation(Ts, TS) -> case catch rel(Ts, TS) of {'EXIT', _} -> @@ -225,6 +285,9 @@ relation(Ts, TS) -> Set end. +-spec(a_function(Tuples) -> Function when + Function :: a_function(), + Tuples :: [tuple()]). a_function(Ts) -> case catch func(Ts, ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -235,6 +298,10 @@ a_function(Ts) -> Set end. +-spec(a_function(Tuples, Type) -> Function when + Function :: a_function(), + Tuples :: [tuple()], + Type :: type()). a_function(Ts, T) -> case catch a_func(Ts, T) of {'EXIT', _} -> @@ -245,6 +312,9 @@ a_function(Ts, T) -> Set end. +-spec(family(Tuples) -> Family when + Family :: family(), + Tuples :: [tuple()]). family(Ts) -> case catch fam2(Ts, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -255,6 +325,10 @@ family(Ts) -> Set end. +-spec(family(Tuples, Type) -> Family when + Family :: family(), + Tuples :: [tuple()], + Type :: type()). family(Ts, T) -> case catch fam(Ts, T) of {'EXIT', _} -> @@ -265,20 +339,30 @@ family(Ts, T) -> Set end. -%%% +%%% %%% Functions on sets. -%%% +%%% +-spec(to_external(AnySet) -> ExternalSet when + ExternalSet :: external_set(), + AnySet :: anyset()). to_external(S) when ?IS_SET(S) -> ?LIST(S); to_external(S) when ?IS_ORDSET(S) -> ?ORDDATA(S). +-spec(type(AnySet) -> Type when + AnySet :: anyset(), + Type :: type()). type(S) when ?IS_SET(S) -> ?SET_OF(?TYPE(S)); type(S) when ?IS_ORDSET(S) -> ?ORDTYPE(S). +-spec(to_sets(ASet) -> Sets when + ASet :: a_set() | ordset(), + Sets :: tuple_of(AnySet) | [AnySet], + AnySet :: anyset()). to_sets(S) when ?IS_SET(S) -> case ?TYPE(S) of ?SET_OF(Type) -> list_of_sets(?LIST(S), Type, []); @@ -289,6 +373,9 @@ to_sets(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> to_sets(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(no_elements(ASet) -> NoElements when + ASet :: a_set() | ordset(), + NoElements :: pos_integer()). no_elements(S) when ?IS_SET(S) -> length(?LIST(S)); no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> @@ -296,6 +383,10 @@ no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> no_elements(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(specification(Fun, Set1) -> Set2 when + Fun :: spec_fun(), + Set1 :: a_set(), + Set2 :: a_set()). specification(Fun, S) when ?IS_SET(S) -> Type = ?TYPE(S), R = case external_fun(Fun) of @@ -311,36 +402,62 @@ specification(Fun, S) when ?IS_SET(S) -> erlang:error(Bad, [Fun, S]) end. +-spec(union(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). union(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(umerge(?LIST(S1), ?LIST(S2)), Type) end. +-spec(intersection(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). intersection(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(intersection(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(difference(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). difference(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(difference(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symdiff(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). symdiff(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(symdiff(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5} when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set(), + Set5 :: a_set()). symmetric_partition(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> sympart(?LIST(S1), ?LIST(S2), [], [], [], Type) end. +-spec(product(Set1, Set2) -> BinRel when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> if ?TYPE(S1) =:= ?ANYTYPE -> S1; @@ -351,6 +468,9 @@ product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> ?SET(relprod(map(F, ?LIST(S1)), map(F, ?LIST(S2))), T) end. +-spec(product(TupleOfSets) -> Relation when + Relation :: relation(), + TupleOfSets :: tuple_of(a_set())). product({S1, S2}) -> product(S1, S2); product(T) when is_tuple(T) -> @@ -365,11 +485,15 @@ product(T) when is_tuple(T) -> case member([], L) of true -> empty_set(); - false -> + false -> ?SET(reverse(prod(L, [], [])), Type) end end. +-spec(constant_function(Set, AnySet) -> Function when + AnySet :: anyset(), + Function :: a_function(), + Set :: a_set()). constant_function(S, E) when ?IS_SET(S) -> case {?TYPE(S), is_sofs_set(E)} of {?ANYTYPE, true} -> S; @@ -381,6 +505,10 @@ constant_function(S, E) when ?IS_SET(S) -> constant_function(S, E) when ?IS_ORDSET(S) -> erlang:error(badarg, [S, E]). +-spec(is_equal(AnySet1, AnySet2) -> Bool when + AnySet1 :: anyset(), + AnySet2 :: anyset(), + Bool :: boolean()). is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> ?LIST(S1) == ?LIST(S2); @@ -396,12 +524,19 @@ is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) -> is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) -> erlang:error(type_mismatch, [S1, S2]). +-spec(is_subset(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_subset(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> subset(?LIST(S1), ?LIST(S2)); false -> erlang:error(type_mismatch, [S1, S2]) end. +-spec(is_sofs_set(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_sofs_set(S) when ?IS_SET(S) -> true; is_sofs_set(S) when ?IS_ORDSET(S) -> @@ -409,16 +544,26 @@ is_sofs_set(S) when ?IS_ORDSET(S) -> is_sofs_set(_S) -> false. +-spec(is_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). is_set(S) when ?IS_SET(S) -> true; is_set(S) when ?IS_ORDSET(S) -> false. -is_empty_set(S) when ?IS_SET(S) -> +-spec(is_empty_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). +is_empty_set(S) when ?IS_SET(S) -> ?LIST(S) =:= []; is_empty_set(S) when ?IS_ORDSET(S) -> false. +-spec(is_disjoint(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> @@ -433,6 +578,9 @@ is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> %%% Functions on set-of-sets. %%% +-spec(union(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). union(Sets) when ?IS_SET(Sets) -> case ?TYPE(Sets) of ?SET_OF(Type) -> ?SET(lunion(?LIST(Sets)), Type); @@ -440,6 +588,9 @@ union(Sets) when ?IS_SET(Sets) -> _ -> erlang:error(badarg, [Sets]) end. +-spec(intersection(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). intersection(Sets) when ?IS_SET(Sets) -> case ?LIST(Sets) of [] -> erlang:error(badarg, [Sets]); @@ -451,32 +602,41 @@ intersection(Sets) when ?IS_SET(Sets) -> end end. +-spec(canonical_relation(SetOfSets) -> BinRel when + BinRel :: binary_relation(), + SetOfSets :: set_of_sets()). canonical_relation(Sets) when ?IS_SET(Sets) -> ST = ?TYPE(Sets), case ST of ?SET_OF(?ANYTYPE) -> empty_set(); - ?SET_OF(Type) -> + ?SET_OF(Type) -> ?SET(can_rel(?LIST(Sets), []), ?BINREL(Type, ST)); ?ANYTYPE -> Sets; _ -> erlang:error(badarg, [Sets]) end. -%%% +%%% %%% Functions on binary relations only. -%%% +%%% rel2fam(R) -> relation_to_family(R). +-spec(relation_to_family(BinRel) -> Family when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. relation_to_family(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> ?SET(rel2family(?LIST(R)), ?FAMILY(DT, RT)); ?ANYTYPE -> R; _Else -> erlang:error(badarg, [R]) end. +-spec(domain(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). domain(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, _) -> ?SET(dom(?LIST(R)), DT); @@ -484,6 +644,9 @@ domain(R) when ?IS_SET(R) -> _Else -> erlang:error(badarg, [R]) end. +-spec(range(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). range(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(_, RT) -> ?SET(ran(?LIST(R), []), RT); @@ -491,35 +654,63 @@ range(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(field(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). %% In "Introduction to LOGIC", Suppes defines the field of a binary %% relation to be the union of the domain and the range (or %% counterdomain). field(R) -> union(domain(R), range(R)). +-spec(relative_product(ListOfBinRels) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel2 :: binary_relation()). +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT) when is_tuple(RT) -> - case relprod_n(RT, foo, false, false) of - {error, Reason} -> - erlang:error(Reason, [RT]); + relative_product(tuple_to_list(RT)); +relative_product(RL) when is_list(RL) -> + case relprod_n(RL, foo, false, false) of + {error, Reason} -> + erlang:error(Reason, [RL]); Reply -> Reply end. +-spec(relative_product(ListOfBinRels, BinRel1) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(); + (BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> relative_product1(converse(R1), R2); +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT, R) when is_tuple(RT), ?IS_SET(R) -> + relative_product(tuple_to_list(RT), R); +relative_product(RL, R) when is_list(RL), ?IS_SET(R) -> EmptyR = case ?TYPE(R) of ?BINREL(_, _) -> ?LIST(R) =:= []; ?ANYTYPE -> true; - _ -> erlang:error(badarg, [RT, R]) + _ -> erlang:error(badarg, [RL, R]) end, - case relprod_n(RT, R, EmptyR, true) of - {error, Reason} -> - erlang:error(Reason, [RT, R]); + case relprod_n(RL, R, EmptyR, true) of + {error, Reason} -> + erlang:error(Reason, [RL, R]); Reply -> Reply end. +-spec(relative_product1(BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> {DTR1, RTR1} = case ?TYPE(R1) of ?BINREL(_, _) = R1T -> R1T; @@ -538,16 +729,23 @@ relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> false -> erlang:error(type_mismatch, [R1, R2]) end. +-spec(converse(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). converse(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, RT) -> ?SET(converse(?LIST(R), []), ?BINREL(RT, DT)); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(DT, ?TYPE(S)) of true -> ?SET(usort(restrict(?LIST(S), ?LIST(R))), RT); @@ -558,9 +756,13 @@ image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(inverse_image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(RT, ?TYPE(S)) of true -> NL = restrict(?LIST(S), converse(?LIST(R), [])), @@ -572,17 +774,23 @@ inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(strict_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). strict_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - Type = ?BINREL(_, _) -> + Type = ?BINREL(_, _) -> ?SET(strict(?LIST(R), []), Type); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(weak_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). weak_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case unify_types(DT, RT) of [] -> erlang:error(badarg, [R]); @@ -592,7 +800,12 @@ weak_relation(R) when ?IS_SET(R) -> ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(extension(BinRel1, Set, AnySet) -> BinRel2 when + AnySet :: anyset(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> case {?TYPE(R), ?TYPE(S), is_sofs_set(E)} of {T=?BINREL(DT, RT), ST, true} -> @@ -621,9 +834,12 @@ extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> erlang:error(badarg, [R, S, E]) end. +-spec(is_a_function(BinRel) -> Bool when + Bool :: boolean(), + BinRel :: binary_relation()). is_a_function(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(_, _) -> + ?BINREL(_, _) -> case ?LIST(R) of [] -> true; [{V,_} | Es] -> is_a_func(Es, V) @@ -632,16 +848,28 @@ is_a_function(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(restriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). restriction(Relation, Set) -> restriction(1, Relation, Set). +-spec(drestriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). drestriction(Relation, Set) -> drestriction(1, Relation, Set). -%%% +%%% %%% Functions on functions only. -%%% +%%% +-spec(composite(Function1, Function2) -> Function3 when + Function1 :: a_function(), + Function2 :: a_function(), + Function3 :: a_function()). composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> ?BINREL(DTF1, RTF1) = case ?TYPE(Fn1)of ?BINREL(_, _) = F1T -> F1T; @@ -656,7 +884,7 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> case match_types(RTF1, DTF2) of true when DTF1 =:= ?ANYTYPE -> Fn1; true when DTF2 =:= ?ANYTYPE -> Fn2; - true -> + true -> case comp(?LIST(Fn1), ?LIST(Fn2)) of SL when is_list(SL) -> ?SET(sort(SL), ?BINREL(DTF1, RTF2)); @@ -666,9 +894,12 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> false -> erlang:error(type_mismatch, [Fn1, Fn2]) end. +-spec(inverse(Function1) -> Function2 when + Function1 :: a_function(), + Function2 :: a_function()). inverse(Fn) when ?IS_SET(Fn) -> case ?TYPE(Fn) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case inverse1(?LIST(Fn)) of SL when is_list(SL) -> ?SET(SL, ?BINREL(RT, DT)); @@ -678,11 +909,16 @@ inverse(Fn) when ?IS_SET(Fn) -> ?ANYTYPE -> Fn; _ -> erlang:error(badarg, [Fn]) end. - -%%% + +%%% %%% Functions on relations (binary or other). -%%% +%%% +-spec(restriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). %% Equivalent to range(restriction(inverse(substitution(Fun, S1)), S2)). restriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), @@ -747,6 +983,11 @@ restriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(drestriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). drestriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -812,6 +1053,10 @@ drestriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(projection(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). projection(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -827,6 +1072,10 @@ projection(I, Set) when is_integer(I), ?IS_SET(Set) -> projection(Fun, Set) -> range(substitution(Fun, Set)). +-spec(substitution(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). substitution(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -867,11 +1116,18 @@ substitution(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(partition(SetOfSets) -> Partition when + SetOfSets :: set_of_sets(), + Partition :: a_set()). partition(Sets) -> F1 = relation_to_family(canonical_relation(Sets)), F2 = relation_to_family(converse(F1)), range(F2). +-spec(partition(SetFun, Set) -> Partition when + SetFun :: set_fun(), + Partition :: a_set(), + Set :: a_set()). partition(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -887,6 +1143,12 @@ partition(I, Set) when is_integer(I), ?IS_SET(Set) -> partition(Fun, Set) -> range(partition_family(Fun, Set)). +-spec(partition(SetFun, Set1, Set2) -> {Set3, Set4} when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set()). partition(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -954,21 +1216,32 @@ partition(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 when + TupleOfBinRels :: tuple_of(BinRel), + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). multiple_relative_product(T, R) when is_tuple(T), ?IS_SET(R) -> case test_rel(R, tuple_size(T), eq) of true when ?TYPE(R) =:= ?ANYTYPE -> empty_set(); - true -> + true -> MProd = mul_relprod(tuple_to_list(T), 1, R), - relative_product(list_to_tuple(MProd)); - false -> + relative_product(MProd); + false -> erlang:error(badarg, [T, R]) end. -join(R1, I1, R2, I2) +-spec(join(Relation1, I, Relation2, J) -> Relation3 when + Relation1 :: relation(), + Relation2 :: relation(), + Relation3 :: relation(), + I :: pos_integer(), + J :: pos_integer()). +join(R1, I1, R2, I2) when ?IS_SET(R1), ?IS_SET(R2), is_integer(I1), is_integer(I2) -> case test_rel(R1, I1, lte) and test_rel(R2, I2, lte) of - false -> + false -> erlang:error(badarg, [R1, I1, R2, I2]); true when ?TYPE(R1) =:= ?ANYTYPE -> R1; true when ?TYPE(R2) =:= ?ANYTYPE -> R2; @@ -980,8 +1253,8 @@ join(R1, I1, R2, I2) true -> fun({X,Y}) -> join_element(X, Y) end; false -> - fun({X,Y}) -> - list_to_tuple(join_element(X, Y, I2)) + fun({X,Y}) -> + list_to_tuple(join_element(X, Y, I2)) end end, ?SET(replace(T, F, []), F({?TYPE(R1), ?TYPE(R2)})) @@ -1001,9 +1274,15 @@ test_rel(R, I, C) -> %%% Family functions %%% +-spec(fam2rel(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). fam2rel(F) -> family_to_relation(F). +-spec(family_to_relation(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. family_to_relation(F) when ?IS_SET(F) -> case ?TYPE(F) of @@ -1013,6 +1292,10 @@ family_to_relation(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_specification(Fun, Family1) -> Family2 when + Fun :: spec_fun(), + Family1 :: family(), + Family2 :: family()). family_specification(Fun, F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) = FType -> @@ -1032,6 +1315,9 @@ family_specification(Fun, F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [Fun, F]) end. +-spec(union_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). union_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1040,6 +1326,9 @@ union_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(intersection_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). intersection_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1052,6 +1341,9 @@ intersection_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_union(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_union(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1060,6 +1352,9 @@ family_union(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_intersection(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_intersection(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1073,6 +1368,9 @@ family_intersection(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_domain(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_domain(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(FDT, ?BINREL(DT, _)) -> @@ -1082,6 +1380,9 @@ family_domain(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_range(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_range(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?BINREL(_, RT)) -> @@ -1091,15 +1392,30 @@ family_range(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_field(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_field(F) -> family_union(family_domain(F), family_range(F)). +-spec(family_union(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_union(F1, F2) -> fam_binop(F1, F2, fun fam_union/3). +-spec(family_intersection(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_intersection(F1, F2) -> fam_binop(F1, F2, fun fam_intersect/3). +-spec(family_difference(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_difference(F1, F2) -> fam_binop(F1, F2, fun fam_difference/3). @@ -1108,13 +1424,17 @@ fam_binop(F1, F2, FF) when ?IS_SET(F1), ?IS_SET(F2) -> case unify_types(?TYPE(F1), ?TYPE(F2)) of [] -> erlang:error(type_mismatch, [F1, F2]); - ?ANYTYPE -> + ?ANYTYPE -> F1; - Type = ?FAMILY(_, _) -> + Type = ?FAMILY(_, _) -> ?SET(FF(?LIST(F1), ?LIST(F2), []), Type); _ -> erlang:error(badarg, [F1, F2]) end. +-spec(partition_family(SetFun, Set) -> Family when + Family :: family(), + SetFun :: set_fun(), + Set :: a_set()). partition_family(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -1159,8 +1479,12 @@ partition_family(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(family_projection(SetFun, Family1) -> Family2 when + SetFun :: set_fun(), + Family1 :: family(), + Family2 :: family()). family_projection(SetFun, F) when ?IS_SET(F) -> - case ?TYPE(F) of + case ?TYPE(F) of ?FAMILY(_, _) when [] =:= ?LIST(F) -> empty_set(); ?FAMILY(DT, Type) -> @@ -1172,7 +1496,7 @@ family_projection(SetFun, F) when ?IS_SET(F) -> Bad -> erlang:error(Bad, [SetFun, F]) end; - _ -> + _ -> erlang:error(badarg, [SetFun, F]) end; ?ANYTYPE -> F; @@ -1183,6 +1507,9 @@ family_projection(SetFun, F) when ?IS_SET(F) -> %%% Digraph functions %%% +-spec(family_to_digraph(Family) -> Graph when + Graph :: digraph(), + Family :: family()). family_to_digraph(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> fam2digraph(F, digraph:new()); @@ -1190,6 +1517,10 @@ family_to_digraph(F) when ?IS_SET(F) -> _Else -> erlang:error(badarg, [F]) end. +-spec(family_to_digraph(Family, GraphType) -> Graph when + Graph :: digraph(), + Family :: family(), + GraphType :: [digraph:d_type()]). family_to_digraph(F, Type) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> ok; @@ -1208,12 +1539,19 @@ family_to_digraph(F, Type) when ?IS_SET(F) -> error:badarg -> erlang:error(badarg, [F, Type]) end. +-spec(digraph_to_family(Graph) -> Family when + Graph :: digraph(), + Family :: family()). digraph_to_family(G) -> case catch digraph_family(G) of {'EXIT', _} -> erlang:error(badarg, [G]); L -> ?SET(L, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) end. +-spec(digraph_to_family(Graph, Type) -> Family when + Graph :: digraph(), + Family :: family(), + Type :: type()). digraph_to_family(G, T) -> case {is_type(T), T} of {true, ?SET_OF(?FAMILY(_,_) = Type)} -> @@ -1284,7 +1622,7 @@ rel(Ts, [Type]) -> end; rel(Ts, Sz) -> rel(Ts, Sz, erlang:make_tuple(Sz, ?ATOM_TYPE)). - + atoms_only(Type, I) when ?IS_ATOM_TYPE(?REL_TYPE(I, Type)) -> atoms_only(Type, I+1); atoms_only(Type, I) when I > tuple_size(Type), ?IS_RELATION(Type) -> @@ -1312,7 +1650,7 @@ rel_type([], SL, Type) when ?IS_RELATION(Type) -> %% Inlined. a_func(Ts, T) -> case {T, is_type(T)} of - {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> func(Ts, Type); {[Type], true} -> @@ -1333,16 +1671,16 @@ func([], _X0, L, Type) -> %% Inlined. fam(Ts, T) -> case {T, is_type(T)} of - {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> fam2(Ts, Type); {[Type], true} -> func_type(Ts, [], Type, fun(?FAMILY(_,_)) -> true end) end. -fam2([], Type) -> +fam2([], Type) -> ?SET([], Type); -fam2(Ts, Type) -> +fam2(Ts, Type) -> fam2(sort(Ts), Ts, [], Type). fam2([{I,L} | T], I0, SL, Type) when I /= I0 -> @@ -1383,7 +1721,7 @@ setify(E, Type0) -> {Type, OrdSet} = make_element(E, Type0, Type0), ?ORDSET(OrdSet, Type). -is_no_lists(T) when is_tuple(T) -> +is_no_lists(T) when is_tuple(T) -> Sz = tuple_size(T), is_no_lists(T, Sz, Sz, []). @@ -1404,7 +1742,7 @@ create([], T, _T0, L) -> make_element(C, ?ANYTYPE, _T0) -> make_element(C); -make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), +make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), not is_list(C), not is_tuple(C) -> {Atom, C}; make_element(C, Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> @@ -1585,12 +1923,12 @@ sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 == H2 -> sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart(S1, [], L1, L12, L2, T) -> - {?SET(reverse(L1, S1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, S1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}; sympart(_, S2, L1, L12, L2, T) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, S2), T)}. sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 < H2 -> @@ -1600,8 +1938,8 @@ sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 == H2 -> sympart1([H1 | T1], T2, L1, L12, L2, T, H2) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart1(_, T2, L1, L12, L2, T, H2) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, [H2 | T2]), T)}. sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 > H2 -> @@ -1611,8 +1949,8 @@ sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 == H2 -> sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) -> sympart1(T1, T2, [H1 | L1], L12, L2, T, H2); sympart2(T1, _, L1, L12, L2, T, H1) -> - {?SET(reverse(L1, [H1 | T1]), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, [H1 | T1]), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}. prod([[E | Es] | Xs], T, L) -> @@ -1660,7 +1998,7 @@ lunion([[] | Ls]) -> lunion(Ls); lunion([S | Ss]) -> umerge(lunion(Ss, last(S), [S], [])); -lunion([]) -> +lunion([]) -> []. lunion([[E] = S | Ss], Last, SL, Ls) when E > Last -> % optimization @@ -1669,7 +2007,7 @@ lunion([S | Ss], Last, SL, Ls) when hd(S) > Last -> lunion(Ss, last(S), [S | SL], Ls); lunion([S | Ss], _Last, SL, Ls) -> lunion(Ss, last(S), [S], [append(reverse(SL)) | Ls]); -lunion([], _Last, SL, Ls) -> +lunion([], _Last, SL, Ls) -> [append(reverse(SL)) | Ls]. %% The empty list is always the first list, if present. @@ -1752,18 +2090,17 @@ relprod(B0, Bx0, By0, A0, L, Ax, [{Bx,By} | B], Ay) when Ay == Bx -> relprod(B0, Bx0, By0, A0, L, _Ax, _B, _Ay) -> relprod2(B0, Bx0, By0, A0, L). -relprod_n({}, _R, _EmptyG, _IsR) -> +relprod_n([], _R, _EmptyG, _IsR) -> {error, badarg}; -relprod_n(RT, R, EmptyR, IsR) -> - RL = tuple_to_list(RT), +relprod_n(RL, R, EmptyR, IsR) -> case domain_type(RL, ?ANYTYPE) of - Error = {error, _Reason} -> + Error = {error, _Reason} -> Error; DType -> Empty = any(fun is_empty_set/1, RL) or EmptyR, RType = range_type(RL, []), Type = ?BINREL(DType, RType), - Prod = + Prod = case Empty of true when DType =:= ?ANYTYPE; RType =:= ?ANYTYPE -> empty_set(); @@ -1771,7 +2108,7 @@ relprod_n(RT, R, EmptyR, IsR) -> ?SET([], Type); false -> TL = ?LIST((relprod_n(RL))), - Sz = tuple_size(RT), + Sz = length(RL), Fun = fun({X,A}) -> {X, flat(Sz, A, [])} end, ?SET(map(Fun, TL), Type) end, @@ -1799,12 +2136,12 @@ flat(N, {T,A}, L) -> domain_type([T | Ts], T0) when ?IS_SET(T) -> case ?TYPE(T) of - ?BINREL(DT, _RT) -> + ?BINREL(DT, _RT) -> case unify_types(DT, T0) of [] -> {error, type_mismatch}; T1 -> domain_type(Ts, T1) end; - ?ANYTYPE -> + ?ANYTYPE -> domain_type(Ts, T0); _ -> {error, badarg} end; @@ -1813,12 +2150,12 @@ domain_type([], T0) -> range_type([T | Ts], L) -> case ?TYPE(T) of - ?BINREL(_DT, RT) -> + ?BINREL(_DT, RT) -> range_type(Ts, [RT | L]); - ?ANYTYPE -> + ?ANYTYPE -> ?ANYTYPE end; -range_type([], L) -> +range_type([], L) -> list_to_tuple(reverse(L)). converse([{A,B} | X], L) -> @@ -1861,7 +2198,7 @@ weak1([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < Y weak1(Es, Ys, L, X) -> weak(Es, Ys, [{X,X} | L]). -weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y +weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y weak2(Es, Ys, [E | L], X); weak2(Es, Ys, L, _X) -> weak(Es, Ys, L). @@ -1910,7 +2247,7 @@ restrict_n(I, [T | Ts], Key, Keys, L) -> end; restrict_n(_I, _Ts, _Key, _Keys, L) -> L. - + restrict_n(I, K, Ts, [Key | Keys], L, E) when K > Key -> restrict_n(I, K, Ts, Keys, L, E); restrict_n(I, K, Ts, [Key | Keys], L, E) when K == Key -> @@ -1933,7 +2270,7 @@ restrict([{K,E} | Ts], _Key, Keys, L) -> restrict(Ts, K, Keys, L, E); restrict(_Ts, _Key, _Keys, L) -> L. - + restrict(Ts, K, [Key | Keys], L, E) when K > Key -> restrict(Ts, K, Keys, L, E); restrict(Ts, K, [Key | Keys], L, E) when K == Key -> @@ -1956,7 +2293,7 @@ diff_restrict_n(I, _Ts, _Key, _Keys, L) when I =:= 1 -> reverse(L); diff_restrict_n(_I, _Ts, _Key, _Keys, L) -> sort(L). - + diff_restrict_n(I, K, Ts, [Key | Keys], L, T) when K > Key -> diff_restrict_n(I, K, Ts, Keys, L, T); diff_restrict_n(I, K, Ts, [Key | Keys], L, _T) when K == Key -> @@ -1981,7 +2318,7 @@ diff_restrict([{K,E} | Ts], _Key, Keys, L) -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(_Ts, _Key, _Keys, L) -> L. - + diff_restrict(Ts, K, [Key | Keys], L, E) when K > Key -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(Ts, K, [Key | Keys], L, _E) when K == Key -> @@ -2041,7 +2378,7 @@ external_fun({external, Function}) when is_atom(Function) -> false; external_fun({external, Fun}) -> Fun; -external_fun(_) -> +external_fun(_) -> false. %% Inlined. @@ -2121,7 +2458,7 @@ partition3_n(I, _Ts, _Key, _Keys, L1, L2) when I =:= 1 -> [reverse(L1) | reverse(L2)]; partition3_n(_I, _Ts, _Key, _Keys, L1, L2) -> [sort(L1) | sort(L2)]. - + partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K > Key -> partition3_n(I, K, Ts, Keys, L1, L2, T); partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K == Key -> @@ -2146,7 +2483,7 @@ partition3([{K,E} | Ts], _Key, Keys, L1, L2) -> partition3(Ts, K, Keys, L1, L2, E); partition3(_Ts, _Key, _Keys, L1, L2) -> [L1 | L2]. - + partition3(Ts, K, [Key | Keys], L1, L2, E) when K > Key -> partition3(Ts, K, Keys, L1, L2, E); partition3(Ts, K, [Key | Keys], L1, L2, E) when K == Key -> @@ -2192,7 +2529,7 @@ join_element(E1, E2, I2) -> join_element2([B | Bs], C, I2) when C =/= I2 -> [B | join_element2(Bs, C+1, I2)]; -join_element2([_ | Bs], _C, _I2) -> +join_element2([_ | Bs], _C, _I2) -> Bs. family2rel([{X,S} | F], L) -> @@ -2297,7 +2634,7 @@ check_function([{X,_} | XL], R) -> check_function(X, XL, R); check_function([], R) -> R. - + check_function(X0, [{X,_} | XL], R) when X0 /= X -> check_function(X, XL, R); check_function(X0, [{X,_} | _XL], _R) when X0 == X -> @@ -2371,14 +2708,14 @@ term2set(T, Type) -> ?ORDSET(T, Type). fam2digraph(F, G) -> - Fun = fun({From, ToL}) -> + Fun = fun({From, ToL}) -> digraph:add_vertex(G, From), Fun2 = fun(To) -> digraph:add_vertex(G, To), case digraph:add_edge(G, From, To) of - {error, {bad_edge, _}} -> + {error, {bad_edge, _}} -> throw({error, cyclic}); - _ -> + _ -> true end end, @@ -2397,7 +2734,7 @@ digraph_fam([V | Vs], V0, G, L) when V /= V0 -> digraph_fam([], _V0, _G, L) -> reverse(L). -%% -> bool() +%% -> boolean() check_fun(T, F, FunT) -> true = is_type(FunT), {NT, _MaxI} = number_tuples(T, 1), @@ -2424,7 +2761,7 @@ check_for_sort(T, _I) when T =:= ?ANYTYPE -> check_for_sort(T, I) when ?IS_RELATION(T), I =< ?REL_ARITY(T), I >= 1 -> I > 1; check_for_sort(_T, _I) -> - error. + error. inverse_substitution(L, Fun, Sort) -> %% One easily sees that the inverse of the tuples created by @@ -2477,11 +2814,11 @@ match_types(Type1, Type2) -> match_types1(Type1, Type2). match_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> true; -match_types1(?ANYTYPE, _) -> +match_types1(?ANYTYPE, _) -> true; -match_types1(_, ?ANYTYPE) -> +match_types1(_, ?ANYTYPE) -> true; -match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> +match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> match_types1(Type1, Type2); match_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> match_typesl(tuple_size(T1), T1, T2); diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 264348180f..30eac4f07d 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -29,23 +29,23 @@ %%--------------------------------------------------------------------------- --type direction() :: 'left' | 'right' | 'both'. - -%%--------------------------------------------------------------------------- - %% Robert's bit %% len(String) %% Return the length of a string. --spec len(string()) -> non_neg_integer(). +-spec len(String) -> Length when + String :: string(), + Length :: non_neg_integer(). len(S) -> length(S). %% equal(String1, String2) %% Test if 2 strings are equal. --spec equal(string(), string()) -> boolean(). +-spec equal(String1, String2) -> boolean() when + String1 :: string(), + String2 :: string(). equal(S, S) -> true; equal(_, _) -> false. @@ -53,7 +53,10 @@ equal(_, _) -> false. %% concat(String1, String2) %% Concatenate 2 strings. --spec concat(string(), string()) -> string(). +-spec concat(String1, String2) -> String3 when + String1 :: string(), + String2 :: string(), + String3 :: string(). concat(S1, S2) -> S1 ++ S2. @@ -61,7 +64,10 @@ concat(S1, S2) -> S1 ++ S2. %% rchr(String, Char) %% Return the first/last index of the character in a string. --spec chr(string(), char()) -> non_neg_integer(). +-spec chr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). chr(S, C) when is_integer(C) -> chr(S, C, 1). @@ -69,7 +75,10 @@ chr([C|_Cs], C, I) -> I; chr([_|Cs], C, I) -> chr(Cs, C, I+1); chr([], _C, _I) -> 0. --spec rchr(string(), char()) -> non_neg_integer(). +-spec rchr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0). @@ -85,7 +94,10 @@ rchr([], _C, _I, L) -> L. %% Return the first/last index of the sub-string in a string. %% index/2 is kept for backwards compatibility. --spec str(string(), string()) -> non_neg_integer(). +-spec str(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). str(S, Sub) when is_list(Sub) -> str(S, Sub, 1). @@ -97,7 +109,10 @@ str([C|S], [C|Sub], I) -> str([_|S], Sub, I) -> str(S, Sub, I+1); str([], _Sub, _I) -> 0. --spec rstr(string(), string()) -> non_neg_integer(). +-spec rstr(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0). @@ -116,7 +131,10 @@ prefix(Pre, String) when is_list(Pre), is_list(String) -> false. %% span(String, Chars) -> Length. %% cspan(String, Chars) -> Length. --spec span(string(), string()) -> non_neg_integer(). +-spec span(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). span(S, Cs) when is_list(Cs) -> span(S, Cs, 0). @@ -127,7 +145,10 @@ span([C|S], Cs, I) -> end; span([], _Cs, I) -> I. --spec cspan(string(), string()) -> non_neg_integer(). +-spec cspan(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0). @@ -142,14 +163,21 @@ cspan([], _Cs, I) -> I. %% substr(String, Start, Length) %% Extract a sub-string from String. --spec substr(string(), pos_integer()) -> string(). +-spec substr(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). substr(String, 1) when is_list(String) -> String; substr(String, S) when is_integer(S), S > 1 -> substr2(String, S). --spec substr(string(), pos_integer(), non_neg_integer()) -> string(). +-spec substr(String, Start, Length) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Length :: non_neg_integer(). substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 -> substr1(substr2(String, S), L). @@ -163,7 +191,10 @@ substr2([_|String], S) -> substr2(String, S-1). %% tokens(String, Seperators). %% Return a list of tokens seperated by characters in Seperators. --spec tokens(string(), string()) -> [[char(),...]]. +-spec tokens(String, SeparatorList) -> Tokens when + String :: string(), + SeparatorList :: string(), + Tokens :: [Token :: nonempty_string()]. tokens(S, Seps) -> tokens1(S, Seps, []). @@ -184,11 +215,18 @@ tokens2([C|S], Seps, Toks, Cs) -> tokens2([], _Seps, Toks, Cs) -> reverse([reverse(Cs)|Toks]). --spec chars(char(), non_neg_integer()) -> string(). +-spec chars(Character, Number) -> String when + Character :: char(), + Number :: non_neg_integer(), + String :: string(). chars(C, N) -> chars(C, N, []). --spec chars(char(), non_neg_integer(), string()) -> string(). +-spec chars(Character, Number, Tail) -> String when + Character :: char(), + Number :: non_neg_integer(), + Tail :: string(), + String :: string(). chars(C, N, Tail) when N > 0 -> chars(C, N-1, [C|Tail]); @@ -199,7 +237,10 @@ chars(C, 0, Tail) when is_integer(C) -> %%% COPIES %%% --spec copies(string(), non_neg_integer()) -> string(). +-spec copies(String, Number) -> Copies when + String :: string(), + Copies :: string(), + Number :: non_neg_integer(). copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). @@ -211,11 +252,16 @@ copies(CharList, Num, R) -> %%% WORDS %%% --spec words(string()) -> pos_integer(). +-spec words(String) -> Count when + String :: string(), + Count :: pos_integer(). words(String) -> words(String, $\s). --spec words(string(), char()) -> pos_integer(). +-spec words(String, Character) -> Count when + String :: string(), + Character :: char(), + Count :: pos_integer(). words(String, Char) when is_integer(Char) -> w_count(strip(String, both, Char), Char, 0). @@ -226,11 +272,18 @@ w_count([_H|T], Char, Num) -> w_count(T, Char, Num). %%% SUB_WORDS %%% --spec sub_word(string(), integer()) -> string(). +-spec sub_word(String, Number) -> Word when + String :: string(), + Word :: string(), + Number :: integer(). sub_word(String, Index) -> sub_word(String, Index, $\s). --spec sub_word(string(), integer(), char()) -> string(). +-spec sub_word(String, Number, Character) -> Word when + String :: string(), + Word :: string(), + Number :: integer(), + Character :: char(). sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) -> case words(String, Char) of @@ -254,14 +307,21 @@ s_word([_|T],Stop,Char,Index,Res) when Index < Stop -> strip(String) -> strip(String, both). --spec strip(string(), direction()) -> string(). +-spec strip(String, Direction) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both. strip(String, left) -> strip_left(String, $\s); strip(String, right) -> strip_right(String, $\s); strip(String, both) -> strip_right(strip_left(String, $\s), $\s). --spec strip(string(), direction(), char()) -> string(). +-spec strip(String, Direction, Character) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both, + Character :: char(). strip(String, right, Char) -> strip_right(String, Char); strip(String, left, Char) -> strip_left(String, Char); @@ -285,11 +345,18 @@ strip_right([], Sc) when is_integer(Sc) -> %%% LEFT %%% --spec left(string(), non_neg_integer()) -> string(). +-spec left(String, Number) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(). left(String, Len) when is_integer(Len) -> left(String, Len, $\s). --spec left(string(), non_neg_integer(), char()) -> string(). +-spec left(String, Number, Character) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(), + Character :: char(). left(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -303,11 +370,18 @@ l_pad(String, Num, Char) -> String ++ chars(Char, Num). %%% RIGHT %%% --spec right(string(), non_neg_integer()) -> string(). +-spec right(String, Number) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(). right(String, Len) when is_integer(Len) -> right(String, Len, $\s). --spec right(string(), non_neg_integer(), char()) -> string(). +-spec right(String, Number, Character) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(), + Character :: char(). right(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -321,11 +395,18 @@ r_pad(String, Num, Char) -> chars(Char, Num, String). %%% CENTRE %%% --spec centre(string(), non_neg_integer()) -> string(). +-spec centre(String, Number) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(). centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s). --spec centre(string(), non_neg_integer(), char()) -> string(). +-spec centre(String, Number, Character) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(), + Character :: char(). centre(String, 0, Char) when is_list(String), is_integer(Char) -> []; % Strange cases to centre string @@ -341,11 +422,18 @@ centre(String, Len, Char) when is_integer(Char) -> %%% SUB_STRING %%% --spec sub_string(string(), pos_integer()) -> string(). +-spec sub_string(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). sub_string(String, Start) -> substr(String, Start). --spec sub_string(string(), pos_integer(), pos_integer()) -> string(). +-spec sub_string(String, Start, Stop) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Stop :: pos_integer(). sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1). @@ -370,23 +458,34 @@ to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE -> to_upper_char(C) -> C. --spec to_lower(string()) -> string() - ; (char()) -> char(). +-spec to_lower(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_lower(S) when is_list(S) -> [to_lower_char(C) || C <- S]; to_lower(C) when is_integer(C) -> to_lower_char(C). --spec to_upper(string()) -> string() - ; (char()) -> char(). +-spec to_upper(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_upper(S) when is_list(S) -> [to_upper_char(C) || C <- S]; to_upper(C) when is_integer(C) -> to_upper_char(C). --spec join([string()], string()) -> string(). +-spec join(StringList, Separator) -> String when + StringList :: [string()], + Separator :: string(), + String :: string(). join([], Sep) when is_list(Sep) -> []; diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 09a01a9aea..e60706ed05 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -35,19 +35,28 @@ %%-------------------------------------------------------------------------- --export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]). +-export_type([child_spec/0, startchild_ret/0, strategy/0]). %%-------------------------------------------------------------------------- --type child_id() :: pid() | 'undefined'. --type mfargs() :: {module(), atom(), [term()] | undefined}. +-type child() :: pid() | 'undefined'. +-type child_id() :: term(). +-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. -type modules() :: [module()] | 'dynamic'. -type restart() :: 'permanent' | 'transient' | 'temporary'. -type shutdown() :: 'brutal_kill' | timeout(). -type worker() :: 'worker' | 'supervisor'. --type sup_name() :: {'local', atom()} | {'global', atom()}. --type sup_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). --type child_spec() :: {term(),mfargs(),restart(),shutdown(),worker(),modules()}. +-type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. +-type sup_ref() :: (Name :: atom()) + | {Name :: atom(), Node :: node()} + | {'global', Name :: atom()} + | pid(). +-type child_spec() :: {Id :: child_id(), + StartFunc :: mfargs(), + Restart :: restart(), + Shutdown :: shutdown(), + Type :: worker(), + Modules :: modules()}. -type strategy() :: 'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'. @@ -55,14 +64,14 @@ %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined :: child_id(), + pid = undefined :: child(), name, mfargs :: mfargs(), restart_type :: restart(), shutdown :: shutdown(), child_type :: worker(), modules = [] :: modules()}). --type child() :: #child{}. +-type child_rec() :: #child{}. -define(DICT, dict). -define(SETS, sets). @@ -70,7 +79,7 @@ -record(state, {name, strategy :: strategy(), - children = [] :: [child()], + children = [] :: [child_rec()], dynamics :: ?DICT() | ?SET(), intensity :: non_neg_integer(), period :: pos_integer(), @@ -99,11 +108,16 @@ behaviour_info(_Other) -> -type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. --spec start_link(module(), term()) -> startlink_ret(). +-spec start_link(Module, Args) -> startlink_ret() when + Module :: module(), + Args :: term(). start_link(Mod, Args) -> gen_server:start_link(supervisor, {self, Mod, Args}, []). --spec start_link(sup_name(), module(), term()) -> startlink_ret(). +-spec start_link(SupName, Module, Args) -> startlink_ret() when + SupName :: sup_name(), + Module :: module(), + Args :: term(). start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []). @@ -111,24 +125,33 @@ start_link(SupName, Mod, Args) -> %%% Interface functions. %%% --------------------------------------------------- --type info() :: term(). -type startchild_err() :: 'already_present' - | {'already_started', child_id()} | term(). --type startchild_ret() :: {'ok', child_id()} | {'ok', child_id(), info()} + | {'already_started', Child :: child()} | term(). +-type startchild_ret() :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} | {'error', startchild_err()}. --spec start_child(sup_ref(), child_spec() | [term()]) -> startchild_ret(). +-spec start_child(SupRef, ChildSpec) -> startchild_ret() when + SupRef :: sup_ref(), + ChildSpec :: child_spec() | (List :: [term()]). start_child(Supervisor, ChildSpec) -> call(Supervisor, {start_child, ChildSpec}). --type restart_err() :: 'running' | 'not_found' | 'simple_one_for_one' | term(). --spec restart_child(sup_ref(), term()) -> - {'ok', child_id()} | {'ok', child_id(), info()} | {'error', restart_err()}. +-spec restart_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} + | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). --type del_err() :: 'running' | 'not_found' | 'simple_one_for_one'. --spec delete_child(sup_ref(), term()) -> 'ok' | {'error', del_err()}. +-spec delete_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one'. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -139,22 +162,39 @@ delete_child(Supervisor, Name) -> %% way (maybe killed). %%----------------------------------------------------------------- --type term_err() :: 'not_found' | 'simple_one_for_one'. --spec terminate_child(sup_ref(), pid() | term()) -> 'ok' | {'error', term_err()}. +-spec terminate_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: pid() | child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'not_found' | 'simple_one_for_one'. terminate_child(Supervisor, Name) -> call(Supervisor, {terminate_child, Name}). --spec which_children(sup_ref()) -> [{term(), child_id(), worker(), modules()}]. +-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when + SupRef :: sup_ref(), + Id :: child_id() | undefined, + Child :: child(), + Type :: worker(), + Modules :: modules(). which_children(Supervisor) -> call(Supervisor, which_children). +-spec count_children(SupRef) -> PropListOfCounts when + SupRef :: sup_ref(), + PropListOfCounts :: [Count], + Count :: {specs, ChildSpecCount :: non_neg_integer()} + | {active, ActiveProcessCount :: non_neg_integer()} + | {supervisors, ChildSupervisorCount :: non_neg_integer()} + |{workers, ChildWorkerCount :: non_neg_integer()}. count_children(Supervisor) -> call(Supervisor, count_children). call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). --spec check_childspecs([child_spec()]) -> 'ok' | {'error', term()}. +-spec check_childspecs(ChildSpecs) -> Result when + ChildSpecs :: [child_spec()], + Result :: 'ok' | {'error', Error :: term()}. check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -222,12 +262,12 @@ init_dynamic(_State, StartSpec) -> %%----------------------------------------------------------------- %% Func: start_children/2 -%% Args: Children = [child()] in start order +%% Args: Children = [child_rec()] in start order %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} %% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} -%% NChildren = [child()] in termination order (reversed +%% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- start_children(Children, SupName) -> start_children(Children, [], SupName). @@ -687,9 +727,9 @@ restart(one_for_all, Child, State) -> %%----------------------------------------------------------------- %% Func: terminate_children/2 -%% Args: Children = [child()] in termination order +%% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [child()] in +%% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) -> @@ -958,7 +998,7 @@ supname(N, _) -> N. %%% Shutdown = integer() | infinity | brutal_kill %%% ChildType = supervisor | worker %%% Modules = [atom()] | dynamic -%%% Returns: {ok, [child()]} | Error +%%% Returns: {ok, [child_rec()]} | Error %%% ------------------------------------------------------ check_startspec(Children) -> check_startspec(Children, []). diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl index 3d2bd2c9a5..555cb5a66f 100644 --- a/lib/stdlib/src/supervisor_bridge.erl +++ b/lib/stdlib/src/supervisor_bridge.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,9 +49,25 @@ behaviour_info(_Other) -> %%%----------------------------------------------------------------- -record(state, {mod, pid, child_state, name}). +-spec start_link(Module, Args) -> Result when + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Mod, StartArgs) -> gen_server:start_link(supervisor_bridge, [Mod, StartArgs, self], []). +-spec start_link(SupBridgeName, Module, Args) -> Result when + SupBridgeName :: {local, Name} | {global, Name}, + Name :: atom(), + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Name, Mod, StartArgs) -> gen_server:start_link(Name, supervisor_bridge, [Mod, StartArgs, Name], []). diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 12209c16d7..8ab72c9b50 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,22 +33,74 @@ %%----------------------------------------------------------------- -type name() :: pid() | atom() | {'global', atom()}. --type system_event() :: {'in', _Msg} | {'in', _Msg, _From} | {'out', _Msg, _To}. +-type system_event() :: {'in', Msg :: _} + | {'in', Msg :: _, From :: _} + | {'out', Msg :: _, To :: _} + | term(). +-opaque dbg_opt() :: list(). +-type dbg_fun() :: fun((FuncState :: _, + Event :: system_event(), + ProcState :: _) -> 'done' | (NewFuncState :: _)). %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- +-spec suspend(Name) -> Void when + Name :: name(), + Void :: term(). suspend(Name) -> send_system_msg(Name, suspend). +-spec suspend(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). +-spec resume(Name) -> Void when + Name :: name(), + Void :: term(). resume(Name) -> send_system_msg(Name, resume). +-spec resume(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). +-spec get_status(Name) -> Status when + Name :: name(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name) -> send_system_msg(Name, get_status). +-spec get_status(Name, Timeout) -> Status when + Name :: name(), + Timeout :: timeout(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). +-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}). +-spec change_code(Name, Module, OldVsn, Extra, Timeout) -> + 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Timeout :: timeout(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra, Timeout) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout). @@ -56,52 +108,116 @@ change_code(Name, Mod, Vsn, Extra, Timeout) -> %% Debug commands %%----------------------------------------------------------------- --type log_flag() :: 'true' | {'true',pos_integer()} | 'false' | 'get' | 'print'. - --spec log(name(), log_flag()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print'. log(Name, Flag) -> send_system_msg(Name, {debug, {log, Flag}}). --spec log(name(), log_flag(), timeout()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag, Timeout) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print', + Timeout :: timeout(). log(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {log, Flag}}, Timeout). --spec trace(name(), boolean()) -> 'ok'. +-spec trace(Name, Flag) -> 'ok' when + Name :: name(), + Flag :: boolean(). trace(Name, Flag) -> send_system_msg(Name, {debug, {trace, Flag}}). --spec trace(name(), boolean(), timeout()) -> 'ok'. +-spec trace(Name, Flag, Timeout) -> 'ok' when + Name :: name(), + Flag :: boolean(), + Timeout :: timeout(). trace(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {trace, Flag}}, Timeout). --type l2f_fname() :: string() | 'false'. - --spec log_to_file(name(), l2f_fname()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false'. log_to_file(Name, FileName) -> send_system_msg(Name, {debug, {log_to_file, FileName}}). --spec log_to_file(name(), l2f_fname(), timeout()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag, Timeout) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false', + Timeout :: timeout(). log_to_file(Name, FileName, Timeout) -> send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout). +-spec statistics(Name, Flag) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(). statistics(Name, Flag) -> send_system_msg(Name, {debug, {statistics, Flag}}). + +-spec statistics(Name, Flag, Timeout) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(), + Timeout :: timeout(). statistics(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {statistics, Flag}}, Timeout). --spec no_debug(name()) -> 'ok'. +-spec no_debug(Name) -> 'ok' when + Name :: name(). no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). --spec no_debug(name(), timeout()) -> 'ok'. +-spec no_debug(Name, Timeout) -> 'ok' when + Name :: name(), + Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). +-spec install(Name, FuncSpec) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Void :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). +-spec install(Name, FuncSpec, Timeout) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Timeout :: timeout(), + Void :: term(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). +-spec remove(Name, Func) -> Void when + Name :: name(), + Func :: dbg_fun(), + Void :: term(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). +-spec remove(Name, Func, Timeout) -> Void when + Name :: name(), + Func :: dbg_fun(), + Timeout :: timeout(), + Void :: term(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -150,6 +266,14 @@ mfa(Name, Req, Timeout) -> %% The Module must export system_continue/3, system_terminate/4 %% and format_status/2 for status information. %%----------------------------------------------------------------- +-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when + Msg :: term(), + From :: {pid(), Tag :: _}, + Parent :: pid(), + Module :: module(), + Debug :: [dbg_opt()], + Misc :: term(), + Void :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -176,6 +300,11 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] %%----------------------------------------------------------------- +-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when + Debug :: [dbg_opt()], + FormFunc :: dbg_fun(), + Extra :: term(), + Event :: system_event(). handle_debug([{trace, true} | T], FormFunc, State, Event) -> print_event({Event, State, FormFunc}), [{trace, true} | handle_debug(T, FormFunc, State, Event)]; @@ -341,24 +470,36 @@ trim(N, LogData) -> %% Debug structure manipulating functions %%----------------------------------------------------------------- install_debug(Item, Data, Debug) -> - case get_debug(Item, Debug, undefined) of + case get_debug2(Item, Debug, undefined) of undefined -> [{Item, Data} | Debug]; _ -> Debug end. remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug). + +-spec get_debug(Item, Debug, Default) -> term() when + Item :: 'log' | 'statistics', + Debug :: [dbg_opt()], + Default :: term(). get_debug(Item, Debug, Default) -> + get_debug2(Item, Debug, Default). + +%% Workaround: accepts more Item types than get_debug/3. +get_debug2(Item, Debug, Default) -> case lists:keysearch(Item, 1, Debug) of {value, {Item, Data}} -> Data; _ -> Default end. +-spec print_log(Debug) -> Void when + Debug :: [dbg_opt()], + Void :: term(). print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, lists:reverse(Logs)). close_log_file(Debug) -> - case get_debug(log_to_file, Debug, []) of + case get_debug2(log_to_file, Debug, []) of [] -> Debug; Fd -> @@ -375,6 +516,15 @@ close_log_file(Debug) -> %% system messages. %% Returns: [debug_opts()] %%----------------------------------------------------------------- + +-spec debug_options(Options) -> [dbg_opt()] when + Options :: [Opt], + Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + | {'install', FuncSpec}, + FileName :: file:name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(). debug_options(Options) -> debug_options(Options, []). debug_options([trace | T], Debug) -> diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index b456c5d6c1..89fae05e4f 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/2, tc/3, now_diff/2, + cancel/1, sleep/1, tc/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -46,103 +46,189 @@ %% -opaque tref() :: {integer(), reference()}. -type time() :: non_neg_integer(). --type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %% %% Interface functions %% --spec apply_after(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_after(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). + apply_after(Time, M, F, A) -> req(apply_after, {Time, {M, F, A}}). --spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Pid, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Pid, Message) -> req(apply_after, {Time, {?MODULE, send, [Pid, Message]}}). --spec send_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Message) -> send_after(Time, self(), Message). --spec exit_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Pid, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Pid, Reason) -> req(apply_after, {Time, {erlang, exit, [Pid, Reason]}}). --spec exit_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Reason) -> exit_after(Time, self(), Reason). --spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time, Pid) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason2 :: term(). kill_after(Time, Pid) -> exit_after(Time, Pid, kill). --spec kill_after(time()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason2 :: term(). kill_after(Time) -> exit_after(Time, self(), kill). --spec apply_interval(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_interval(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). apply_interval(Time, M, F, A) -> req(apply_interval, {Time, self(), {M, F, A}}). --spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Pid, Message) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Pid, Message) -> req(apply_interval, {Time, Pid, {?MODULE, send, [Pid, Message]}}). --spec send_interval(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Message) -> send_interval(Time, self(), Message). --spec cancel(tref()) -> {'ok', 'cancel'} | {'error', term()}. +-spec cancel(TRef) -> {'ok', 'cancel'} | {'error', Reason} when + TRef :: tref(), + Reason :: term(). cancel(BRef) -> req(cancel, BRef). --spec sleep(timeout()) -> 'ok'. +-spec sleep(Time) -> 'ok' when + Time :: timeout(). sleep(T) -> receive after T -> ok end. +%% +%% Measure the execution time (in microseconds) for Fun(). +%% +-spec tc(Fun) -> {Time, Value} when + Fun :: function(), + Time :: integer(), + Value :: term(). +tc(F) -> + Before = os:timestamp(), + Val = F(), + After = os:timestamp(), + {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for Fun(Args). %% --spec tc(function(), [_]) -> {time(), term()}. +-spec tc(Fun, Arguments) -> {Time, Value} when + Fun :: function(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(F, A) -> - Before = erlang:now(), - Val = (catch apply(F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for an MFA. %% --spec tc(atom(), atom(), [term()]) -> {time(), term()}. +-spec tc(Module, Function, Arguments) -> {Time, Value} when + Module :: module(), + Function :: atom(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(M, F, A) -> - Before = erlang:now(), - Val = (catch apply(M, F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(M, F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Calculate the time difference (in microseconds) of two %% erlang:now() timestamps, T2-T1. %% --spec now_diff(timestamp(), timestamp()) -> integer(). +-spec now_diff(T1, T2) -> Tdiff when + T1 :: calendar:t_now(), + T2 :: calendar:t_now(), + Tdiff :: integer(). now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. %% %% Convert seconds, minutes etc. to milliseconds. %% --spec seconds(non_neg_integer()) -> non_neg_integer(). +-spec seconds(Seconds) -> MilliSeconds when + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). seconds(Seconds) -> 1000*Seconds. --spec minutes(non_neg_integer()) -> non_neg_integer(). +-spec minutes(Minutes) -> MilliSeconds when + Minutes :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). minutes(Minutes) -> 1000*60*Minutes. --spec hours(non_neg_integer()) -> non_neg_integer(). +-spec hours(Hours) -> MilliSeconds when + Hours :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hours(Hours) -> 1000*60*60*Hours. --spec hms(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +-spec hms(Hours, Minutes, Seconds) -> MilliSeconds when + Hours :: non_neg_integer(), + Minutes :: non_neg_integer(), + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hms(H, M, S) -> hours(H) + minutes(M) + seconds(S). diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl index 12bc60623d..a5d9965ca2 100644 --- a/lib/stdlib/src/unicode.erl +++ b/lib/stdlib/src/unicode.erl @@ -30,12 +30,34 @@ characters_to_binary/3, bom_to_encoding/1, encoding_to_bom/1]). --export_type([encoding/0]). +-export_type([chardata/0, charlist/0, encoding/0, external_chardata/0, + external_charlist/0, latin1_chardata/0, + latin1_charlist/0, unicode_binary/0, unicode_char/0]). -type encoding() :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | {'utf16', endian()} | 'utf32' | {'utf32', endian()}. -type endian() :: 'big' | 'little'. +-type unicode_binary() :: binary(). +-type unicode_char() :: non_neg_integer(). +-type charlist() :: [unicode_char() | unicode_binary() | charlist()]. +-type chardata() :: charlist() | unicode_binary(). +-type external_unicode_binary() :: binary(). +-type external_chardata() :: external_charlist() | external_unicode_binary(). +-type external_charlist() :: [unicode_char() | external_unicode_binary() + | external_charlist()]. +-type latin1_binary() :: binary(). +-type latin1_char() :: byte(). +-type latin1_chardata() :: latin1_charlist() | latin1_binary(). +-type latin1_charlist() :: [latin1_char() | latin1_binary() + | latin1_charlist()]. + +-spec characters_to_list(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: list() + | {error, list(), RestData} + | {incomplete, list(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). characters_to_list(ML) -> unicode:characters_to_list(ML,unicode). @@ -69,6 +91,13 @@ do_characters_to_list(ML, Encoding) -> end. +-spec characters_to_binary(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML) -> try unicode:characters_to_binary(ML,unicode) @@ -104,6 +133,15 @@ characters_to_binary_int(ML,InEncoding) -> erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) end. +-spec characters_to_binary(Data, InEncoding, OutEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + OutEncoding :: encoding(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML, latin1, latin1) when is_binary(ML) -> ML; characters_to_binary(ML, latin1, Uni) when is_binary(ML) and ((Uni =:= utf8) or (Uni =:= unicode)) -> @@ -215,6 +253,13 @@ characters_to_binary_int(ML, InEncoding, OutEncoding) -> Res end. +-spec bom_to_encoding(Bin) -> {Encoding, Length} when + Bin :: binary(), + Encoding :: 'latin1' | 'utf8' + | {'utf16', endian()} + | {'utf32', endian()}, + Length :: non_neg_integer(). + bom_to_encoding(<<239,187,191,_/binary>>) -> {utf8,3}; bom_to_encoding(<<0,0,254,255,_/binary>>) -> @@ -228,6 +273,10 @@ bom_to_encoding(<<255,254,_/binary>>) -> bom_to_encoding(Bin) when is_binary(Bin) -> {latin1,0}. +-spec encoding_to_bom(InEncoding) -> Bin when + Bin :: binary(), + InEncoding :: encoding(). + encoding_to_bom(unicode) -> <<239,187,191>>; encoding_to_bom(utf8) -> diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index ee0d17bc94..598e77ffdc 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -52,14 +52,17 @@ -define(reg_dword, 4). %% Basic types internal to this file. --type open_mode() :: 'read' | 'write'. --type reg_handle() :: {'win32reg',port()}. +-opaque reg_handle() :: {'win32reg',port()}. -type name() :: string() | 'default'. -type value() :: string() | integer() | binary(). %%% Exported functions. --spec open([open_mode()]) -> {'ok', reg_handle()} | {'error', 'enotsup'}. +-spec open(OpenModeList) -> ReturnValue when + OpenModeList :: [OpenMode], + OpenMode :: 'read' | 'write', + ReturnValue :: {'ok', RegHandle} | {'error', ErrorId :: 'enotsup'}, + RegHandle :: reg_handle(). open(Modes) -> case os:type() of @@ -75,14 +78,17 @@ open(Modes) -> {error, enotsup} end. --spec close(reg_handle()) -> 'ok'. +-spec close(RegHandle) -> 'ok' when + RegHandle :: reg_handle(). close({win32reg, Reg}) when is_port(Reg) -> unlink(Reg), exit(Reg, die), ok. --spec current_key(reg_handle()) -> {'ok', string()}. +-spec current_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', string()}. current_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_current], @@ -94,12 +100,18 @@ current_key({win32reg, Reg}) when is_port(Reg) -> _ -> Root ++ [$\\|Name] end}. --spec change_key(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_open_key, Key). --spec change_key_create(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key_create(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key_create({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_create_key, Key). @@ -113,21 +125,30 @@ change_key(Reg, Cmd, Key) -> {error, Reason} end. --spec sub_keys(reg_handle()) -> {'ok', [string()]} | {'error', atom()}. +-spec sub_keys(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [SubKey]} | {'error', ErrorId :: atom()}, + SubKey :: string(). sub_keys({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_subkeys], Reg ! {self(), {command, Cmd}}, collect_keys(Reg, []). --spec delete_key(reg_handle()) -> 'ok' | {'error', atom()}. +-spec delete_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_delete_key], Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec set_value(reg_handle(), name(), value()) -> 'ok' | {'error', atom()}. +-spec set_value(RegHandle, Name, Value) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + Value :: value(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Name = @@ -140,7 +161,10 @@ set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec value(reg_handle(), name()) -> {'ok', value()} | {'error', atom()}. +-spec value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: {'ok', Value :: value()} | {'error', ErrorId :: atom()}. value({win32reg, Reg}, Name) when is_port(Reg) -> Cmd = [?cmd_get_value, Name, 0], @@ -152,14 +176,20 @@ value({win32reg, Reg}, Name) when is_port(Reg) -> {error, Reason} end. --spec values(reg_handle()) -> {'ok', [{name(), value()}]} | {'error', atom()}. +-spec values(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [ValuePair]} | {'error', ErrorId :: atom()}, + ValuePair :: {Name :: name(), Value :: value()}. values({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_values], Reg ! {self(), {command, Cmd}}, collect_values(Reg, []). --spec delete_value(reg_handle(), name()) -> 'ok' | {'error', atom()}. +-spec delete_value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Name = @@ -171,7 +201,9 @@ delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec expand(string()) -> string(). +-spec expand(String) -> ExpandedString when + String :: string(), + ExpandedString :: string(). expand(Value) -> expand(Value, [], []). @@ -195,7 +227,9 @@ expand([C|Rest], Env, Result) -> expand([], [], Result) -> lists:reverse(Result). --spec format_error(atom()) -> string(). +-spec format_error(ErrorId) -> ErrorString when + ErrorId :: atom(), + ErrorString :: string(). format_error(ErrorId) -> erl_posix_msg:message(ErrorId). @@ -203,7 +237,7 @@ format_error(ErrorId) -> %%% Implementation. -spec collect_values(port(), [{name(), value()}]) -> - {'ok', [{name(), value()}]} | {'error', atom()}. + {'ok', [{name(), value()}]} | {'error', ErrorId :: atom()}. collect_values(P, Result) -> case get_result(P) of @@ -215,7 +249,7 @@ collect_values(P, Result) -> {error, Reason} end. --spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', atom()}. +-spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', ErrorId :: atom()}. collect_keys(P, Result) -> case get_result(P) of diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index d41aeefa59..524d709431 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -203,6 +203,9 @@ zip_comment_length}). +-type zip_file() :: #zip_file{}. +-type zip_comment() :: #zip_comment{}. + %% Open a zip archive with options %% @@ -323,8 +326,33 @@ openzip_close(_) -> %% Accepted options: %% verbose, cooked, file_list, keep_old_files, file_filter, memory +-spec(unzip(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + unzip(F) -> unzip(F, []). +-spec(unzip(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + unzip(F, Options) -> case ?CATCH do_unzip(F, Options) of {ok, R} -> {ok, R}; @@ -345,6 +373,18 @@ do_unzip(F, Options) -> {ok, Files}. %% Iterate over all files in a zip archive +-spec(foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason} when + Fun :: fun((FileInArchive, GetInfo, GetBin, AccIn) -> AccOut), + FileInArchive :: file:name(), + GetInfo :: fun(() -> file:file_info()), + GetBin :: fun(() -> binary()), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Archive :: file:name() | {file:name(), binary()}, + Reason :: term()). + foldl(Fun, Acc0, Archive) when is_function(Fun, 4) -> ZipFun = fun({Name, GetInfo, GetBin}, A) -> @@ -368,8 +408,33 @@ foldl(_,_, _) -> %% Accepted options: %% verbose, cooked, memory, comment +-spec(zip(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files) -> zip(F, Files, []). +-spec(zip(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files, Options) -> case ?CATCH do_zip(F, Files, Options) of {ok, R} -> {ok, R}; @@ -392,8 +457,20 @@ do_zip(F, Files, Options) -> %% Accepted options: %% cooked, file_filter, file_output (latter 2 undocumented) +-spec(list_dir(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + list_dir(F) -> list_dir(F, []). +-spec(list_dir(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + Options :: [Option], + Option :: cooked). + list_dir(F, Options) -> case ?CATCH do_list_dir(F, Options) of {ok, R} -> {ok, R}; @@ -411,6 +488,10 @@ do_list_dir(F, Options) -> %% Print zip directory in short form +-spec(t(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + t(F) when is_pid(F) -> zip_t(F); t(F) when is_record(F, openzip) -> openzip_t(F); t(F) -> t(F, fun raw_short_print_info_etc/5). @@ -431,6 +512,10 @@ do_t(F, RawPrint) -> %% Print zip directory in long form (like ls -l) +-spec(tt(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + tt(F) when is_pid(F) -> zip_tt(F); tt(F) when is_record(F, openzip) -> openzip_tt(F); tt(F) -> t(F, fun raw_long_print_info_etc/5). @@ -605,11 +690,78 @@ get_list_dir_options(F, Options) -> get_list_dir_opt(Options, Opts). %% aliases for erl_tar compatibility +-spec(table(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + table(F) -> list_dir(F). + +-spec(table(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + + Options :: [Option], + Option :: cooked). + table(F, O) -> list_dir(F, O). + +-spec(create(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + create(F, Fs) -> zip(F, Fs). + +-spec(create(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). create(F, Fs, O) -> zip(F, Fs, O). + +-spec(extract(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + extract(F) -> unzip(F). + +-spec(extract(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + extract(F, O) -> unzip(F, O). @@ -990,21 +1142,52 @@ server_loop(OpenZip) -> {error, bad_msg} end. +-spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Reason :: term()). + zip_open(Archive) -> zip_open(Archive, []). +-spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Options :: [Option], + Option :: cooked | memory | {cwd, CWD :: string()}, + Reason :: term()). + zip_open(Archive, Options) -> Pid = spawn(fun() -> server_loop(not_open) end), request(self(), Pid, {open, Archive, Options}). +-spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(Pid) when is_pid(Pid) -> request(self(), Pid, get). +-spec(zip_close(ZipHandle) -> ok | {error, einval} when + ZipHandle :: pid()). + zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). +-spec(zip_get(FileName, ZipHandle) -> {ok, [Result]} | {error, Reason} when + FileName :: file:name(), + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(FileName, Pid) when is_pid(Pid) -> request(self(), Pid, {get, FileName}). +-spec(zip_list_dir(ZipHandle) -> Result | {error, Reason} when + Result :: [zip_comment() | zip_file()], + ZipHandle :: pid(), + Reason :: term()). + zip_list_dir(Pid) when is_pid(Pid) -> request(self(), Pid, list_dir). diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 9fcc9e6aaf..698070368f 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -53,7 +53,7 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1]). + otp_8923/1, otp_9282/1]). -export([dets_dirty_loop/0]). @@ -112,7 +112,7 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923] + otp_8899, otp_8903, otp_8923, otp_9282] end. groups() -> @@ -3857,6 +3857,28 @@ otp_8923(Config) when is_list(Config) -> file:delete(File), ok. +otp_9282(doc) -> + ["OTP-9282. The name of a table can be an arbitrary term"]; +otp_9282(suite) -> + []; +otp_9282(Config) when is_list(Config) -> + some_calls(make_ref(), Config), + some_calls({a,typical,name}, Config), + some_calls(fun() -> a_funny_name end, Config), + ok. + +some_calls(Tab, Config) -> + File = filename(ref, Config), + ?line {ok,T} = dets:open_file(Tab, [{file,File}]), + ?line T = Tab, + ?line false = dets:info(T, safe_fixed), + ?line File = dets:info(T, filename), + ?line ok = dets:insert(Tab, [{3,0}]), + ?line [{3,0}] = dets:lookup(Tab, 3), + ?line [{3,0}] = dets:traverse(Tab, fun(X) -> {continue, X} end), + ?line ok = dets:close(T), + file:delete(File). + %% %% Parts common to several test cases %% diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 4b59cee99e..0bcf3c5b71 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1199,7 +1199,7 @@ local_func(F, As0, Bs0) when is_atom(F) -> lfh_value_extra() -> %% Not documented. - {value, fun(F, As) -> local_func_value(F, As) end, []}. + {value, fun(F, As, a1, a2) -> local_func_value(F, As) end, [a1, a2]}. lfh_value() -> {value, fun(F, As) -> local_func_value(F, As) end}. diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index bc811355ab..280c95b1aa 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1161,7 +1161,7 @@ parse_forms2(String, Cont0, Line, Forms) -> {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); - {more, Cont} when element(3, Cont) =:= [] -> + {more, Cont} when element(4, Cont) =:= [] -> %% extra spaces after forms... parse_forms2([], Cont, Line, Forms); {more, Cont} -> diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl index 01de1f0600..d6f88a655e 100644 --- a/lib/stdlib/test/sofs_SUITE.erl +++ b/lib/stdlib/test/sofs_SUITE.erl @@ -1602,19 +1602,15 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a},b}])}, ER)), ?line {'EXIT', {badarg, _}} = (catch relative_product({}, ER)), - ?line eval(relative_product({relation([{a,b}])}, - from_term([],[{{atom},atom}])), - ER), - ?line eval(relative_product({relation([{a,b}]),relation([{a,1}])}, - from_term([{{b,1},{tjo,hej,sa}}])), - from_term([{a,{tjo,hej,sa}}])), - ?line eval(relative_product({relation([{a,b}]), ER}, - from_term([{{a,b},b}])), - ER), - ?line eval(relative_product({relation([{a,b},{c,a}]), - relation([{a,1},{a,2}])}, - from_term([{{b,1},b1},{{b,2},b2}])), - relation([{a,b1},{a,b2}])), + ?line relprod2({relation([{a,b}])}, from_term([],[{{atom},atom}]), ER), + ?line relprod2({relation([{a,b}]),relation([{a,1}])}, + from_term([{{b,1},{tjo,hej,sa}}]), + from_term([{a,{tjo,hej,sa}}])), + ?line relprod2({relation([{a,b}]), ER}, from_term([{{a,b},b}]), ER), + ?line relprod2({relation([{a,b},{c,a}]), + relation([{a,1},{a,2}])}, + from_term([{{b,1},b1},{{b,2},b2}]), + relation([{a,b1},{a,b2}])), ?line eval(relative_product({relation([{a,b}]), ER}), from_term([],[{atom,{atom,atom}}])), ?line eval(relative_product({from_term([{{a,[a,b]},[a]}]), @@ -1622,6 +1618,11 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a,[a,b]},{[a],[[a,b]]}}])), ok. +relprod2(A1T, A2, R) -> + %% A tuple as first argument is the old interface: + eval(relative_product(A1T, A2), R), + eval(relative_product(tuple_to_list(A1T), A2), R). + product_1(suite) -> []; product_1(doc) -> [""]; product_1(Conf) when is_list(Conf) -> diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl index 852afa1a4d..dc751aad16 100644 --- a/lib/stdlib/test/timer_simple_SUITE.erl +++ b/lib/stdlib/test/timer_simple_SUITE.erl @@ -229,7 +229,7 @@ cancel2(Config) when is_list(Config) -> tc(doc) -> "Test sleep/1 and tc/3."; tc(suite) -> []; tc(Config) when is_list(Config) -> - % This should both sleep and tc/3 + %% This should test both sleep and tc/3 ?line {Res1, ok} = timer:tc(timer, sleep, [500]), ?line ok = if Res1 < 500*1000 -> {too_early, Res1}; % Too early @@ -237,13 +237,40 @@ tc(Config) when is_list(Config) -> true -> ok end, - % This should both sleep and tc/2 + %% tc/2 ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]), ?line ok = if Res2 < 500*1000 -> {too_early, Res2}; % Too early Res2 > 800*1000 -> {too_late, Res2}; % Too much time true -> ok end, + + %% tc/1 + ?line {Res3, ok} = timer:tc(fun() -> timer:sleep(500) end), + ?line ok = if + Res3 < 500*1000 -> {too_early, Res3}; % Too early + Res3 > 800*1000 -> {too_late, Res3}; % Too much time + true -> ok + end, + + %% Check that timer:tc don't catch errors + ?line ok = try timer:tc(erlang, exit, [foo]) + catch exit:foo -> ok + end, + + ?line ok = try timer:tc(fun(Reason) -> 1 = Reason end, [foo]) + catch error:{badmatch,_} -> ok + end, + + ?line ok = try timer:tc(fun() -> throw(foo) end) + catch foo -> ok + end, + + %% Check that return values are propageted + Self = self(), + ?line {_, Self} = timer:tc(erlang, self, []), + ?line {_, Self} = timer:tc(fun(P) -> P end, [self()]), + ?line {_, Self} = timer:tc(fun() -> self() end), ?line Sec = timer:seconds(4), ?line Min = timer:minutes(4), diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml index 9c62b0fcf6..3a10bb209d 100644 --- a/lib/test_server/doc/src/notes.xml +++ b/lib/test_server/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index 118800e44a..93bb6b71c9 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 73a736f0e8..905ad895c9 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -662,6 +662,7 @@ main_process_loop(State) -> Imported = do_import_to_table(Fd,File, State#main_state.imported), reply(From, ok), + file:close(Fd), main_process_loop(State#main_state{imported=Imported}); {error,Reason} -> reply(From, {error, {cant_open_file,File,Reason}}), diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index b5c8e8a1b7..fe7f92de78 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -395,6 +395,7 @@ export_import(suite) -> []; export_import(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line ok = file:set_cwd(DataDir), + ?line PortCount = length(erlang:ports()), %% Export one module ?line {ok,f} = cover:compile(f), @@ -484,6 +485,9 @@ export_import(Config) when is_list(Config) -> ?line ?t:capture_stop(), ?line check_f_calls(1,0), + %% Check no raw files are left open + ?line PortCount = length(erlang:ports()), + %% Cleanup ?line ok = cover:stop(), ?line Files = lsfiles(), diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml index 3d27cf671b..4282e19769 100644 --- a/lib/wx/doc/src/notes.xml +++ b/lib/wx/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2009</year><year>2010</year> + <year>2009</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 8542435456..654bbbc05d 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index c1acf51b8a..571feabee0 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -263,11 +263,11 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>> = Xml, State {Xml, State#xmerl_sax_parser_state{encoding=Enc}} end; _ -> - {Xml, State#xmerl_sax_parser_state{encoding=utf8}} + {Xml, State} end end; detect_charset_1(Xml, State) -> - {Xml, State#xmerl_sax_parser_state{encoding=utf8}}. + {Xml, State}. %%---------------------------------------------------------------------- %% Function: convert_encoding(Enc) diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 280ff10efa..965a0ae7b4 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.2.8 +XMERL_VSN = 1.2.9 diff --git a/make/otp.mk.in b/make/otp.mk.in index bf287be416..a4e9dad46f 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -4,7 +4,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -240,9 +240,10 @@ FOP = @FOP@ DOCGEN=$(ERL_TOP)/lib/erl_docgen SPECS_ESRC = ../../src +SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript # Extract specifications and types from Erlang source files (-spec, -type) $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl - escript $(DOCGEN)/priv/bin/specs_gen.escript $(SPECS_FLAGS) -o$(dir $@) $< + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< $(MAN1DIR)/%.1: %.xml |