summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_fsm.c37
-rwxr-xr-xconfigure.ac2
-rw-r--r--lib/list_util.h140
-rw-r--r--lib/qrand.c9
-rw-r--r--lib/qrand.h4
-rw-r--r--lib/vector.c2
-rw-r--r--lib/vty_command.c61
-rw-r--r--lib/vty_io.c60
-rw-r--r--lib/vty_io.h4
-rw-r--r--lib/vty_io_basic.c54
-rw-r--r--lib/vty_io_basic.h3
-rw-r--r--lib/vty_io_term.c8
-rw-r--r--lib/vty_io_vtysh.c9
-rw-r--r--tests/test-list_util.c1230
-rw-r--r--vtysh/vtysh_config.c650
15 files changed, 1608 insertions, 665 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 9b63e9d2..03266369 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -36,6 +36,7 @@
#include "lib/qtimers.h"
#include "lib/sockunion.h"
+#include "lib/qrand.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_network.h"
@@ -1539,7 +1540,7 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event)
*/
static void
-bgp_hold_timer_set(bgp_connection connection, unsigned secs) ;
+bgp_hold_timer_set(bgp_connection connection, uint secs) ;
/*------------------------------------------------------------------------------
* Null action -- do nothing at all.
@@ -2282,27 +2283,43 @@ static qtimer_action bgp_keepalive_timer_action ;
*/
enum
{
- no_jitter = 0,
- with_jitter = 1,
+ no_jitter = false,
+ with_jitter = true,
} ;
+static qrand_seq_t jseq = QRAND_SEQ_INIT(314159265) ;
+
/*------------------------------------------------------------------------------
* Start or reset given qtimer with given interval, in seconds.
*
* If the interval is zero, unset the timer.
*/
static void
-bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
- int jitter, qtimer_action* action)
+bgp_timer_set(bgp_connection connection, qtimer timer, uint secs,
+ bool jitter, qtimer_action* action)
{
if (secs == 0)
qtimer_unset(timer) ;
else
{
- secs *= 40 ; /* a bit of resolution for jitter */
- if (jitter != no_jitter)
- secs -= ((rand() % ((int)secs + 1)) / 4) ;
- qtimer_set_interval(timer, QTIME(secs) / 40, action) ;
+ qtime_t interval ;
+
+ if (jitter)
+ {
+ /* Jitter as recommended in RFC 4271, Section 10
+ *
+ * We expect secs to be relatively small, so we can multiply it by
+ * some number 750..1000 without overflow !
+ */
+ qassert(secs <= (UINT_MAX / 1000)) ;
+
+ secs *= 750 + qrand(jseq, 250 + 1) ;
+ interval = QTIME(secs) / 1000 ;
+ }
+ else
+ interval = QTIME(secs) ;
+
+ qtimer_set_interval(timer, interval, action) ;
} ;
} ;
@@ -2313,7 +2330,7 @@ bgp_timer_set(bgp_connection connection, qtimer timer, unsigned secs,
* Setting 0 will unset the HoldTimer.
*/
static void
-bgp_hold_timer_set(bgp_connection connection, unsigned secs)
+bgp_hold_timer_set(bgp_connection connection, uint secs)
{
bgp_timer_set(connection, connection->hold_timer, secs, no_jitter,
bgp_hold_timer_action) ;
diff --git a/configure.ac b/configure.ac
index 778458b0..2406d758 100755
--- a/configure.ac
+++ b/configure.ac
@@ -7,7 +7,7 @@
##
AC_PREREQ(2.53)
-AC_INIT(Quagga, 0.99.20ex24b, [http://bugzilla.quagga.net])
+AC_INIT(Quagga, 0.99.20ex25b, [http://bugzilla.quagga.net])
AC_CONFIG_SRCDIR(lib/zebra.h)
AC_CONFIG_MACRO_DIR([m4])
diff --git a/lib/list_util.h b/lib/list_util.h
index 19183760..23677cd5 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -512,14 +512,18 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
*
* ddl_in_after(after, base, item, list) -- insert after
*
- * Treat as void function. The after & item may *not* be NULL.
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * If after == NULL, insert item at the end of the current list.
*
* Undefined if item is already on any list (including this one), or if
* after is not on the list.
*
* ddl_in_before(before, base, item, list) -- insert before
*
- * Treat as void function. The before & item may *not* be NULL.
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * If before == NULL, insert item at the start of the current list.
*
* Undefined if item is already on any list (including this one), or if
* before is not on the list.
@@ -570,6 +574,24 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
*
* Treat as function returning void*. Returns NULL if the item is NULL.
*
+ * ddl_slice(base, sub, list) -- remove sublist from given list
+ *
+ * Treat as void function. Does nothing if the sublist is empty.
+ *
+ * ddl_splice_after(after, base, sub, list)
+ * -- insert sublist after given item
+ *
+ * Treat as void function. Does nothing if the sublist is empty.
+ *
+ * If after == NULL, insert sublist at the end of the current list.
+ *
+ * ddl_splice_before(before, base, sub, list)
+ * -- insert sublist before given item
+ *
+ * Treat as void function. Does nothing if the sublist is empty.
+ *
+ * If before == NULL, insert sublist at the start of the current list.
+ *
* Note that ddl_del() and ddl_pop() do NOT affect the item->list.next
* or item->list.prev pointers.
*
@@ -577,7 +599,7 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
*
* "base" to be an r-value of type: struct base_pair(struct item*)*
*
- * That is... a variable or field which is a pointer to
+ * That is... a variable or field which is a pointer pair.
*
* "item" to be an l-value of type struct item*
*
@@ -586,7 +608,9 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
* "list" to be the name of a field in struct item
* of type: struct list_pair(struct item*)
*
+ * "sub" to be an r-value of type: struct base_pair(struct item*)*
*
+ * That is... a variable or field which is a pointer pointer pair.
*
*------------------------------------------------------------------------------
* For example:
@@ -672,23 +696,33 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
} while (0)
#define ddl_in_after(after, base, item, list) \
- do { (item)->list.next = (after)->list.next ; \
- (item)->list.prev = (after) ; \
- if ((after)->list.next != NULL) \
- (after)->list.next->list.prev = (item) ; \
+ do { if (after != NULL) \
+ { \
+ (item)->list.next = (after)->list.next ; \
+ (item)->list.prev = (after) ; \
+ if ((after)->list.next != NULL) \
+ (after)->list.next->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (after)->list.next = (item) ; \
+ } \
else \
- (base).tail = (item) ; \
- (after)->list.next = (item) ; \
+ ddl_append(base, item, list) ; \
} while (0)
#define ddl_in_before(before, base, item, list) \
- do { (item)->list.next = (before) ; \
- (item)->list.prev = (before)->list.prev ; \
- if ((before)->list.prev != NULL) \
- (before)->list.prev->list.next = (item) ; \
+ do { if (before != NULL) \
+ { \
+ (item)->list.next = (before) ; \
+ (item)->list.prev = (before)->list.prev ; \
+ if ((before)->list.prev != NULL) \
+ (before)->list.prev->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (before)->list.prev = (item) ; \
+ } \
else \
- (base).head = (item) ; \
- (before)->list.prev = (item) ; \
+ ddl_push(base, item, list) ; \
} while (0)
#define ddl_del(base, item, list) \
@@ -751,6 +785,82 @@ Private bool ssl_del_func(void** p_prev, void* item, size_t link_offset)
#define ddl_prev(item, list) \
((item) != NULL ? (item)->list.prev : NULL)
+#define ddl_slice(base, sub, list) \
+ do { if ((sub).head != NULL) \
+ { \
+ if ((sub).head->list.prev != NULL) \
+ (sub).head->list.prev->list.next = (sub).tail->list.next ; \
+ else \
+ { \
+ qassert((sub).head == (base).head) ; \
+ (base).head = (sub).tail->list.next ; \
+ } ; \
+ \
+ if ((sub).tail->list.next != NULL) \
+ (sub).tail->list.next->list.prev = (sub).head->list.prev ; \
+ else \
+ { \
+ qassert((sub).tail == (base).tail) ; \
+ (base).tail = (sub).head->list.prev ; \
+ } ; \
+ \
+ (sub).head->list.prev = NULL ; \
+ (sub).tail->list.next = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_splice_after(after, base, sub, list) \
+ do { if ((sub).head != NULL) \
+ { \
+ if (after != NULL) \
+ { \
+ (sub).head->list.prev = (after) ; \
+ (sub).tail->list.next = (after)->list.next ; \
+ if ((after)->list.next != NULL) \
+ (after)->list.next->list.prev = (sub).tail ; \
+ else \
+ (base).tail = (sub).tail ; \
+ (after)->list.next = (sub).head ; \
+ } \
+ else \
+ { \
+ (sub).head->list.prev = (base).tail ; \
+ (sub).tail->list.next = NULL ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = (sub).head ; \
+ else \
+ (base).head = (sub).head ; \
+ (base).tail = (sub).tail ; \
+ } ; \
+ } \
+ } while (0)
+
+#define ddl_splice_before(before, base, sub, list) \
+ do { if ((sub).head != NULL) \
+ { \
+ if (before != NULL) \
+ { \
+ (sub).tail->list.next = (before) ; \
+ (sub).head->list.prev = (before)->list.prev ; \
+ if ((before)->list.prev != NULL) \
+ (before)->list.prev->list.next = (sub).head ; \
+ else \
+ (base).head = (sub).head ; \
+ (before)->list.prev = (sub).tail ; \
+ } \
+ else \
+ { \
+ (sub).tail->list.next = (base).head ; \
+ (sub).head->list.prev = NULL ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = (sub).tail ; \
+ else \
+ (base).tail = (sub).tail ; \
+ (base).head = (sub).head ; \
+ } ; \
+ } \
+ } while (0)
+
/*==============================================================================
* Double Base, Single Link
*
diff --git a/lib/qrand.c b/lib/qrand.c
index 402bb432..a395b92c 100644
--- a/lib/qrand.c
+++ b/lib/qrand.c
@@ -32,17 +32,16 @@
*
* If range == 1, returns 0 every time !
*/
-extern int
-qrand(qrand_seq seq, int range)
+extern uint
+qrand(qrand_seq seq, uint range)
{
uint64_t r ;
- r = seq->last ^ 3141592653 ;
- r = ((r * 2650845021) + 5) & 0xFFFFFFFF ; /* see Knuth */
+ r = ((seq->last * 2650845021) + 5) & 0xFFFFFFFF ; /* see Knuth */
seq->last = r ;
if (range == 0)
return r >> 1 ;
else
- return (r % range) ;
+ return (r * range) >> 32 ;
} ;
diff --git a/lib/qrand.h b/lib/qrand.h
index e9a18436..cb81aceb 100644
--- a/lib/qrand.h
+++ b/lib/qrand.h
@@ -35,7 +35,7 @@
struct qrand_seq
{
- uint last ;
+ uint32_t last ;
} ;
typedef struct qrand_seq* qrand_seq ;
@@ -47,6 +47,6 @@ typedef struct qrand_seq qrand_seq_t[1] ;
* Functions
*/
-extern int qrand(qrand_seq seq, int range) ;
+extern uint qrand(qrand_seq seq, uint range) ;
#endif /* _ZEBRA_QRAND_H */
diff --git a/lib/vector.c b/lib/vector.c
index 09fb1b54..09457aac 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -903,7 +903,7 @@ vector_sak(int to_copy, vector to,
if (n_dst_nulls > 0)
memset(&dst->p_items[dst->end], 0, P_ITEMS_SIZE(n_dst_nulls)) ;
if (n_dst_move > 0)
- memmove(&dst->p_items[i_dst + n_dst], &dst->p_items[i_dst + n_src],
+ memmove(&dst->p_items[i_dst + n_src], &dst->p_items[i_dst + n_dst],
P_ITEMS_SIZE(n_dst_move)) ;
if (n_src_real > 0)
memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
diff --git a/lib/vty_command.c b/lib/vty_command.c
index d55220a6..02897a5a 100644
--- a/lib/vty_command.c
+++ b/lib/vty_command.c
@@ -131,7 +131,7 @@
* Prototypes
*/
static bool uty_cmd_loop_prepare(vty_io vio) ;
-static cmd_ret_t uty_cmd_hiatus(vty_io vio, cmd_ret_t ret) ;
+static cmd_ret_t uty_cmd_hiatus(vty_io vio) ;
static cmd_ret_t vty_cmd_auth(vty vty, node_type_t* p_next_node) ;
static void uty_cmd_out_cancel(vty_io vio) ;
static uint uty_show_error_context(vio_fifo ebuf, vio_vf vf) ;
@@ -1295,9 +1295,6 @@ vty_cmd_hiatus(vty vty, cmd_ret_t ret)
qassert((vio->cl_state == vcl_cq_running) || (vio->cl_state == vcl_running)) ;
- if (vio->state & vst_final)
- return CMD_STOP ; /* it is all over */
-
/* Handle the return code, either:
*
* * nothing further required -- any exception will have updated the
@@ -1306,26 +1303,31 @@ vty_cmd_hiatus(vty vty, cmd_ret_t ret)
* * this is a command or parsing error, for which a vx_quash exception
* must be raised, and suitable error message generated.
*/
- switch (ret)
+ if (vio->state & vst_final)
+ ret = CMD_STOP ; /* it is all over */
+ else
{
- case CMD_SUCCESS:
- case CMD_HIATUS:
- case CMD_WAITING:
- case CMD_STOP:
- case CMD_CANCEL:
- case CMD_IO_ERROR:
- break ;
+ switch (ret)
+ {
+ case CMD_SUCCESS:
+ case CMD_HIATUS:
+ case CMD_WAITING:
+ case CMD_STOP:
+ case CMD_CANCEL:
+ case CMD_IO_ERROR:
+ break ;
- case CMD_WARNING:
- case CMD_ERROR:
- case CMD_ERR_PARSING:
- case CMD_ERR_NO_MATCH:
- case CMD_ERR_AMBIGUOUS:
- case CMD_ERR_INCOMPLETE:
- default: /* assume some sort of error */
- uty_vio_exception(vio, vx_quash) ;
- uty_cmd_failed(vio, ret) ;
- break ;
+ case CMD_WARNING:
+ case CMD_ERROR:
+ case CMD_ERR_PARSING:
+ case CMD_ERR_NO_MATCH:
+ case CMD_ERR_AMBIGUOUS:
+ case CMD_ERR_INCOMPLETE:
+ default: /* assume some sort of error */
+ uty_vio_exception(vio, vx_quash) ;
+ uty_cmd_failed(vio, ret) ;
+ break ;
+ } ;
} ;
/* The meat of the hiatus -- loop back as required.
@@ -1342,11 +1344,12 @@ vty_cmd_hiatus(vty vty, cmd_ret_t ret)
* position is a little complicated. In particular, changes on the output
* side may affect whether is waiting in the CLI.
*/
- while (1)
+ do
{
vio->signal = CMD_SUCCESS ; /* we are here ! */
- ret = uty_cmd_hiatus(vio, ret) ;
+ if (ret != CMD_STOP)
+ ret = uty_cmd_hiatus(vio) ;
switch (ret)
{
@@ -1384,10 +1387,8 @@ vty_cmd_hiatus(vty vty, cmd_ret_t ret)
default:
zabort("invalid return code from uty_cmd_hiatus") ;
} ;
-
- if (vio->signal == CMD_SUCCESS)
- break ;
- } ;
+ }
+ while (vio->signal != CMD_SUCCESS);
if (ret == CMD_WAITING)
{
@@ -1418,8 +1419,10 @@ vty_cmd_hiatus(vty vty, cmd_ret_t ret)
* pselect() system has moved things forward to).
*/
static cmd_ret_t
-uty_cmd_hiatus(vty_io vio, cmd_ret_t ret)
+uty_cmd_hiatus(vty_io vio)
{
+ cmd_ret_t ret ;
+
/* (1) Do we need to close one or more vin and/or vout, or are we waiting for
* one to close ?
*
diff --git a/lib/vty_io.c b/lib/vty_io.c
index 930ef7dd..ebcdb0a9 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -353,7 +353,7 @@ uty_close_reason_set(vty_io vio, const char* why, bool replace)
if ((why == NULL) || (*why == '\0'))
vio->close_reason = qs_free(vio->close_reason) ;
else
- vio->close_reason = qs_printf(vio->close_reason, "Terminated: %s", why) ;
+ vio->close_reason = qs_printf(vio->close_reason, "terminated: %s", why) ;
} ;
/*------------------------------------------------------------------------------
@@ -370,7 +370,7 @@ uty_suspend_reason_set(vty_io vio, const char* why)
if ((why == NULL) || (*why == '\0'))
why = "*reason unknown (BUG)*" ;
- vio->suspend_reason = qs_printf(vio->suspend_reason, "Suspended: %s", why) ;
+ vio->suspend_reason = qs_printf(vio->suspend_reason, "suspended: %s", why) ;
} ;
/*------------------------------------------------------------------------------
@@ -912,7 +912,7 @@ uty_vout_base_close(vty_io vio)
{
qstring wrapped ;
- wrapped = qs_printf(NULL, "---\n%s\n", qs_string(vio->close_reason)) ;
+ wrapped = qs_printf(NULL, "%%---\n%% %s\n", qs_string(vio->close_reason));
switch(vf->vout_type)
{
@@ -1276,7 +1276,7 @@ uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
* and uty_cmd_loop_prepare()
* push_complete = false -- see uty_vout_push()
*
- * io_error = false -- so far, so good
+ * had_error = false -- so far, so good
*
* blocking = vio->blocking || io_type is blocking
*
@@ -2161,7 +2161,7 @@ uty_vf_not_open_error(vio_vf vf, vio_err_type_t err_type)
*
* * if the error is in the vin_base or the vout_base, but the vout_base is
* still working, then raise vx_stop_io_error, which will cancel down to
- * to vin_base/vout_base and then close vin_base and hence the vty.
+ * vin_base/vout_base and then close vin_base and hence the vty.
*
* * if the error is an I/O error, and the error is in the vout_base, then
* stop everything, final -- raise vx_sto_final exception !
@@ -2218,6 +2218,7 @@ uty_vf_error(vio_vf vf, vio_err_type_t err_type, int err)
vio_exception_t vx ;
vty_io vio ;
bool was_final ;
+ bool epipe ;
VTY_ASSERT_LOCKED() ;
@@ -2225,6 +2226,8 @@ uty_vf_error(vio_vf vf, vio_err_type_t err_type, int err)
/* Decide how to handle the error, post the exception and signal.
*/
+ epipe = false ;
+
if ((vf != vio->vin_base) && (vf != vio->vout_base))
vx = vx_io_error ;
else
@@ -2234,36 +2237,49 @@ uty_vf_error(vio_vf vf, vio_err_type_t err_type, int err)
if (vf == vio->vout_base)
{
if ((err_type & verr_io) || ((err_type & verr_mask) == verr_vout))
- vx = vx_stop_final ;
+ {
+ vx = vx_stop_final ;
+ epipe = (err_type & verr_io) && (err == EPIPE) ;
+ } ;
} ;
} ;
- was_final = (vio->state & vst_final) != 0 ;
+ was_final = (vio->state & vst_final) != 0 ; /* before this exception */
uty_vio_exception(vio, vx) ;
/* Log error and create error message as required.
*
- * Note that we log the first error on the vf and all I/O errors.
+ * Note that we log the first error on the vf and all I/O errors (except
+ * for EPIPE on the vout_base -- which we leave for the close reason
+ * logging).
*
* We only output an error message for the first error on the vf, and then
* only if was not vst_final before the error.
*/
- if (!vf->io_error || (err_type & verr_io))
+ if ((!vf->had_error || (err_type & verr_io)) && !epipe)
zlog_warn("%s", uty_error_message(vf, err_type, err, true /* log */).str) ;
- if (!vf->io_error && !was_final)
+ if (!vf->had_error && !was_final)
{
verr_mess_t err_mess ;
err_mess = uty_error_message(vf, err_type, err, false /* not log */) ;
if (vx == vx_stop_final)
- uty_close_reason_set(vio, err_mess.str, true /* replace */) ;
+ uty_close_reason_set(vio, err_mess.str, !epipe) ;
else
vio_fifo_printf(uty_cmd_get_ebuf(vio), "%% %s\n", err_mess.str) ;
} ;
+ vf->had_error = true ;
+
+ /* If this is an I/O error, tell the vfd not to generate any further I/O
+ * error messages on close etc.
+ */
+ if (err_type & verr_io)
+ vio_vfd_set_failed(vf->vfd) ;
+
/* Signal to the command loop and return CMD_IO_ERROR.
*
* One or both will be collected in the command loop "hiatus" and dealt
@@ -2291,9 +2307,12 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
const char* sort ;
bool vout ;
int fd ;
+ bool epipe ;
VTY_ASSERT_LOCKED() ;
+ epipe = (err_type & verr_io) && (err == EPIPE) ;
+
vout = false ;
fd = -1 ;
what = "*unknown verr_xxx (bug)*" ;
@@ -2330,6 +2349,7 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
default:
qassert(false) ;
+ epipe = false ;
} ;
name = vf->name ;
@@ -2387,6 +2407,7 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
qassert(false) ;
where = "*unknown VOUT_XXX (bug)*" ;
+ epipe = false ;
break ;
} ;
}
@@ -2434,6 +2455,7 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
qassert(false) ;
where = "*unknown VIN_XXX (bug)*" ;
+ epipe = false ;
break ;
} ;
} ;
@@ -2447,7 +2469,10 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
else
sort = "time-out" ;
- qfs_printf(qfs, "%s %s %s", where, what, sort) ;
+ if (!epipe)
+ qfs_printf(qfs, "%s %s %s", where, what, sort) ;
+ else
+ qfs_printf(qfs, "%s", where) ;
if (name != NULL)
qfs_printf(qfs, " '%s'", name) ;
@@ -2455,9 +2480,14 @@ uty_error_message(vio_vf vf, vio_err_type_t err_type, int err, bool log)
if (fd >= 0)
qfs_printf(qfs, " (fd=%d)", fd) ;
- if (((err_type & verr_io) != 0) && (err != 0))
- qfs_printf(qfs, ": %s", errtoa(err, 0).str) ;
- else if ((err_type & verr_vtysh) != 0)
+ if (err_type & verr_io)
+ {
+ if (!epipe)
+ qfs_printf(qfs, ": %s", errtoa(err, 0).str) ;
+ else
+ qfs_printf(qfs, " connection closed") ;
+ }
+ else if (err_type & verr_vtysh)
{
switch (err)
{
diff --git a/lib/vty_io.h b/lib/vty_io.h
index 3594d025..13139af3 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -375,7 +375,7 @@ struct vio_vf
/* General I/O
*
* Once an error has been posted -- see uty_vf_error() -- it is curtains
- * for the vio_vf. The io_error flag is to prevent multiple errors being
+ * for the vio_vf. The had_error flag is to prevent multiple errors being
* posted for the same vio_vf, because after the first error any subsequent
* errors are most likely a consequence of the first.
*
@@ -397,7 +397,7 @@ struct vio_vf
* line, or to return CMD_WAITING, which will then wait for some I/O to
* complete and signal the command loop to proceed.
*/
- bool io_error ; /* an I/O or timeout posted */
+ bool had_error ; /* an error has been posted */
bool blocking ; /* all I/O is blocking, pseudo or real */
diff --git a/lib/vty_io_basic.c b/lib/vty_io_basic.c
index 8fa579d6..fac64f66 100644
--- a/lib/vty_io_basic.c
+++ b/lib/vty_io_basic.c
@@ -434,6 +434,7 @@ vio_vfd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
*
* type -- X -- see below
* io_type -- X -- see below
+ * failed -- false
*
* action_info -- NULL -- set below if ! "blocking" vio_vfd
*
@@ -520,6 +521,17 @@ vio_vfd_set_action_info(vio_vfd vfd, void* action_info)
vfd->action_info = action_info ;
} ;
+/*------------------------------------------------------------------------------
+ * If there is a vfd, set the "failed" flag, which suppresses any further
+ * error logging - to avoid clutter.
+ */
+extern void
+vio_vfd_set_failed(vio_vfd vfd)
+{
+ if (vfd != NULL)
+ vfd->failed = true ;
+} ;
+
#if 0
/*------------------------------------------------------------------------------
* If there is a read action set for the give vio_vfd (if any), then kick it.
@@ -577,10 +589,25 @@ vio_vfd_read_close(vio_vfd vfd)
{
if (vfd->type == vfd_socket)
{
- int rc = shutdown(vfd->fd, SHUT_RD) ;
- if (rc < 0)
- zlog_err("%s: shutdown() failed, fd=%d: %s", __func__,
+ int rc, try ;
+
+ /* POSIX doesn't list EINTR as a failure for shutdown()
+ * but we handle it the same way as for close() in any case.
+ *
+ * We are completely paranoid and arrange not to get trapped
+ * here indefinitely.
+ */
+ try = 5 ;
+ do
+ rc = shutdown(vfd->fd, SHUT_RD) ;
+ while ((rc < 0) && (errno == EINTR) && (try-- > 0)) ;
+
+ if ((rc < 0) && !vfd->failed)
+ {
+ zlog_err("%s: shutdown() failed, fd=%d: %s", __func__,
vfd->fd, errtoa(errno, 0).str) ;
+ vfd->failed = true ;
+ } ;
} ;
vio_vfd_set_read(vfd, off, 0) ;
vfd->io_type ^= vfd_io_read ; /* now write only ! */
@@ -668,18 +695,23 @@ vio_vfd_close(vio_vfd vfd)
*/
if ((vfd->fd >= 0) && ((vfd->io_type & vfd_io_no_close) == 0))
{
- while (1)
- {
- int rc = close(vfd->fd) ;
-
- if (rc == 0)
- break ;
+ int rc, try ;
- if (errno == EINTR)
- continue ;
+ /* POSIX lists EINTR as a failure for close().
+ *
+ * We are completely paranoid and arrange not to get trapped
+ * here indefinitely.
+ */
+ try = 5 ;
+ do
+ rc = close(vfd->fd) ;
+ while ((rc < 0) && (errno == EINTR) && (try-- > 0)) ;
+ if ((rc < 0) && !vfd->failed)
+ {
zlog_err("%s: close() failed, fd=%d: %s", __func__, vfd->fd,
errtoa(errno, 0).str) ;
+ vfd->failed = true ;
} ;
} ;
diff --git a/lib/vty_io_basic.h b/lib/vty_io_basic.h
index 5c122873..3f731905 100644
--- a/lib/vty_io_basic.h
+++ b/lib/vty_io_basic.h
@@ -124,6 +124,8 @@ struct vio_vfd
vfd_type_t type ; /* used for half-close */
vfd_io_type_t io_type ; /* read, write, read/write */
+ bool failed ; /* avoid repeated error messages */
+
/* The rest of the vfd is to do with managing read/write ready and
* read/write timeouts for *non* blocking vfd.
*
@@ -219,6 +221,7 @@ extern vio_vfd vio_vfd_new(int fd, vfd_type_t type,
extern void vio_vfd_set_read_action(vio_vfd vfd, vio_vfd_action* action) ;
extern void vio_vfd_set_write_action(vio_vfd vfd, vio_vfd_action* action) ;
extern void vio_vfd_set_action_info(vio_vfd vfd, void* action_info) ;
+extern void vio_vfd_set_failed(vio_vfd vfd) ;
extern vio_vfd vio_vfd_read_close(vio_vfd vfd) ;
extern vio_vfd vio_vfd_close(vio_vfd vfd) ;
extern on_off_b vio_vfd_set_read(vio_vfd vfd, on_off_b how,
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 7ef7e0df..fc0fd8f0 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -431,17 +431,15 @@ uty_term_write_close(vio_vf vf)
*/
if (vio->state & vst_final)
{
- const char* reason, * sep ;
+ const char* reason ;
vf->cli = uty_cli_free(vf->cli) ;
reason = qs_string(vf->vio->close_reason) ;
- sep = ": " ;
if ((reason == NULL) || (*reason == '\0'))
- reason = sep = "" ;
+ reason = "closed" ;
- zlog_info("VTY connection (fd %d) closed%s%s",
- vio_vfd_fd(vf->vfd), sep, reason) ;
+ zlog_info("VTY connection (fd %d) %s", vio_vfd_fd(vf->vfd), reason) ;
}
else if (vf_active(vf->vout_state))
{
diff --git a/lib/vty_io_vtysh.c b/lib/vty_io_vtysh.c
index f506eaa3..321ee6aa 100644
--- a/lib/vty_io_vtysh.c
+++ b/lib/vty_io_vtysh.c
@@ -258,17 +258,16 @@ uty_sh_serv_write_close(vio_vf vf)
if (vio->state & vst_final)
{
- const char* reason, * sep ;
+ const char* reason ;
XFREE(MTYPE_VTY_CLI, vf->vtysh) ;
reason = qs_string(vf->vio->close_reason) ;
- sep = ": " ;
if ((reason == NULL) || (*reason == '\0'))
- reason = sep = "" ;
+ reason = "closed" ;
- zlog_info("vtysh connection '%s' (fd %d) closed%s%s", vf->name,
- vio_vfd_fd(vf->vfd), sep, reason) ;
+ zlog_info("vtysh connection '%s' (fd %d) %s", vf->name,
+ vio_vfd_fd(vf->vfd), reason) ;
}
else if (vf_active(vf->vout_state))
{
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
index 7078762c..1107586e 100644
--- a/tests/test-list_util.c
+++ b/tests/test-list_util.c
@@ -4,6 +4,7 @@
#include "command.h"
#include "list_util.h"
#include <string.h>
+#include "vector.h"
/* List util torture tests
*
@@ -16,6 +17,9 @@ static void test_ssl(void);
static void test_sdl(void);
static void test_ddl(void);
+static uint assert_limit ;
+
+
#define test_assert(true, message) \
do { if (!(true)) test_assert_fail(#true, message, __func__, __LINE__) ; \
} while (0)
@@ -26,6 +30,11 @@ test_assert_fail(const char* truth, const char* message, const char* func,
{
printf("*** %s %d: (%s) not true: %s\n", func, line, truth, message) ;
+ if (assert_limit > 0)
+ {
+ --assert_limit ;
+ assert(assert_limit > 0) ;
+ } ;
} ;
/*==============================================================================
@@ -47,6 +56,8 @@ main(int argc, char **argv)
srand(22) ; /* Consistent testing required */
+ assert_limit = 25 ;
+
test_ssl() ;
test_sdl() ;
test_ddl() ;
@@ -63,6 +74,22 @@ static unsigned majic(void* a, void* b)
return z ;
} ;
+/* aux list (vector) functions
+ */
+static vector aux_init(void) ;
+static void aux_reset(vector aux) ;
+static int aux_length(vector aux) ;
+static void* aux_item(vector aux, int index) ;
+static void* aux_next_item(vector aux, int index) ;
+static void* aux_prev_item(vector aux, int index) ;
+static void aux_insert(vector aux, int index, void* item) ;
+static void aux_delete(vector aux, void* item) ;
+static void aux_push_head(vector aux, void* item) ;
+static void* aux_pop_head(vector aux) ;
+static void aux_push_tail(vector aux, void* item) ;
+static void* aux_pop_tail(vector aux) ;
+static int aux_find(vector aux, void* item) ;
+
/*==============================================================================
* Single Base, Single Link
*
@@ -1162,157 +1189,201 @@ test_sdl(void)
* ddl_next(item, next) -- step to next item, if any
* ddl_prev(item, next) -- step to prev item, if any
*
+ * ddl_slice(base, sub, list) -- remove sublist from given list
+ * ddl_splice_after(after, base, sub, list)
+ * -- insert sublist after given item
+ * ddl_splice_before(before, base, sub, list)
+ * -- insert sublist before given item
+ *
* Cases to cover:
*/
/* Testing runs two lists through struct ddt_item objects. */
-enum list { a_list, b_list, list_count } ;
+enum list
+ {
+ a_list,
+ b_list, /* a_list..b_list is all main lists */
+
+ a_sub_list,
+ b_sub_list, /* a_sub_list..b_sub_list is all sub lists */
+
+ list_count /* 0..list_count -1 is all lists */
+ } ;
+typedef enum list list_t ;
+
+enum list_bit
+ {
+ a_list_bit = 1 << a_list,
+ b_list_bit = 1 << b_list,
+
+ a_sub_list_bit = 1 << a_sub_list,
+ b_sub_list_bit = 1 << b_sub_list,
+
+ list_bits = 1 << list_count,
+ } ;
+typedef enum list_bit list_bit_t ;
typedef struct ddt_item* ddt_item ;
struct ddt_list_pair dl_list_pair(ddt_item) ; /* Example struct constructor */
struct ddt_base_pair dl_base_pair(ddt_item) ;
-typedef struct dl_base_pair(ddt_item) ddt_base_pair_t ;
- /* Example typedef constructor */
-typedef struct dl_base_pair(ddt_item)* p_ddt_base_pair ;
+typedef struct dl_base_pair(ddt_item) ddt_base_pair_xt ;
/* Example typedef constructor */
+typedef struct ddt_list_pair ddt_list_pair_t ;
typedef struct ddt_list_pair* ddt_list_pair ;
-typedef struct ddt_base_pair* ddt_base_pair ;
-
-typedef struct ddt_rank* ddt_rank ;
-struct ddt_rank /* information for each list */
-{
- struct ddt_list_pair list ; /* the thing we are testing */
-
- int lister ;
- int list_found ;
- unsigned majic ;
-
- int ordinal ;
-};
+typedef struct ddt_base_pair ddt_base_pair_t ;
+typedef struct ddt_base_pair* ddt_base_pair ;
struct ddt_item /* the test items */
{
- struct ddt_rank a ;
+ ddt_list_pair_t a ;
char a_rubbish[21] ;
- struct ddt_rank b ;
+ uint seen ;
+
+ ddt_list_pair_t b ;
char b_rubbish[19] ;
} ;
-/* The test list base pairs, and pointers to the actual bases, for use in
+/* Pointers to the actual bases, for use in
* the verification code.
*/
-static ddt_base_pair ddt_bases[list_count] ;
-
-/* For some tests want to construct lists and know the first, last and
- * somewhere between items.
- */
+struct ddt_test_list
+{
+ const char* name ;
-enum where { first, middle, last, where_count } ;
+ ddt_base_pair base ;
-struct ddt_test_list_items
-{
- struct
- {
- ddt_item where[where_count] ;
- } list[list_count] ;
+ vector aux ;
} ;
-/*------------------------------------------------------------------------------
- * The test list items -- keep track here also for use in verification.
- */
-enum { ddt_max_items = 1000 } ;
+typedef struct ddt_test_list* ddt_test_list ;
+typedef struct ddt_test_list ddt_test_list_t ;
-static unsigned ddt_item_count = 0 ;
-static unsigned ddt_item_alloc = 0 ;
-static ddt_item ddt_items[ddt_max_items] ;
+static ddt_test_list_t ddt_lists[list_count] ;
-static inline ddt_item
-ddt_new_item(void)
+
+static inline ddt_list_pair
+ddt_list(ddt_item item, list_t lt)
{
- ddt_item item ;
+ switch (lt)
+ {
+ case a_list:
+ case a_sub_list:
+ return &(item->a) ;
- assert(ddt_item_count <= ddt_item_alloc) ;
+ case b_list:
+ case b_sub_list:
+ return &(item->b) ;
- if (ddt_item_count == ddt_item_alloc)
- {
- assert(ddt_item_alloc < ddt_max_items) ;
- ddt_items[ddt_item_alloc++] = malloc(sizeof(struct ddt_item)) ;
+ default:
+ assert(false) ;
} ;
+} ;
- item = ddt_items[ddt_item_count++] ;
+static inline ddt_base_pair
+ddt_base(list_t lt)
+{
+ switch (lt)
+ {
+ case a_list:
+ case a_sub_list:
+ case b_list:
+ case b_sub_list:
+ return ddt_lists[lt].base ;
+
+ default:
+ assert(false) ;
+ } ;
+} ;
- memset(item, ddt_item_count & 0xFF, sizeof(struct ddt_item)) ;
+static inline vector
+ddt_aux(list_t lt)
+{
+ switch (lt)
+ {
+ case a_list:
+ case a_sub_list:
+ case b_list:
+ case b_sub_list:
+ return ddt_lists[lt].aux ;
+
+ default:
+ assert(false) ;
+ } ;
+} ;
- item->a.lister = 0 ;
- item->b.lister = 0 ;
+static inline uint
+ddt_list_bit(list_t lt)
+{
+ switch (lt)
+ {
+ case a_list:
+ case a_sub_list:
+ return 1 ;
- item->a.ordinal = 0 ;
- item->b.ordinal = 0 ;
+ case b_list:
+ case b_sub_list:
+ return 2 ;
- return item ;
+ default:
+ assert(false) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Given an item and a list ordinal, return pointer to "rank" for item.
+ * Initialise a test list.
*/
-static inline ddt_rank
-ddt_get_rank(ddt_item item, enum list l)
+static void
+ddt_test_list_init(ddt_test_list tl, ddt_base_pair base, const char* name)
{
- if (item == NULL)
- return NULL ;
+ tl->name = name ;
- if (l == a_list)
- return &item->a ;
- if (l == b_list)
- return &item->b ;
+ tl->base = base ;
- assert(0) ;
+ base->head = NULL ;
+ base->tail = NULL ;
+
+ tl->aux = aux_init() ;
} ;
/*------------------------------------------------------------------------------
- * Keep track of what should be found on the lists, and majic marker to check
- * that don't get lost and point into space.
+ * The test list items -- keep track here also for use in verification.
*/
-static inline unsigned
-ddt_get_majic(ddt_item item, enum list l)
-{
- return majic(item, ddt_bases[l]) ;
-} ;
-
-static void
-ddt_test_list_add(ddt_item item, enum list l)
-{
- ddt_rank rank = ddt_get_rank(item, l) ;
-
- test_assert(rank->lister == 0, "already on list") ;
+enum { ddt_max_items = 1000 } ;
- rank->lister = 1 ;
- rank->majic = ddt_get_majic(item, l) ;
- rank->ordinal = 0 ; /* unknown ordinal */
-} ;
+static unsigned ddt_item_count = 0 ;
+static unsigned ddt_item_alloc = 0 ;
+static unsigned ddt_item_byte = 0 ;
+static ddt_item ddt_items[ddt_max_items] ;
-static void
-ddt_test_list_del(ddt_item item, enum list l)
+static ddt_item
+ddt_new_item(void)
{
- ddt_rank rank ;
+ ddt_item item ;
+ uint i, b ;
- if (item == NULL)
- return ;
+ assert(ddt_item_count <= ddt_item_alloc) ;
- rank = ddt_get_rank(item, l) ;
+ if (ddt_item_count == ddt_item_alloc)
+ {
+ assert(ddt_item_alloc < ddt_max_items) ;
+ ddt_items[ddt_item_alloc++] = malloc(sizeof(struct ddt_item)) ;
+ } ;
- test_assert(rank->lister == 1, "not on list") ;
+ item = ddt_items[ddt_item_count++] ;
- rank->lister = 0 ;
- rank->majic = 0 ;
+ b = ddt_item_byte + 0xA5 ;
+ for (i = 0 ; i < sizeof(struct ddt_item) ; ++i)
+ ((uchar*)item)[i] = (b += 0x55) ;
+
+ return item ;
} ;
/*------------------------------------------------------------------------------
@@ -1339,24 +1410,20 @@ ddt_test_list_del(ddt_item item, enum list l)
static void
ddt_verify_lists(void)
{
- ddt_base_pair base ;
- ddt_rank r ;
- ddt_item this ;
- ddt_item prev ;
- int l ;
- int i ;
+ uint i, l ;
- /* Wash the found flags */
- for (l = 0 ; l < list_count ; ++l)
- for (i = 0 ; i < (int)ddt_item_count ; ++i)
- ddt_get_rank(ddt_items[i], l)->list_found = 0 ;
+ /* Wash the seen flags
+ */
+ for (i = 0 ; i < ddt_item_count ; ++i)
+ ddt_items[i]->seen = 0 ;
- /* Walk the lists */
+ /* Walk the lists
+ */
for (l = 0 ; l < list_count ; ++l)
{
- int ordinal = 0 ;
+ ddt_base_pair base ;
- base = ddt_bases[l] ;
+ base = ddt_lists[l].base ;
if (base == NULL)
continue ;
@@ -1364,56 +1431,52 @@ ddt_verify_lists(void)
test_assert(base->head == base->tail, "broken list bases") ;
else
{
- r = ddt_get_rank(base->head, l) ;
- test_assert(r->list.prev == NULL, "broken list first item->prev") ;
- r = ddt_get_rank(base->tail, l) ;
- test_assert(r->list.next == NULL, "broken list last item->next") ;
+ vector aux ;
+ ddt_list_pair list ;
+ ddt_item this ;
+ ddt_item prev ;
+ uint s ;
+
+ list = ddt_list(base->head, l) ;
+ test_assert(list->prev == NULL, "broken list first item->prev") ;
+ list = ddt_list(base->tail, l) ;
+ test_assert(list->next == NULL, "broken list last item->next") ;
this = base->head ;
prev = NULL ;
+ s = ddt_list_bit(l) ; /* seen flag */
+
+ aux = ddt_lists[l].aux ;
+ i = 0 ;
while (this != NULL)
{
- r = ddt_get_rank(this, l) ;
+ test_assert(i < vector_length(aux), "list longer than aux list") ;
+ test_assert(aux_item(aux, i) == this,
+ "list and aux list out of step") ;
- test_assert(r->list.prev == prev, "broken item->prev") ;
+ test_assert((this->seen & s) == 0, "list item seen already") ;
+ this->seen |= s ;
- test_assert(r->lister, "should not be on this list") ;
+ list = ddt_list(this, l) ;
- test_assert(!r->list_found, "circular list") ;
- r->list_found = 1 ;
+ test_assert(list->prev == prev, "broken item->prev") ;
- if (r->ordinal != 0)
- {
- test_assert(r->ordinal > ordinal, "list out of order") ;
- ordinal = r->ordinal ;
- }
-
- test_assert(r->majic == ddt_get_majic(this, l),
- "wrong sort of majic") ;
prev = this ;
- this = r->list.next ;
+ this = list->next ;
+ ++i ;
} ;
+ test_assert(i == vector_length(aux), "list shorter than aux list") ;
test_assert(base->tail == prev, "broken tail pointer") ;
} ;
} ;
-
- /* Verify that did not miss anything should have found */
- /* Wash the found flags */
- for (l = 0 ; l < list_count ; ++l)
- for (i = 0 ; i < (int)ddt_item_count ; ++i)
- {
- r = ddt_get_rank(ddt_items[i], l) ;
-
- if (r->lister)
- test_assert(r->list_found, "should have found this on list") ;
- } ;
} ;
/*------------------------------------------------------------------------------
* Reset the test list handling
*
+ * Sets all ddt_lists empty.
*/
static void
ddt_reset_lists(void)
@@ -1421,126 +1484,130 @@ ddt_reset_lists(void)
int l ;
for (l = 0 ; l < list_count ; ++l)
- ddl_init(*(ddt_bases[l])) ;
+ {
+ ddl_init(*(ddt_lists[l].base)) ;
+ aux_reset(ddt_lists[l].aux) ;
+ } ;
ddt_item_count = 0 ;
} ;
/*------------------------------------------------------------------------------
- * Make lists with 'n' items each.
- *
- * If 'n' 0..3, makes lists with exactly that many items.
+ * Set all lists empty, make main lists with 'n' and sub lists with 'm' items
+ * each.
*
- * Otherwise, list length has +/- 25% jitter.
+ * Lists will have a random number of items in common.
*/
static void
-ddt_test_make_lists(struct ddt_test_list_items* test, int n)
+ddt_test_make_lists(int n, int m)
{
- ddt_base_pair base ;
ddt_item item ;
- ddt_rank rank ;
- enum list l ;
- int t ;
- int m ;
+ vector aux ;
- int req[list_count] ;
- int mid[list_count] ;
+ uint l, t ;
- /* Capture the requirements */
+ ddt_reset_lists() ;
+
+ uint req[list_count] ;
+
+ /* Capture the requirements
+ */
t = 0 ;
- m = 1 ;
for (l = 0 ; l < list_count ; ++l)
{
- m += m ;
- int j = (n + 1) / 4 ;
+ uint ln ;
- if (n <= 3)
- req[l] = n ;
- else
- req[l] = n - j + (rand() % (j + j + 1)) ;
+ switch (l)
+ {
+ case a_list:
+ case b_list:
+ ln = n ;
+ break ;
- mid[l] = req[l] / 2 ;
+ case a_sub_list:
+ case b_sub_list:
+ ln = m ;
+ break ;
- t += req[l] ;
+ default:
+ assert(false) ;
+ } ;
- test->list[l].where[first] = NULL ;
- test->list[l].where[middle] = NULL ;
- test->list[l].where[last] = NULL ;
+ t += ln ;
+ req[l] = ln ;
} ;
- ddt_reset_lists() ;
-
/* Have t = total number of items still required
- * m = 2^n -- where there are 'n' lists
+ *
+ * We ensure that the a_list and the a_sub_list are distinct, and that
+ * the b_list and the b_sub_list are also distinct.
*/
while (t != 0)
{
- int b ;
- int r ;
- r = (rand() % (m - 1)) + 1 ; /* bit pattern, at least one set */
+ uint r ;
- item = ddt_new_item() ;
+ r = rand() % list_bits ;
- b = 1 ;
- for (l = 0 ; l < list_count ; ++l)
+ if ((r & a_list_bit) || (m == 0))
+ r &= ~a_sub_list_bit ;
+ if ((r & b_list_bit) || (m == 0))
+ r &= ~b_sub_list_bit ;
+
+ item = NULL ;
+
+ l = 0 ;
+ while (r != 0)
{
- if ((req[l] != 0) && ((r & b) != 0))
+ if ((r & 1) && (req[l] != 0))
{
--req[l] ;
--t ;
- ddt_test_list_add(item, l) ;
-
- if (test->list[l].where[first] == NULL)
- test->list[l].where[first] = item ;
-
- if (mid[l]-- == 0)
- test->list[l].where[middle] = item ;
+ if (item == NULL)
+ item = ddt_new_item() ;
- test->list[l].where[last] = item ;
-
- base = ddt_bases[l] ;
- rank = ddt_get_rank(item, l) ;
-
- if (base->head == NULL)
- {
- base->head = item ;
- base->tail = item ;
- rank->list.next = NULL ;
- rank->list.prev = NULL ;
- }
- else if (rand() & 1)
- {
- rank->list.next = base->head ;
- rank->list.prev = NULL ;
- ddt_get_rank(base->head, l)->list.prev = item ;
- base->head = item ;
- }
- else
- {
- rank->list.next = NULL ;
- rank->list.prev = base->tail ;
- ddt_get_rank(base->tail, l)->list.next = item ;
- base->tail = item ;
- }
+ aux = ddt_lists[l].aux ;
+ aux_insert(aux, rand() % (vector_length(aux) + 1), item) ;
} ;
- b <<= 1 ;
+
+ r >>= 1 ;
+ l += 1 ;
}
} ;
- /* Number the items */
+ /* Construct the lists
+ */
for (l = 0 ; l < list_count ; ++l)
{
- int o = 0 ;
+ ddt_base_pair base ;
+ ddt_list_pair list ;
+ uint i ;
+
+ base = ddt_lists[l].base ;
+ aux = ddt_lists[l].aux ;
- base = ddt_bases[l] ;
+ base->head = NULL ; /* Both NULL if list empty */
+ base->tail = NULL ;
- item = base->head ;
- while (item != NULL)
+ list = NULL ;
+ for (i = 0 ; i < vector_length(aux) ; ++i)
{
- rank = ddt_get_rank(item, l) ;
- rank->ordinal = ++o ; /* first is 1 */
- item = rank->list.next ;
+ ddt_list_pair prev ;
+
+ prev = list ;
+
+ item = aux_item(aux, i) ;
+ list = ddt_list(item, l) ;
+
+ if (prev == NULL)
+ base->head = item ;
+ else
+ prev->next = item ;
+
+ list->next = NULL ;
+ list->prev = base->tail ;
+
+ base->tail = item ;
} ;
} ;
@@ -1562,6 +1629,12 @@ ddt_test_make_lists(struct ddt_test_list_items* test, int n)
* ddl_tail(base) -- return tail of list
* ddl_next(item, next) -- step to next item, if any
* ddl_prev(item, next) -- step to prev item, if any
+ *
+ * ddl_slice(base, sub, list) -- remove sublist from given list
+ * ddl_splice_after(after, base, sub, list)
+ * -- insert sublist after given item
+ * ddl_splice_before(before, base, sub, list)
+ * -- insert sublist before given item
*/
static struct ddt_parent
@@ -1578,19 +1651,23 @@ test_ddl(void)
{
struct ddt_base_pair a_base ;
struct ddt_parent* b ;
+ ddt_base_pair_t a_sub ;
+ ddt_base_pair_t b_sub ;
- struct ddt_test_list_items test ;
int n ;
- int o ;
int base_n = 23 ;
int rand_n = 17 ;
printf("=== Testing ddl -- Double Base, Double Link -- stuff\n") ;
- /* Initialise the test support */
- ddt_bases[a_list] = &a_base ;
- ddt_bases[b_list] = &ddt_parent.base ;
+ /* Initialise the test support
+ */
+ ddt_test_list_init(&ddt_lists[a_list], &a_base, "a-list") ;
+ ddt_test_list_init(&ddt_lists[b_list], &ddt_parent.base, "b-list") ;
+
+ ddt_test_list_init(&ddt_lists[a_sub_list], &a_sub, "a-sub-list") ;
+ ddt_test_list_init(&ddt_lists[b_sub_list], &b_sub, "b-sub-list") ;
ddt_item_count = 0 ;
ddt_item_alloc = 0 ;
@@ -1604,7 +1681,6 @@ test_ddl(void)
ddt_verify_lists() ; /* start as mean to go on */
-
/* ddl_push(base, item, list) -- insert at head of list
*
* Cases: (a) empty list
@@ -1615,7 +1691,7 @@ test_ddl(void)
ddt_reset_lists() ;
n = base_n + (rand() % rand_n) ;
- while (n)
+ while (n > 0)
{
ddt_item item ;
int r ;
@@ -1627,19 +1703,17 @@ test_ddl(void)
if (r & 1)
{
- ddl_push(a_base, item, a.list) ;
+ ddl_push(a_base, item, a) ;
test_assert(a_base.head == item, "ddl_push broken") ;
- ddt_test_list_add(item, a_list) ;
- item->a.ordinal = n ;
+ aux_push_head(ddt_lists[a_list].aux, item) ;
} ;
ddt_verify_lists() ;
if (r & 2)
{
- ddl_push(b->base, item, b.list) ;
+ ddl_push(b->base, item, b) ;
test_assert(b->base.head == item, "ddl_push broken") ;
- ddt_test_list_add(item, b_list) ;
- item->b.ordinal = n ;
+ aux_push_head(ddt_lists[b_list].aux, item) ;
} ;
ddt_verify_lists() ;
@@ -1657,33 +1731,29 @@ test_ddl(void)
ddt_reset_lists() ;
n = base_n + (rand() % rand_n) ;
- o = 0 ;
- while (n)
+ while (n > 0)
{
ddt_item item ;
int r ;
printf(".") ;
- ++o ;
item = ddt_new_item() ;
r = (rand() % 3) + 1 ;
if (r & 1)
{
- ddl_append(a_base, item, a.list) ;
+ ddl_append(a_base, item, a) ;
test_assert(a_base.tail == item, "ddl_append broken") ;
- ddt_test_list_add(item, a_list) ;
- item->a.ordinal = o ;
+ aux_push_tail(ddt_lists[a_list].aux, item) ;
} ;
ddt_verify_lists() ;
if (r & 2)
{
- ddl_append(b->base, item, b.list) ;
+ ddl_append(b->base, item, b) ;
test_assert(b->base.tail == item, "ddl_append broken") ;
- ddt_test_list_add(item, b_list) ;
- item->b.ordinal = o ;
+ aux_push_tail(ddt_lists[b_list].aux, item) ;
} ;
ddt_verify_lists() ;
@@ -1693,37 +1763,60 @@ test_ddl(void)
/* ddl_in_after(after, base, item, list) -- insert after
*
- * Cases: (a) after first and only (so is also last)
- * (b) after first when more than one
- * (c) after last when more than one
- * (d) after something between
+ * NB: after may NOT be NULL.
+ *
+ * Cases: (a) after NULL when list is empty
+ * (b) after NULL when list has 1 or more entries
+ * (c) after first and only (so is also last)
+ * (d) after first when more than one
+ * (e) after last when more than one
+ * (f) after something between
*/
printf(" ddl_in_after test") ;
n = base_n + (rand() % rand_n) ;
- while (n)
+ while (n >= 0)
{
- ddt_item item ;
- ddt_item after ;
+ int w ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
- item = ddt_new_item() ;
- after = test.list[a_list].where[n % where_count] ;
- ddl_in_after(after, a_base, item, a.list) ;
- test_assert(after->a.list.next == item, "ddl_in_after broken") ;
- test_assert(item->a.list.prev == after, "ddl_in_after broken") ;
- ddt_test_list_add(item, a_list) ;
- ddt_verify_lists() ;
+ for (w = 0 ; w <= n ; ++w)
+ {
+ ddt_item item ;
+ ddt_item after ;
+ vector aux ;
- item = ddt_new_item() ;
- after = test.list[b_list].where[n % where_count] ;
- ddl_in_after(after, b->base, item, b.list) ;
- test_assert(after->b.list.next == item, "ddl_in_after broken") ;
- test_assert(item->b.list.prev == after, "ddl_in_after broken") ;
- ddt_test_list_add(item, b_list) ;
- ddt_verify_lists() ;
+ ddt_test_make_lists(n, n) ;
+
+ item = ddt_new_item() ;
+
+ aux = ddt_aux(a_list) ;
+ after = aux_item(aux, w) ;
+
+ ddl_in_after(after, a_base, item, a) ;
+
+ if (after != NULL)
+ aux_insert(aux, w+1, item) ;
+ else
+ aux_push_tail(aux, item) ;
+
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+
+ aux = ddt_aux(b_list) ;
+ after = aux_item(aux, w) ;
+
+ ddl_in_after(after, b->base, item, b) ;
+
+ if (after != NULL)
+ aux_insert(aux, w+1, item) ;
+ else
+ aux_push_tail(aux, item) ;
+
+ ddt_verify_lists() ;
+ } ;
--n ;
} ;
@@ -1731,38 +1824,58 @@ test_ddl(void)
/* ddl_in_before(before, base, item, list) -- insert before
*
- * Cases: (a) before first and only (so is also last)
- * (b) before first when more than one
- * (c) before last when more than one
- * (d) before something between
+ * Cases: (a) before NULL when list empty
+ * (b) before NULL when list has 1 or more entries
+ * (c) before first and only (so is also last)
+ * (d) before first when more than one
+ * (e) before last when more than one
+ * (f) before something between
*/
printf(" ddl_in_before test") ;
n = base_n + (rand() % rand_n) ;
- while (n)
+ while (n >= 0)
{
- ddt_item item ;
- ddt_item before ;
+ int w ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ for (w = 0 ; w <= n ; ++w)
+ {
+ ddt_item item ;
+ ddt_item before ;
+ vector aux ;
- item = ddt_new_item() ;
- before = test.list[a_list].where[n % where_count] ;
- ddl_in_before(before, a_base, item, a.list) ;
- test_assert(before->a.list.prev == item, "ddl_in_before broken") ;
- test_assert(item->a.list.next == before, "ddl_in_before broken") ;
- ddt_test_list_add(item, a_list) ;
- ddt_verify_lists() ;
+ ddt_test_make_lists(n, n) ;
- item = ddt_new_item() ;
- before = test.list[b_list].where[n % where_count] ;
- ddl_in_before(before, b->base, item, b.list) ;
- test_assert(before->b.list.prev == item, "ddl_in_before broken") ;
- test_assert(item->b.list.next == before, "ddl_in_before broken") ;
- ddt_test_list_add(item, b_list) ;
- ddt_verify_lists() ;
+ item = ddt_new_item() ;
+
+ aux = ddt_aux(a_list) ;
+ before = aux_item(aux, w) ;
+
+ ddl_in_before(before, a_base, item, a) ;
+
+ if (before != NULL)
+ aux_insert(aux, w, item) ;
+ else
+ aux_push_head(aux, item) ;
+
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+
+ aux = ddt_aux(b_list) ;
+ before = aux_item(aux, w) ;
+
+ ddl_in_before(before, b->base, item, b) ;
+
+ if (before != NULL)
+ aux_insert(aux, w, item) ;
+ else
+ aux_push_head(aux, item) ;
+
+ ddt_verify_lists() ;
+ } ;
--n ;
} ;
@@ -1782,46 +1895,37 @@ test_ddl(void)
ddt_item item ;
ddt_item temp ;
ddt_item peek ;
- int ordinal ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ ddt_test_make_lists(n, n) ;
- ordinal = 0 ;
while (1)
{
peek = a_base.head ;
- temp = ddl_pop(&item, a_base, a.list) ;
+ temp = ddl_pop(&item, a_base, a) ;
test_assert(temp == item, "ddl_pop broken") ;
test_assert(peek == item, "ddl_pop broken") ;
- ddt_test_list_del(item, a_list) ;
+ aux_pop_head(ddt_aux(a_list)) ;
ddt_verify_lists() ;
if (item == NULL)
break ;
-
- ++ordinal ;
- test_assert(item->a.ordinal == ordinal, "ddl_pop not in order") ;
} ;
- ordinal = 0 ;
while (1)
{
peek = b->base.head ;
- temp = ddl_pop(&item, b->base, b.list) ;
+ temp = ddl_pop(&item, b->base, b) ;
test_assert(temp == item, "ddl_pop broken") ;
test_assert(peek == item, "ddl_pop broken") ;
- ddt_test_list_del(item, b_list) ;
+ aux_pop_head(ddt_aux(b_list)) ;
ddt_verify_lists() ;
if (item == NULL)
break ;
-
- ++ordinal ;
- test_assert(item->b.ordinal == ordinal, "ddl_pop not in order") ;
} ;
--n ;
@@ -1834,7 +1938,6 @@ test_ddl(void)
* (b) list with one item
* (c) empty list
*/
-
printf(" ddl_crop test") ;
n = base_n + (rand() % rand_n) ;
@@ -1843,58 +1946,37 @@ test_ddl(void)
ddt_item item ;
ddt_item temp ;
ddt_item peek ;
- int ordinal ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ ddt_test_make_lists(n, n) ;
- ordinal = 0 ;
while (1)
{
peek = a_base.tail ;
- temp = ddl_crop(&item, a_base, a.list) ;
+ temp = ddl_crop(&item, a_base, a) ;
test_assert(temp == item, "ddl_crop broken") ;
test_assert(peek == item, "ddl_crop broken") ;
- ddt_test_list_del(item, a_list) ;
+ aux_pop_tail(ddt_aux(a_list)) ;
ddt_verify_lists() ;
if (item == NULL)
break ;
-
- if (ordinal == 0)
- ordinal = item->a.ordinal ;
- else
- {
- test_assert(ordinal > 0, "ordinal out of whack") ;
- --ordinal ;
- test_assert(item->a.ordinal == ordinal, "ddl_crop not in order") ;
- } ;
} ;
- ordinal = 0 ;
while (1)
{
peek = b->base.tail ;
- temp = ddl_crop(&item, b->base, b.list) ;
+ temp = ddl_crop(&item, b->base, b) ;
test_assert(temp == item, "ddl_crop broken") ;
test_assert(peek == item, "ddl_crop broken") ;
- ddt_test_list_del(item, b_list) ;
+ aux_pop_tail(ddt_aux(b_list)) ;
ddt_verify_lists() ;
if (item == NULL)
break ;
-
- if (ordinal == 0)
- ordinal = item->b.ordinal ;
- else
- {
- test_assert(ordinal > 0, "ordinal out of whack") ;
- --ordinal ;
- test_assert(item->b.ordinal == ordinal, "ddl_crop not in order") ;
- } ;
} ;
--n ;
@@ -1906,28 +1988,38 @@ test_ddl(void)
* Cases: (a) first and only (so is also last)
* (b) first when more than one
* (c) last when more than one
- * (d) something between
+ * (d) everything between
*/
printf(" ddl_del test") ;
n = base_n + (rand() % rand_n) ;
- while (n)
+ while (n > 0)
{
- ddt_item item ;
+ int w ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ for (w = 0 ; w < n ; ++w)
+ {
+ ddt_item item ;
+ vector aux ;
- item = test.list[a_list].where[n % where_count] ;
- ddl_del(a_base, item, a.list) ;
- ddt_test_list_del(item, a_list) ;
- ddt_verify_lists() ;
+ ddt_test_make_lists(n, n) ;
- item = test.list[b_list].where[n % where_count] ;
- ddl_del(b->base, item, b.list) ;
- ddt_test_list_del(item, b_list) ;
- ddt_verify_lists() ;
+ aux = ddt_aux(a_list) ;
+ item = aux_item(aux, w) ;
+
+ ddl_del(a_base, item, a) ;
+ aux_delete(aux, item) ;
+ ddt_verify_lists() ;
+
+ aux = ddt_aux(b_list) ;
+ item = aux_item(aux, w) ;
+
+ ddl_del(b->base, item, b) ;
+ aux_delete(aux, item) ;
+ ddt_verify_lists() ;
+ } ;
--n ;
} ;
@@ -1949,18 +2041,20 @@ test_ddl(void)
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ ddt_test_make_lists(n, n) ;
while (1)
{
item = a_base.head ;
- peek = (item != NULL) ? item->a.list.next : NULL ;
+ peek = (item != NULL) ? item->a.next : NULL ;
- ddl_del_head(a_base, a.list) ;
+ ddl_del_head(a_base, a) ;
test_assert(a_base.head == peek, "ddl_del_head broken") ;
- ddt_test_list_del(item, a_list) ;
+ if (item != NULL)
+ aux_delete(ddt_aux(a_list), item) ;
+
ddt_verify_lists() ;
if (item == NULL)
@@ -1970,13 +2064,15 @@ test_ddl(void)
while (1)
{
item = b->base.head ;
- peek = (item != NULL) ? item->b.list.next : NULL ;
+ peek = (item != NULL) ? item->b.next : NULL ;
- ddl_del_head(b->base, b.list) ;
+ ddl_del_head(b->base, b) ;
test_assert(b->base.head == peek, "ddl_del_head broken") ;
- ddt_test_list_del(item, b_list) ;
+ if (item != NULL)
+ aux_delete(ddt_aux(b_list), item) ;
+
ddt_verify_lists() ;
if (item == NULL)
@@ -2003,18 +2099,20 @@ test_ddl(void)
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ ddt_test_make_lists(n, n) ;
while (1)
{
item = a_base.tail ;
- peek = (item != NULL) ? item->a.list.prev : NULL ;
+ peek = (item != NULL) ? item->a.prev : NULL ;
- ddl_del_tail(a_base, a.list) ;
+ ddl_del_tail(a_base, a) ;
test_assert(a_base.tail == peek, "ddl_del_tail broken") ;
- ddt_test_list_del(item, a_list) ;
+ if (item != NULL)
+ aux_delete(ddt_aux(a_list), item) ;
+
ddt_verify_lists() ;
if (item == NULL)
@@ -2024,13 +2122,15 @@ test_ddl(void)
while (1)
{
item = b->base.tail ;
- peek = (item != NULL) ? item->b.list.prev : NULL ;
+ peek = (item != NULL) ? item->b.prev : NULL ;
- ddl_del_tail(b->base, b.list) ;
+ ddl_del_tail(b->base, b) ;
test_assert(b->base.tail == peek, "ddl_del_tail broken") ;
- ddt_test_list_del(item, b_list) ;
+ if (item != NULL)
+ aux_delete(ddt_aux(b_list), item) ;
+
ddt_verify_lists() ;
if (item == NULL)
@@ -2055,7 +2155,7 @@ test_ddl(void)
{
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ ddt_test_make_lists(n, n) ;
test_assert(ddl_head(a_base) == a_base.head, "ddl_head broken") ;
test_assert(ddl_tail(a_base) == a_base.tail, "ddl_head broken") ;
@@ -2066,7 +2166,6 @@ test_ddl(void)
} ;
printf("\n") ;
-
/* ddl_next(item, next) -- step to next item, if any
* ddl_prev(item, next) -- step to prev item, if any
*
@@ -2078,39 +2177,444 @@ test_ddl(void)
printf(" ddl_next and ddl_prev test") ;
n = base_n + (rand() % rand_n) ;
- while (n)
+ while (n > 0)
{
- ddt_item item ;
- ddt_item where ;
+ int w ;
printf(".") ;
- ddt_test_make_lists(&test, n) ;
+ for (w = 0 ; w < n ; ++w)
+ {
+ ddt_item item ;
+ vector aux ;
+ int i ;
+
+ ddt_test_make_lists(n, n) ;
+
+ aux = ddt_aux(a_list) ;
+ item = aux_item(aux, w) ;
+
+ i = aux_find(aux, item) ;
+ test_assert(i == w, "ddl_next/_prev list and aux list out of step") ;
- where = test.list[a_list].where[n % where_count] ;
- item = ddl_next(where, a.list) ;
- test_assert(item == where->a.list.next, "ddl_next broken") ;
+ test_assert(ddl_next(item, a) == aux_next_item(aux, i),
+ "ddl_next broken") ;
+ test_assert(ddl_prev(item, a) == aux_prev_item(aux, i),
+ "ddl_prev broken") ;
- where = test.list[b_list].where[n % where_count] ;
- item = ddl_next(where, b.list) ;
- test_assert(item == where->b.list.next, "ddl_next broken") ;
+ aux = ddt_aux(b_list) ;
+ item = aux_item(aux, w) ;
- where = test.list[a_list].where[n % where_count] ;
- item = ddl_prev(where, a.list) ;
- test_assert(item == where->a.list.prev, "ddl_prev broken") ;
+ i = aux_find(aux, item) ;
+ test_assert(i == w, "ddl_next/_prev list and aux list out of step") ;
- where = test.list[b_list].where[n % where_count] ;
- item = ddl_prev(where, b.list) ;
- test_assert(item == where->b.list.prev, "ddl_prev broken") ;
+ test_assert(ddl_next(item, b) == aux_next_item(aux, i),
+ "ddl_next broken") ;
+ test_assert(ddl_prev(item, b) == aux_prev_item(aux, i),
+ "ddl_prev broken") ;
+ } ;
--n ;
} ;
printf("\n") ;
+ /* ddl_slice(base, sub, list) -- remove sublist from given list
+ *
+ * Cases: (a) sub is empty
+ * (b) sub is the entire list -- list with 1 or more entries.
+ * (c) sub includes head of list, but 1 or more entries remain
+ * (d) sub includes tail of list, but 1 or more entries remain
+ * (e) sub is part of list, and 1 or more entries remain at either
+ * end.
+ */
+ printf(" ddl_slice") ;
-}
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ int s, e ;
+
+ printf(".") ;
+
+ for (s = 0 ; s <= n ; ++s)
+ {
+ for (e = s ; e <= n ; ++e)
+ {
+ vector aux ;
+
+ ddt_test_make_lists(n, 0) ;
+
+ if (s < e)
+ {
+ vector aux_sub ;
+
+ aux = ddt_aux(a_list) ;
+
+ a_sub.head = aux_item(aux, s) ;
+ a_sub.tail = aux_item(aux, e - 1) ;
+
+ aux_sub = ddt_aux(a_sub_list) ;
+ assert(vector_length(aux_sub) == 0) ;
+
+ vector_move_extract(aux_sub, aux, s, e - s) ;
+ } ;
+
+ ddl_slice(a_base, a_sub, a) ;
+ ddt_verify_lists() ;
+
+ if (s < e)
+ {
+ vector aux_sub ;
+
+ aux = ddt_aux(b_list) ;
+
+ b_sub.head = aux_item(aux, s) ;
+ b_sub.tail = aux_item(aux, e - 1) ;
+
+ aux_sub = ddt_aux(b_sub_list) ;
+ assert(vector_length(aux_sub) == 0) ;
+
+ vector_move_extract(aux_sub, aux, s, e - s) ;
+ } ;
+
+ ddl_slice(b->base, b_sub, b) ;
+ ddt_verify_lists() ;
+ } ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_splice_after(after, base, sub, list)
+ * -- insert sublist after given item
+ * Cases: (a) sub is empty and list is empty
+ * (b) sub has 1 or more entries and list is empty (after = NULL).
+ * (c) sub includes head of list, but 1 or more entries remain
+ * (d) sub includes tail of list, but 1 or more entries remain
+ * (e) sub is part of list, and 1 or more entries remain at either
+ * end.
+ */
+ printf(" ddl_splice_after") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ printf(".") ;
+
+ int m ;
+
+ for (m = 0 ; m <= 5 ; ++m)
+ {
+ int w ;
+
+ for (w = 0 ; w <= n ; ++w)
+ {
+ ddt_item after ;
+ vector aux, s_aux ;
+
+ ddt_test_make_lists(n, m) ;
+
+ aux = ddt_aux(a_list) ;
+ after = aux_item(aux, w) ;
+
+ ddl_splice_after(after, a_base, a_sub, a) ;
+
+ s_aux = ddt_aux(a_sub_list) ;
+
+ if (after != NULL)
+ vector_move_replace(aux, w+1, 0, s_aux, 0, m) ;
+ else
+ vector_move_replace(aux, w, 0, s_aux, 0, m) ;
+
+ ddl_init(a_sub) ;
+
+ ddt_verify_lists() ;
+
+ aux = ddt_aux(b_list) ;
+ after = aux_item(aux, w) ;
+
+ ddl_splice_after(after, b->base, b_sub, b) ;
+
+ s_aux = ddt_aux(b_sub_list) ;
+
+ if (after != NULL)
+ vector_move_replace(aux, w+1, 0, s_aux, 0, m) ;
+ else
+ vector_move_replace(aux, w, 0, s_aux, 0, m) ;
+
+ ddl_init(b_sub) ;
+
+ ddt_verify_lists() ;
+ } ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_splice_before(before, base, sub, list)
+ * -- insert sublist before given item
+ *
+ * Cases: (a) sub is empty and list is empty
+ * (b) sub has 1 or more entries and list is empty (before = NULL).
+ * (c) sub includes head of list, but 1 or more entries remain
+ * (d) sub includes tail of list, but 1 or more entries remain
+ * (e) sub is part of list, and 1 or more entries remain at either
+ * end.
+ */
+ printf(" ddl_splice_before") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ printf(".") ;
+
+ int m ;
+
+ for (m = 0 ; m <= 5 ; ++m)
+ {
+ int w ;
+
+ for (w = 0 ; w <= n ; ++w)
+ {
+ ddt_item before ;
+ vector aux, s_aux ;
+
+ ddt_test_make_lists(n, m) ;
+
+ aux = ddt_aux(a_list) ;
+ before = aux_item(aux, w) ;
+
+ ddl_splice_before(before, a_base, a_sub, a) ;
+
+ s_aux = ddt_aux(a_sub_list) ;
+
+ if (before != NULL)
+ vector_move_replace(aux, w, 0, s_aux, 0, m) ;
+ else
+ vector_move_replace(aux, 0, 0, s_aux, 0, m) ;
+
+ ddl_init(a_sub) ;
+
+ ddt_verify_lists() ;
+
+ aux = ddt_aux(b_list) ;
+ before = aux_item(aux, w) ;
+
+ ddl_splice_before(before, b->base, b_sub, b) ;
+
+ s_aux = ddt_aux(b_sub_list) ;
+
+ if (before != NULL)
+ vector_move_replace(aux, w, 0, s_aux, 0, m) ;
+ else
+ vector_move_replace(aux, 0, 0, s_aux, 0, m) ;
+
+ ddl_init(b_sub) ;
+
+ ddt_verify_lists() ;
+ } ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+} ;
/*
* TODO
*
*/
+
+/*==============================================================================
+ * Auxilliary list handling -- so that can test one list structure against
+ * another.
+ *
+ * Implements list as vector of pointers.
+ */
+
+/*------------------------------------------------------------------------------
+ * Create new, empty aux list (vector)
+ */
+static vector
+aux_init(void)
+{
+ return vector_init_new(NULL, 50) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty the aux vector
+ */
+static void
+aux_reset(vector aux)
+{
+ vector_set_length(aux, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get number of items in the given aux list
+ */
+static int
+aux_length(vector aux)
+{
+ return vector_length(aux) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get item at the given index -- -ve counts from length
+ *
+ * Index must be -length..length
+ */
+static void*
+aux_item(vector aux, int index)
+{
+ int length ;
+
+ length = vector_length(aux) ;
+
+ if (index < 0)
+ index += length ;
+
+ if ((index >= 0) && (index < length))
+ return vector_get_item(aux, index) ;
+ else
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get item after item at the given index -- -ve counts from length
+ *
+ * Index must be -length..length
+ */
+static void*
+aux_next_item(vector aux, int index)
+{
+ int length ;
+
+ length = vector_length(aux) ;
+
+ if (index < 0)
+ index += length ;
+
+ if ((index >= 0) && (index < length))
+ return vector_get_item(aux, index + 1) ;
+ else
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get item before the item at the given index -- -ve counts from length
+ *
+ * Offset must be -length..length
+ */
+static void*
+aux_prev_item(vector aux, int index)
+{
+ int length ;
+
+ length = vector_length(aux) ;
+
+ if (index < 0)
+ index += length ;
+
+ if ((index > 0) && (index <= length))
+ return vector_get_item(aux, index - 1) ;
+ else
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert item at given offset in the aux list.
+ *
+ * Item(s) at and beyond the given offset (if any) move along the list.
+ *
+ * 0 => at start
+ * -1 => at end
+ *
+ * Offset must be -1..length
+ */
+static void
+aux_insert(vector aux, int index, void* item)
+{
+ if (index >= 0)
+ assert((uint)index <= vector_length(aux)) ;
+ else
+ index = vector_length(aux) ;
+
+ vector_insert_item(aux, index, item) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Find given item in the aux list and remove it.
+ *
+ * Shortens the list by 1 !
+ */
+static void
+aux_delete(vector aux, void* item)
+{
+ int index ;
+
+ index = aux_find(aux, item) ;
+
+ if (index < 0)
+ test_assert(index >= 0, "aux_delete: item not found !") ;
+ else
+ vector_delete_item(aux, index) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push item onto front of list.
+ */
+static void
+aux_push_head(vector aux, void* item)
+{
+ vector_unshift_item(aux, item) ; /* opposite sense to vector ! */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop item from front of list -- returns NULL if list is empty.
+ */
+static void*
+aux_pop_head(vector aux)
+{
+ return vector_shift_item(aux) ; /* opposite sense to vector ! */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append item to end of list.
+ */
+static void
+aux_push_tail(vector aux, void* item)
+{
+ vector_push_item(aux, item) ; /* opposite sense to vector ! */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove item from end of list -- returns NULL if list is empty.
+ */
+static void*
+aux_pop_tail(vector aux)
+{
+ return vector_pop_item(aux) ; /* opposite sense to vector ! */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Find index of item -- returns -1 if not found.
+ */
+static int
+aux_find(vector aux, void* item)
+{
+ int i, l ;
+
+ l = aux_length(aux) ;
+
+ for (i = 0 ; i < l ; ++i)
+ if (aux_item(aux, i) == item)
+ return i ;
+
+ return -1 ;
+} ;
+
+
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index e7afc49d..c6be8ea3 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -391,6 +391,7 @@ typedef config_item_list_t* config_item_list ;
typedef enum /* types of config_item */
{
it_dummy = 0,
+
it_node, /* Node is a list of line/group */
it_line,
it_group,
@@ -500,6 +501,7 @@ struct config_collection
config_fragment fragment_list ;
qstring_t temp ; /* used when creating fragments and names */
+ qstring_t comment ; /* used when creating comment names */
} ;
/*------------------------------------------------------------------------------
@@ -524,10 +526,11 @@ static void vtysh_config_parse_group_end(config_collection collection) ;
static void vtysh_config_node_find(config_collection collection) ;
static bool vtysh_config_item_insert(config_collection collection,
config_item_type_t it) ;
-static bool vtysh_config_try_merge(config_collection collection,
- config_item parent, config_name name) ;
-static config_name vtysh_config_get_name(config_collection collection,
- config_item parent, config_item_type_t type) ;
+static config_item vtysh_config_try_merge(config_collection collection,
+ config_item_list list) ;
+static config_name vtysh_config_get_name(config_collection collection) ;
+static config_name vtysh_config_get_comment_name(config_collection collection,
+ config_item comment) ;
static symbol_cmp_func vtysh_config_match_cmp ;
static symbol_free_func vtysh_config_match_free ;
@@ -573,6 +576,7 @@ vtysh_config_collection_new(void)
* lump_list -- NULLs -- empty
* fragment_list -- NULL -- empty
* temp -- empty qstring (embedded)
+ * comment -- empty qstring (embedded)
*/
confirm(NULL_NODE == 0) ;
confirm(ELSTRING_INIT_ALL_ZEROS) ;
@@ -936,7 +940,7 @@ vtysh_config_item_insert(config_collection collection, config_item_type_t it)
{
config_item parent ;
config_name name ;
- bool merged ;
+ config_item merged ;
bool co_opted ;
assert((it == it_line) || (it == it_group) || (it == it_dummy)) ;
@@ -997,8 +1001,8 @@ vtysh_config_item_insert(config_collection collection, config_item_type_t it)
ddl_append(parent->list, collection->last_item, siblings) ;
} ;
- /* We now have a new item, attached to the parent. For that item we have
- * the following set:
+ /* We now have a new collection->last_item, attached to its parent. For that
+ * item we have the following set:
*
* parent
* ordinal
@@ -1009,10 +1013,10 @@ vtysh_config_item_insert(config_collection collection, config_item_type_t it)
*
* And everything else is clear.
*
- * Now need to establish, and register, the name of the item.
+ * Now need to establish, and register, the name of the item -- based on the
+ * parsed stuff still sitting in the collection.
*/
- collection->last_item->name =
- name = vtysh_config_get_name(collection, parent, it) ;
+ name = vtysh_config_get_name(collection) ;
/* If the name is unique, then cannot merge.
*
@@ -1022,14 +1026,16 @@ vtysh_config_item_insert(config_collection collection, config_item_type_t it)
* If does not merge, we add the new item to the owners of the name.
*/
if (name->list == NULL)
- merged = false ;
+ merged = NULL ;
else
- merged = vtysh_config_try_merge(collection, parent, name) ;
+ merged = vtysh_config_try_merge(collection, &parent->list) ;
- if (!merged)
- ssl_append(name->list, collection->last_item, also) ;
+ if (merged == NULL)
+ ssl_push(name->list, collection->last_item, also) ;
+ else
+ collection->last_item = merged ;
- return merged ;
+ return merged != NULL ;
} ;
/*------------------------------------------------------------------------------
@@ -1205,11 +1211,14 @@ vtysh_config_node_find(config_collection collection)
* separator "following" it. This means that multiple blank lines at the start
* of a block of comments will be squashed together, and squashed with blank
* '!' and/or '#' comment lines.
+ *
+ * NB: does not name the comment item. That is done when the it_dummy item
+ * is merged with a real item.
*/
static void
vtysh_config_parse_comment(config_collection collection)
{
- config_item new_item, parent ;
+ config_item new_item, comment_parent ;
/* If the last item was it_comment, then need to create new item
* and attach it to the comment part of the parent
@@ -1217,29 +1226,112 @@ vtysh_config_parse_comment(config_collection collection)
* If the last item was not comment, then need to start a (pro tem) it_dummy
* item, to which the comment can be attached.
*/
- parent = collection->last_item ;
+ comment_parent = collection->last_item ;
- if (parent != NULL)
+ if ((comment_parent != NULL) && (comment_parent->type == it_comment))
{
- if (parent->type == it_comment)
- parent = parent->parent ;
+ comment_parent = comment_parent->parent ;
}
else
{
- vtysh_config_item_insert(collection, it_dummy) ;
+ if (comment_parent != NULL)
+ qassert(comment_parent->type != it_dummy) ;
- parent = collection->last_item ;
+ vtysh_config_item_insert(collection, it_dummy) ;
+ comment_parent = collection->last_item ;
} ;
- new_item = vtysh_config_item_new(collection, parent, it_comment) ;
+ new_item = vtysh_config_item_new(collection, comment_parent, it_comment) ;
*new_item->line = *collection->line ;
- ddl_append(parent->pre_comments, new_item, siblings) ;
+ ddl_append(comment_parent->pre_comments, new_item, siblings) ;
collection->last_item = new_item ;
} ;
/*------------------------------------------------------------------------------
+ * Merge one set of pre-comment lines with an existing one.
+ *
+ * This is used where items are being merged.
+ *
+ * If the target pre-comment lines are unnamed, names them. Then, name each
+ * source pre-comment line and merge it in.
+ */
+static void
+vtysh_config_merge_comment(config_collection collection,
+ config_item target, config_item source)
+{
+ config_item comment ;
+
+ /* Get the trivial cases out of the way.
+ */
+ comment = ddl_head(source->pre_comments) ;
+ if (comment == NULL)
+ return ;
+
+ qassert(comment->type == it_comment) ;
+
+ comment = ddl_head(target->pre_comments) ;
+ if (comment == NULL)
+ {
+ /* Move source pre-comments to target, and update parent of all of
+ * those.
+ */
+ target->pre_comments = source->pre_comments ;
+ ddl_init( source->pre_comments) ;
+
+ comment = ddl_head(target->pre_comments) ;
+ while (comment != NULL)
+ {
+ qassert(comment->parent == source) ;
+ qassert(comment->type == it_comment) ;
+
+ comment->parent = target ;
+
+ comment = ddl_next(comment, siblings) ;
+ } ;
+
+ return ;
+ } ;
+
+ qassert(comment->type == it_comment) ;
+
+ /* Name the target pre-comments, if required.
+ */
+ if (comment->name == NULL)
+ {
+ qassert(comment->parent == target) ;
+
+ comment->name = vtysh_config_get_comment_name(collection, comment) ;
+ comment = ddl_next(comment, siblings) ;
+ } ;
+
+ /* Now merge the source comments in
+ */
+ while (ddl_pop(&comment, source->pre_comments, siblings) != NULL)
+ {
+ config_name name ;
+ config_item merged ;
+
+ qassert(comment->parent == source) ;
+ qassert(comment->type == it_comment) ;
+ qassert(ddl_head(comment->pre_comments) == NULL) ;
+
+ comment->parent = target ;
+ name = vtysh_config_get_comment_name(collection, comment) ;
+ ddl_append(target->pre_comments, comment, siblings) ;
+
+ if (name->list != NULL)
+ merged = vtysh_config_try_merge(collection, &target->pre_comments) ;
+ else
+ merged = NULL ;
+
+ if (merged == NULL)
+ ssl_push(comment->name->list, comment, also) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
* We have a separator line.
*
* If we have a last_item, then we add a separator to it.
@@ -1380,87 +1472,182 @@ vtysh_config_parse_group_end(config_collection collection)
* Try to merge new item with an earlier one of the same name.
*
* The current last_item has been appended to the given parent, and has the
- * given name. At least one other item has the same name.
+ * given name. At least one other item has the same name, so we have one or
+ * more "targets" to attempt to merge with.
*
* No value has yet been set for the current last_item, but it may have
* pre_comments.
*
- * If we can move a "bubble" of items up the parent's list, such that the
- * current last_item overlaps (one of) the items with the same name, then
- * we do that and discard the duplicate node -- this is a "merge". Must also
- * merge any pre-comment(s).
+ * We can move items up the parent's list, provided we are moving past items
+ * which have no daemons in common with the items we are moving.
+ *
+ * We have two cases:
+ *
+ * 1) where the current last_item and one or more items above it have
+ * at least one daemon in common.
+ *
+ * In this case we have a "bubble" above the current last_item which we
+ * will need to move past the "target", if the current last_item is to
+ * be merged with the "target". This means that:
+ *
+ * a) all items between the current last_item and the target must
+ * have nothing in common with the bubble and nothing in
+ * common with the current last_item.
+ *
+ * For otherwise neither the bubble nor the current last_item can
+ * slide up past these items.
*
- * Each item with the same name is known as a "target".
+ * b) the target must have nothing in common with the bubble.
*
- * We can collect a "bubble" of items to move up, by scanning up from the
- * current last_item, while each item has at least the daemons in the bubble
- * so far (and adding an item adds all its daemons to the bubble).
+ * For otherwise the "bubble" cannot slide up past the target.
*
- * The bubble can move up to the target, provided that no item between the
- * bubble and the target has any items in common with the bubble.
+ * 2) where the current last_item has no daemons in common with its
+ * immediate predecessor.
+ *
+ * In this case we have an empty bubble.
+ *
+ * If we can move the (possibly empty) bubble and the current last_item up the
+ * list such that the current last_item overlaps (one of) the items with the
+ * same name, then we do that and discard the duplicate node -- this is a
+ * "merge". Must also merge any pre-comment(s).
+ *
+ * If there is a choice, will attempt to merge as far up the parent's list as
+ * possible.
+
+
+
*
* Note that since we do this as we add items to the collection, the bubble is
* always below the target.
*
*/
-static bool
-vtysh_config_try_merge(config_collection collection, config_item parent,
- config_name name)
+static config_item
+vtysh_config_try_merge(config_collection collection, config_item_list list)
{
- daemon_set_t bubble_daemons ;
- config_item item, bubble, seek, target ;
+ daemon_set_t bubble_daemons, total_daemons ;
+ config_item item, bubble, this, target ;
/* Set bubble to contain the current last_item
*/
- bubble = item = collection->last_item ;
- bubble_daemons = bubble->daemons ;
+ item = ddl_tail(*list) ;
+ total_daemons = item->daemons ;
+
+ bubble = item ; /* bubble == item <=> empty bubble */
+ bubble_daemons = 0 ;
- target = NULL ;
+ target = NULL ;
+
+ qassert(item->name->list != NULL) ;
/* Scan upwards adding to "bubble", looking out for a name match.
*
- * We can add an item to the bubble iff it has all the bubble's daemons, at
- * least.
+ * We can add an item to the bubble iff it has one or more daemons in common
+ * with the bubble or the item, and is NOT a target.
*/
while (1)
{
- bool addable ;
-
- seek = ddl_prev(bubble, siblings) ;
+ this = ddl_prev(bubble, siblings) ;
- if (seek == NULL)
- return false ; /* failed to find target */
+ if (this == NULL)
+ return NULL ; /* failed to find target */
- /* If we cannot add the current seek item to the bubble, we are
- * done with this phase.
+ /* Do not add completely disjoint item to the bubble.
*/
- if ((seek->daemons & bubble_daemons) != bubble_daemons)
+ if ((this->daemons & total_daemons) == 0)
break ;
- /* If we have a name match, then we are done.
+ /* Do not add a target item to the bubble.
*
- * If the bubble contains more than just the current last item,
- * then we cannot merge, because we cannot slide the rest of the
- * bubble past the target.
+ * If we find a target item, then we are either going to merge with it,
+ * or move past it.
*
- * If there is more than one target, then this one could not be merged
- * with the nearest one above, which implies that something above this
- * target prevented it and/or its bubble from sliding up.
+ * If there is more than one target item, then this one could not be
+ * merged with its predecessor, so something above prevented it from
+ * being moved. Adding this to the bubble would, therefore, be a waste
+ * of time.
*/
- if (seek->name == name)
+ if (this->name == item->name)
+ break ;
+
+ /* Add item to bubble.
+ */
+ bubble = this ;
+ bubble_daemons |= bubble->daemons ;
+
+ total_daemons |= bubble_daemons ;
+ } ;
+
+ /* Have constructed a (possibly empty) bubble, and we have "this" item above
+ * it.
+ *
+ * If we cannot move the bubble past this item, then we are done. If we have
+ * a target then note that. If we cannot move the bubble + the current
+ * last_item past this item, we are done.
+ */
+ while ((this->daemons & bubble_daemons) == 0)
+ {
+ /* If we have found a target, then make a note, and stop immediately
+ * if there are no further targets.
+ */
+ if (this->name == item->name)
{
- if (bubble != item)
- return false ;
+ /* Good news -- found target !
+ */
+ if (target != NULL)
+ qassert(this == target->also) ;
+ target = this ;
+
+ qassert(target->ordinal < item->ordinal) ;
+
+ /* If there is only one possible target, then we stop.
+ */
+ if (target->also == NULL)
+ break ;
} ;
- /* Add item to bubble.
+ /* If we cannot slide the bubble + current item past here, then we
+ * stop immediately.
+ */
+ if ((this->daemons & total_daemons) != 0)
+ break ;
+
+ /* Step up the list, stopping if run out.
*/
- bubble_daemons |= seek->daemons ;
- bubble = seek ;
+ this = ddl_prev(this, siblings) ;
+
+ if (this == NULL)
+ break ;
+ } ;
+
+ /* If we have found a target to merge with:
+ *
+ * - merging any pre-comments with the target pre-comments.
+ *
+ * - merge the daemons.
+ *
+ * - merge the post-xxxx
+ *
+ * - discard the item being merged.
+ *
+ * - move the bubble above the target.
+ */
+ if (target != NULL)
+ {
+ vtysh_config_merge_comment(collection, target, item) ;
+
+ target->daemons |= item->daemons ;
+
+ if (target->post_sep < item->post_sep)
+ target->post_sep = item->post_sep ;
+
+ ddl_del(*list, item, siblings) ;
+ vtysh_config_item_free(item) ;
+
+
} ;
- return false ;
+ return target ;
} ;
/*==============================================================================
@@ -1698,10 +1885,199 @@ vtysh_config_raw_free(config_collection collection)
} ;
qs_reset(collection->temp, keep_it) ; /* embedded */
+ qs_reset(collection->comment, keep_it) ; /* embedded */
+} ;
+
+/*==============================================================================
+ * Name handling.
+ *
+ * The name of an item covers:
+ *
+ * * the ordinal of the parent item
+ *
+ * * the type of item
+ *
+ * * the item's "raw_name", which may be:
+ *
+ * - the tokens which form the item, separated by spaces
+ *
+ * - the entire line (for comments) less trailing whitespace
+ *
+ * While there is a single, global symbol table, the form of the name means
+ * that each name has local scope within the parent.
+ */
+
+static config_name vtysh_config_make_name(config_collection collection,
+ config_item item, qstring raw_name) ;
+
+/*------------------------------------------------------------------------------
+ * Get and set name for the new collection->last_item (just parsed and created)
+ *
+ * Returns: address of name object.
+ */
+static config_name
+vtysh_config_get_name(config_collection collection)
+{
+ uint ti, tn ;
+
+ ti = (collection->last_item->type == it_group) ? 2 : 0 ;
+ tn = collection->parsed->num_tokens ;
+
+ return vtysh_config_make_name(collection, collection->last_item,
+ cmd_tokens_concat(collection->parsed, ti, tn - ti)) ;
} ;
+/*------------------------------------------------------------------------------
+ * Get name for a comment item.
+ *
+ * Comment items only need names when two items carrying pre-comments are
+ * merged.
+ *
+ * For comments the original line is used, verbatim, for the name -- less any
+ * trailing whitespace.
+ *
+ * Returns: address of name object.
+ */
+static config_name
+vtysh_config_get_comment_name(config_collection collection, config_item comment)
+{
+ qassert(comment->type == it_comment) ;
+
+ qs_set_els(collection->comment, comment->line) ;
+ qs_trim(collection->comment, '\0') ;
+
+ return vtysh_config_make_name(collection, comment, collection->comment) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a name and either look it up in the collection's global name table,
+ * or add new entry to that.
+ *
+ * Requires: item->parent
+ * item->type
+ * the given "raw name"
+ *
+ * Returns: address of name object -- set into item->name
+ *
+ * All items with the same name share the same name object. So, can
+ * test for name equality by comparing name object addresses.
+ *
+ * The items to which a name applies are hung off the name->list entry, in
+ * LIFO order. A NULL name->list entry implies this is a new name.
+ */
+static config_name
+vtysh_config_make_name(config_collection collection, config_item item,
+ qstring raw_name)
+{
+ config_name name ;
+ const char* tag ;
+ symbol sym ;
+
+ qassert(item->name == NULL) ; /* can only have one */
+
+ switch (item->type)
+ {
+ case it_line:
+ tag = "" ;
+ break ;
+
+ case it_group:
+ tag = "$" ;
+ break ;
+
+ case it_comment:
+ tag = "!" ;
+ break ;
+
+ default:
+ qassert(false) ;
+ tag = "?" ;
+ break ;
+ } ;
+
+ qs_clear(collection->temp) ;
+
+ qs_printf(collection->temp, "%u%s:%s", item->parent->ordinal, tag,
+ qs_string(raw_name)) ;
+
+ sym = symbol_lookup(collection->match, qs_string(collection->temp), add) ;
+
+ name = symbol_get_body(sym) ;
+
+ if (name == NULL)
+ {
+ ulen len ;
+
+ len = qs_len(collection->temp) + 1 ; /* including terminator */
+
+ name = vtysh_config_fragment_new(collection,
+ offsetof(config_name_t, string[len]), true /* align */) ;
+
+ name->list = NULL ;
+ memcpy(name->string, qs_char(collection->temp), len) ;
+
+ symbol_set_body(sym, name, true /* set */, free_it /* previous ! */) ;
+ } ;
+
+ return item->name = name ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare name in the given config_name with the given name_string.
+ */
+static int
+vtysh_config_match_cmp(const void* body, const void* name_string)
+{
+ const config_name_t* name = body ;
+ return strcmp(name->string, name_string) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The symbol bodies are in fragments and are freed automatically.
+ */
+static void
+vtysh_config_match_free(void* body)
+{
+} ;
+
+/*==============================================================================
+ * The vtysh own configuration.
+ *
+ * There is not much of this.
+ *
+ * We don't write vtysh specific into file from vtysh. vtysh.conf should
+ * be edited by hand.
+ */
+
+/*------------------------------------------------------------------------------
+ * Show vtysh own configuration -- same like client daemon #vtysh-config-write
+ *
+ * This is for collecting the integrated configuration.
+ */
+static void
+vtysh_config_own_config(vty vtysh)
+{
+ vty_out(vtysh, "#vtysh-config-daemon vtysh\n") ;
+ vty_out(vtysh, "#vtysh-config-node %s\n", cmd_node_name(CONFIG_NODE)) ;
+
+ if (host.name_set)
+ vty_out(vtysh, "hostname %s\n", host.name) ;
+
+ if (vtysh_integrated_vtysh_config)
+ vty_out(vtysh, "service integrated-vtysh-config\n") ;
+} ;
+
+/*==============================================================================
+ * For diagnostic purposes -- "show" functions for data structures.
+ */
+
extern void show_collected(config_collection collection) ;
+static void show_collected_item(config_item item, qstring line) ;
+
+/*------------------------------------------------------------------------------
+ * Output given collection to stderr
+ */
extern void
show_collected(config_collection collection)
{
@@ -1726,8 +2102,9 @@ show_collected(config_collection collection)
fprintf(stderr, "%d lumps\n", i) ;
} ;
-static void show_collected_item(config_item item, qstring line) ;
-
+/*------------------------------------------------------------------------------
+ * Output given node to stderr
+ */
extern void
show_collected_node(config_collection collection)
{
@@ -1768,6 +2145,9 @@ show_collected_node(config_collection collection)
qs_free(line) ;
} ;
+/*------------------------------------------------------------------------------
+ * Output given item to stderr
+ */
static void
show_collected_item(config_item item, qstring line)
{
@@ -1820,135 +2200,3 @@ show_collected_item(config_item item, qstring line)
} ;
} ;
-/*==============================================================================
- * Name handling.
- *
- * When a new item is about to be inserted in the tree, need to get its name,
- * so we can check if we have the same item already or later.
- *
- * If the config_name object has a NULL items entry, then this is the first
- * occurrence of this name.
- *
- * The name covers:
- *
- * * the ordinal of the parent item
- *
- * * the type of item
- *
- * * the tokens which form the item, separated by spaces
- *
- * While there is a single, global symbol table, the form of the name means
- * that each name has local scope within the parent.
- *
- * Returns: address of name object.
- *
- * All items with the same name share the same name object. So, can
- * test for name equality by comparing name object addresses.
- */
-static config_name
-vtysh_config_get_name(config_collection collection, config_item parent,
- config_item_type_t type)
-{
- qstring tokens ;
- config_name name ;
- const char* tag ;
- uint ti, tn ;
- symbol sym ;
-
- ti = 0 ;
- tn = collection->parsed->num_tokens ;
-
- switch (type)
- {
- case it_line:
- tag = "" ;
- break ;
-
- case it_group:
- tag = "$" ;
- ti = 2 ;
- break ;
-
- case it_comment:
- tag = "!" ;
- break ;
-
- default:
- qassert(false) ;
- tag = "?" ;
- break ;
- } ;
-
- tokens = cmd_tokens_concat(collection->parsed, ti, tn - ti) ;
-
- qs_clear(collection->temp) ;
-
- qs_printf(collection->temp, "%u%s:%s", parent->ordinal, tag, qs_string(tokens)) ;
-
- sym = symbol_lookup(collection->match, qs_string(collection->temp), add) ;
-
- name = symbol_get_body(sym) ;
-
- if (name == NULL)
- {
- ulen len ;
-
- len = qs_len(collection->temp) + 1 ; /* including terminator */
-
- name = vtysh_config_fragment_new(collection,
- offsetof(config_name_t, string[len]), true /* align */) ;
-
- name->list = NULL ;
- memcpy(name->string, qs_char(collection->temp), len) ;
-
- symbol_set_body(sym, name, true /* set */, free_it /* previous ! */) ;
- } ;
-
- return name ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Compare name in the given config_name with the given name_string.
- */
-static int
-vtysh_config_match_cmp(const void* body, const void* name_string)
-{
- const config_name_t* name = body ;
- return strcmp(name->string, name_string) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * The symbol bodies are in fragments and are freed automatically.
- */
-static void
-vtysh_config_match_free(void* body)
-{
-} ;
-
-/*==============================================================================
- * The vtysh own configuration.
- *
- * There is not much of this.
- *
- * We don't write vtysh specific into file from vtysh. vtysh.conf should
- * be edited by hand.
- */
-
-/*------------------------------------------------------------------------------
- * Show vtysh own configuration -- same like client daemon #vtysh-config-write
- *
- * This is for collecting the integrated configuration.
- */
-static void
-vtysh_config_own_config(vty vtysh)
-{
- vty_out(vtysh, "#vtysh-config-daemon vtysh\n") ;
- vty_out(vtysh, "#vtysh-config-node %s\n", cmd_node_name(CONFIG_NODE)) ;
-
- if (host.name_set)
- vty_out(vtysh, "hostname %s\n", host.name) ;
-
- if (vtysh_integrated_vtysh_config)
- vty_out(vtysh, "service integrated-vtysh-config\n") ;
-} ;
-