summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp.h2
-rw-r--r--bgpd/bgp_advertise.h4
-rw-r--r--bgpd/bgp_aspath.h5
-rw-r--r--bgpd/bgp_clist.c8
-rw-r--r--bgpd/bgp_common.h7
-rw-r--r--bgpd/bgp_connection.h6
-rw-r--r--bgpd/bgp_engine.h6
-rw-r--r--bgpd/bgp_msg_write.h3
-rw-r--r--bgpd/bgp_notification.h6
-rw-r--r--bgpd/bgp_open_state.c18
-rw-r--r--bgpd/bgp_open_state.h10
-rw-r--r--bgpd/bgp_packet.c10
-rw-r--r--bgpd/bgp_peer_index.c22
-rw-r--r--bgpd/bgp_route_refresh.c6
-rw-r--r--bgpd/bgp_route_refresh.h12
-rw-r--r--bgpd/bgp_session.h6
-rw-r--r--bgpd/bgp_vty.c23
-rw-r--r--lib/Makefile.am13
-rw-r--r--lib/command.c1767
-rw-r--r--lib/command.h74
-rw-r--r--lib/command_execute.h10
-rw-r--r--lib/command_parse.c903
-rw-r--r--lib/command_parse.h478
-rw-r--r--lib/elstring.h354
-rw-r--r--lib/heap.c89
-rw-r--r--lib/heap.h45
-rw-r--r--lib/keystroke.h8
-rw-r--r--lib/list_util.c3
-rw-r--r--lib/list_util.h9
-rw-r--r--lib/log.h1
-rw-r--r--lib/memory.h2
-rw-r--r--lib/memtypes.c3
-rw-r--r--lib/misc.h80
-rw-r--r--lib/miyagi.h4
-rw-r--r--lib/mqueue.h7
-rw-r--r--lib/node_type.h3
-rw-r--r--lib/plist.c54
-rw-r--r--lib/plist.h2
-rw-r--r--lib/prefix.c47
-rw-r--r--lib/prefix.h5
-rw-r--r--lib/qafi_safi.h2
-rw-r--r--lib/qfstring.c4
-rw-r--r--lib/qfstring.h37
-rw-r--r--lib/qiovec.h8
-rw-r--r--lib/qpath.c819
-rw-r--r--lib/qpath.h124
-rw-r--r--lib/qpnexus.h6
-rw-r--r--lib/qpselect.c14
-rw-r--r--lib/qpselect.h5
-rw-r--r--lib/qpthreads.h19
-rw-r--r--lib/qstring.c757
-rw-r--r--lib/qstring.h529
-rw-r--r--lib/qtime.h6
-rw-r--r--lib/qtimers.c11
-rw-r--r--lib/qtimers.h8
-rw-r--r--lib/routemap.h2
-rw-r--r--lib/symtab.c69
-rw-r--r--lib/symtab.h83
-rw-r--r--lib/temp.c1349
-rw-r--r--lib/tstring.h139
-rw-r--r--lib/uty.h33
-rw-r--r--lib/vargs.h34
-rw-r--r--lib/vector.c476
-rw-r--r--lib/vector.h157
-rw-r--r--lib/vio_fifo.c9
-rw-r--r--lib/vio_fifo.h11
-rw-r--r--lib/vio_lines.c6
-rw-r--r--lib/vio_lines.h9
-rw-r--r--lib/vty.c219
-rw-r--r--lib/vty.h94
-rw-r--r--lib/vty_cli.c75
-rw-r--r--lib/vty_io.c2622
-rw-r--r--lib/vty_io.h270
-rw-r--r--lib/vty_io_basic.c1063
-rw-r--r--lib/vty_io_basic.h186
-rw-r--r--lib/vty_io_file.c2608
-rw-r--r--lib/vty_io_file.h56
-rw-r--r--lib/vty_io_shell.c366
-rw-r--r--lib/vty_io_shell.h55
-rw-r--r--lib/vty_io_term.c1288
-rw-r--r--lib/vty_io_term.h64
-rw-r--r--lib/vty_pipe.c2867
-rw-r--r--lib/workqueue.h4
-rw-r--r--lib/zassert.h2
-rw-r--r--tests/test-list_util.c4
-rw-r--r--tests/test-symtab.c23
-rw-r--r--tests/test-vector.c88
-rw-r--r--vtysh/vtysh.c72
-rw-r--r--vtysh/vtysh.h4
-rw-r--r--vtysh/vtysh_config.c22
-rw-r--r--vtysh/vtysh_main.c15
-rw-r--r--vtysh/vtysh_user.c25
-rw-r--r--vtysh/vtysh_user.h3
93 files changed, 15968 insertions, 4968 deletions
diff --git a/bgpd/bgp.h b/bgpd/bgp.h
index 6019b33c..94ce2993 100644
--- a/bgpd/bgp.h
+++ b/bgpd/bgp.h
@@ -30,7 +30,7 @@
#define _GMCH_BGP_H "19-Dec-2009"
-#include <stdint.h>
+#include "misc.h"
#include "confirm.h"
/*##############################################################################
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
index ca92238a..afa812f6 100644
--- a/bgpd/bgp_advertise.h
+++ b/bgpd/bgp_advertise.h
@@ -21,9 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ADVERTISE_H
#define _QUAGGA_BGP_ADVERTISE_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "lib/misc.h"
/* BGP advertise FIFO. */
typedef struct bgp_advertise* bgp_advertise ;
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
index 895379aa..c02a84aa 100644
--- a/bgpd/bgp_aspath.h
+++ b/bgpd/bgp_aspath.h
@@ -21,10 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _QUAGGA_BGP_ASPATH_H
#define _QUAGGA_BGP_ASPATH_H
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "lib/misc.h"
/* AS path segment type. */
#define AS_SET 1
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 1d847ee4..0df34bb0 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -127,7 +127,7 @@ community_list_lookup (struct community_list_handler *ch,
if (!table)
return NULL;
- return symbol_get_value(symbol_seek(table, name)) ;
+ return symbol_get_value(symbol_lookup(table, name, no_add)) ;
}
static struct community_list *
@@ -145,7 +145,7 @@ community_list_get (struct community_list_handler *ch,
if (!table)
return NULL;
- sym = symbol_find(table, name) ;
+ sym = symbol_lookup(table, name, add) ;
list = symbol_get_value(sym) ;
if (!list)
{
@@ -768,10 +768,10 @@ community_list_terminate (struct community_list_handler *ch)
{
struct community_list *list ;
- while ((list = symbol_table_ream_keep(&ch->community_list)))
+ while ((list = symbol_table_ream(&ch->community_list, keep_it)))
community_list_delete(list) ;
- while ((list = symbol_table_ream_keep(&ch->extcommunity_list)))
+ while ((list = symbol_table_ream(&ch->extcommunity_list, keep_it)))
community_list_delete(list) ;
XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch);
diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h
index 0d2395ed..fb695125 100644
--- a/bgpd/bgp_common.h
+++ b/bgpd/bgp_common.h
@@ -22,18 +22,13 @@
#ifndef _QUAGGA_BGP_COMMON_H
#define _QUAGGA_BGP_COMMON_H
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include <sys/socket.h>
#include "bgpd/bgp.h"
#include "qafi_safi.h"
#include "lib/zassert.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Here are a number of "incomplete" declarations, which allow a number of
* bgpd structures to refer to each other.
diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h
index 79270bd8..8c5e00d4 100644
--- a/bgpd/bgp_connection.h
+++ b/bgpd/bgp_connection.h
@@ -22,7 +22,7 @@
#ifndef _QUAGGA_BGP_CONNECTION_H
#define _QUAGGA_BGP_CONNECTION_H
-#include <stdbool.h>
+#include "lib/misc.h"
#include "lib/mqueue.h"
#include "lib/qpthreads.h"
@@ -40,10 +40,6 @@
#include "bgpd/bgp_notification.h"
#include "bgpd/bgp_msg_read.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* The BGP Finite State Machine: states and events
*
diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h
index ceec1b2f..7e3cceec 100644
--- a/bgpd/bgp_engine.h
+++ b/bgpd/bgp_engine.h
@@ -22,16 +22,14 @@
#ifndef _QUAGGA_BGP_ENGINE_H
#define _QUAGGA_BGP_ENGINE_H
+#include "lib/misc.h"
+
#include "bgpd/bgpd.h"
#include "lib/mqueue.h"
#include "lib/qpnexus.h"
#include "lib/log.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
enum { qdebug =
#ifdef QDEBUG
1
diff --git a/bgpd/bgp_msg_write.h b/bgpd/bgp_msg_write.h
index 77bfc1f2..8867e06f 100644
--- a/bgpd/bgp_msg_write.h
+++ b/bgpd/bgp_msg_write.h
@@ -24,8 +24,7 @@
#ifndef _QUAGGA_BGP_MSG_WRITE_H
#define _QUAGGA_BGP_MSG_WRITE_H
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include "bgpd/bgp_common.h"
#include "bgpd/bgp_connection.h"
diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h
index 20e96063..755f431e 100644
--- a/bgpd/bgp_notification.h
+++ b/bgpd/bgp_notification.h
@@ -24,13 +24,9 @@
#ifndef _QUAGGA_BGP_NOTIFY_H
#define _QUAGGA_BGP_NOTIFY_H
-#include <stddef.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP NOTIFICATION message codes.
*/
diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c
index 9ca9617e..e1dbaeaf 100644
--- a/bgpd/bgp_open_state.c
+++ b/bgpd/bgp_open_state.c
@@ -48,7 +48,7 @@ bgp_open_state_init_new(bgp_open_state state)
else
memset(state, 0, sizeof(struct bgp_open_state)) ;
- vector_init_new(&state->unknowns, 0) ;
+ vector_init_new(state->unknowns, 0) ;
return state ;
}
@@ -66,10 +66,10 @@ bgp_open_state_free(bgp_open_state state)
if (state != NULL)
{
- while ((unknown = vector_ream_keep(&state->unknowns)) != NULL)
+ while ((unknown = vector_ream(state->unknowns, keep_it)) != NULL)
XFREE(MTYPE_TMP, unknown) ;
- while ((afi_safi = vector_ream_keep(&state->afi_safi)) != NULL)
+ while ((afi_safi = vector_ream(state->afi_safi, keep_it)) != NULL)
XFREE(MTYPE_TMP, afi_safi) ;
XFREE(MTYPE_BGP_OPEN_STATE, state) ;
@@ -222,7 +222,7 @@ bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
if (length != 0)
memcpy(unknown->value, value, length) ;
- vector_push_item(&state->unknowns, unknown) ;
+ vector_push_item(state->unknowns, unknown) ;
} ;
/*------------------------------------------------------------------------------
@@ -231,7 +231,7 @@ bgp_open_state_unknown_add(bgp_open_state state, uint8_t code,
extern int
bgp_open_state_unknown_count(bgp_open_state state)
{
- return vector_end(&state->unknowns) ;
+ return vector_end(state->unknowns) ;
} ;
/*------------------------------------------------------------------------------
@@ -240,7 +240,7 @@ bgp_open_state_unknown_count(bgp_open_state state)
extern bgp_cap_unknown
bgp_open_state_unknown_cap(bgp_open_state state, unsigned index)
{
- return vector_get_item(&state->unknowns, index) ;
+ return vector_get_item(state->unknowns, index) ;
} ;
/*==============================================================================
@@ -264,7 +264,7 @@ bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
afi_safi->safi = safi ;
afi_safi->cap_code = cap_code ;
- vector_push_item(&state->afi_safi, afi_safi) ;
+ vector_push_item(state->afi_safi, afi_safi) ;
return afi_safi ;
} ;
@@ -275,7 +275,7 @@ bgp_open_state_afi_safi_add(bgp_open_state state, iAFI_t afi, iSAFI_t safi,
extern int
bgp_open_state_afi_safi_count(bgp_open_state state)
{
- return vector_end(&state->afi_safi) ;
+ return vector_end(state->afi_safi) ;
} ;
/*------------------------------------------------------------------------------
@@ -284,7 +284,7 @@ bgp_open_state_afi_safi_count(bgp_open_state state)
extern bgp_cap_afi_safi
bgp_open_state_afi_safi_cap(bgp_open_state state, unsigned index)
{
- return vector_get_item(&state->afi_safi, index) ;
+ return vector_get_item(state->afi_safi, index) ;
} ;
/*==============================================================================
diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h
index 8c30712b..e88deda0 100644
--- a/bgpd/bgp_open_state.h
+++ b/bgpd/bgp_open_state.h
@@ -22,16 +22,12 @@
#ifndef _QUAGGA_BGP_OPEN_STATE_H
#define _QUAGGA_BGP_OPEN_STATE_H
-#include <stdint.h>
+#include "misc.h"
#include "bgpd/bgp.h"
#include "bgpd/bgp_common.h"
#include "lib/vector.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP Open State.
*
@@ -114,8 +110,8 @@ struct bgp_open_state
bool has_restarted ; /* Restart State flag */
unsigned restart_time ; /* Restart Time in seconds */
- struct vector unknowns ; /* list of bgp_cap_unknown */
- struct vector afi_safi ; /* various afi/safi capabilities */
+ vector_t unknowns ; /* list of bgp_cap_unknown */
+ vector_t afi_safi ; /* various afi/safi capabilities */
} ;
/*==============================================================================
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 62d76c60..9a5aaf30 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -776,7 +776,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi,
bgp_orf_entry orfpe = NULL;
struct prefix_list *plist = NULL;
struct orf_prefix orfp;
- vector_index i;
+ vector_index_t i;
int orf_refresh = 0;
enum prefix_list_type pe_type;
@@ -1837,7 +1837,7 @@ bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr)
{
afi_t afi;
safi_t safi;
- vector_index i;
+ vector_index_t i, e;
char name[BUFSIZ];
int ret;
@@ -1852,11 +1852,11 @@ bgp_route_refresh_recv(bgp_peer peer, bgp_route_refresh rr)
ret = snprintf (name, BUFSIZ, "%s.%d.%d", peer->host, afi, safi);
assert(ret < BUFSIZ);
- if (rr->entries.end > 0)
+ if ((e = bgp_orf_get_count(rr)) > 0)
{
- for (i = 0; i < rr->entries.end; ++i)
+ for (i = 0; i < e; ++i)
{
- bgp_orf_entry orfep = vector_slot(&rr->entries, i);
+ bgp_orf_entry orfep = vector_slot(rr->entries, i);
/* ignore unknown */
if (orfep->unknown)
diff --git a/bgpd/bgp_peer_index.c b/bgpd/bgp_peer_index.c
index 518a22bc..10d53f62 100644
--- a/bgpd/bgp_peer_index.c
+++ b/bgpd/bgp_peer_index.c
@@ -59,7 +59,7 @@
*/
static struct symbol_table bgp_peer_index ; /* lookup by 'name' */
-static struct vector bgp_peer_id_index ; /* lookup by peer-id */
+static vector_t bgp_peer_id_index ; /* lookup by peer-id */
static qpt_mutex bgp_peer_index_mutex = NULL ;
@@ -112,7 +112,7 @@ bgp_peer_index_init(void* parent)
sockunion_symbol_hash, /* "name" is an IP Address */
NULL) ; /* no value change call-back */
- vector_init_new(&bgp_peer_id_index, bgp_peer_id_unit) ;
+ vector_init_new(bgp_peer_id_index, bgp_peer_id_unit) ;
/* Initialise table entirely empty */
bgp_peer_id_table = NULL ;
@@ -149,11 +149,11 @@ bgp_peer_index_reset(void)
bgp_peer_id_table_chunk chunk ;
/* Ream out the peer id vector -- checking that all entries are empty */
- while ((entry = vector_ream_keep(&bgp_peer_id_index)) != NULL)
+ while ((entry = vector_ream(bgp_peer_id_index, keep_it)) != NULL)
passert((entry->peer == NULL) && (entry->next_free != entry)) ;
/* Discard body of symbol table -- must be empty ! */
- symbol_table_reset_keep(&bgp_peer_index) ;
+ symbol_table_reset(&bgp_peer_index, keep_it) ;
/* Discard the empty chunks of entries */
while (bgp_peer_id_table != NULL)
@@ -204,7 +204,7 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su)
entry = bgp_peer_id_free_head ;
bgp_peer_id_free_head = entry->next_free ;
- assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+ assert(vector_get_item(bgp_peer_id_index, entry->id) == entry) ;
/* Initialise the entry -- the id is already set */
entry->peer = peer ;
@@ -213,7 +213,7 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su)
peer->index_entry = entry;
/* Insert the new entry into the symbol table. */
- entry = symbol_set_value(symbol_find(&bgp_peer_index, su), entry) ;
+ entry = symbol_set_value(symbol_lookup(&bgp_peer_index, su, add), entry) ;
BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
@@ -241,7 +241,7 @@ bgp_peer_index_deregister(bgp_peer peer, union sockunion* su)
BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- sym = symbol_seek(&bgp_peer_index, su) ;
+ sym = symbol_lookup(&bgp_peer_index, su, no_add) ;
passert(sym != NULL) ;
entry = symbol_delete(sym) ;
@@ -288,7 +288,7 @@ bgp_peer_index_seek_entry(union sockunion* su)
/* Only the Routing Engine can add/delete entries -- so no lock required */
- entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+ entry = symbol_get_value(symbol_lookup(&bgp_peer_index, su, no_add)) ;
if (entry != NULL)
assert((entry->peer != NULL) && (entry->next_free = entry)) ;
@@ -340,7 +340,7 @@ bgp_peer_index_seek_accept(union sockunion* su, bool* p_found)
BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ;
+ entry = symbol_get_value(symbol_lookup(&bgp_peer_index, su, no_add)) ;
if (entry != NULL)
{
@@ -375,7 +375,7 @@ static void
bgp_peer_id_table_free_entry(bgp_peer_index_entry entry)
{
assert((entry != NULL) && (entry->id < bgp_peer_id_count)) ;
- assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ;
+ assert(vector_get_item(bgp_peer_id_index, entry->id) == entry) ;
if (bgp_peer_id_free_head == NULL)
bgp_peer_id_free_head = entry ;
@@ -424,7 +424,7 @@ bgp_peer_id_table_make_ids(void)
while (id_new < bgp_peer_id_count)
{
- vector_set_item(&bgp_peer_id_index, id_new, entry) ;
+ vector_set_item(bgp_peer_id_index, id_new, entry) ;
entry->id = id_new ;
bgp_peer_id_table_free_entry(entry) ;
diff --git a/bgpd/bgp_route_refresh.c b/bgpd/bgp_route_refresh.c
index 5c2b6e5c..7a8d5df5 100644
--- a/bgpd/bgp_route_refresh.c
+++ b/bgpd/bgp_route_refresh.c
@@ -60,7 +60,7 @@ bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count)
rr->afi = afi ;
rr->safi = safi ;
- vector_init_new(&rr->entries, count) ;
+ vector_init_new(rr->entries, count) ;
/* rest of bgp_route_refresh zeroised -- not relevant when vector empty */
@@ -74,7 +74,7 @@ extern void
bgp_route_refresh_free(bgp_route_refresh rr)
{
bgp_orf_entry entry ;
- while((entry = vector_ream_keep(&rr->entries)) != NULL)
+ while((entry = vector_ream(rr->entries, keep_it)) != NULL)
XFREE(MTYPE_BGP_ORF_ENTRY, entry) ;
XFREE(MTYPE_BGP_ROUTE_REFRESH, rr) ;
@@ -130,7 +130,7 @@ bgp_orf_entry_new(bgp_route_refresh rr, uint8_t orf_type, bgp_form_t form,
orfe->form = form ;
orfe->unknown = (unknown_size != 0) ;
- vector_push_item(&rr->entries, orfe) ;
+ vector_push_item(rr->entries, orfe) ;
return orfe ;
} ;
diff --git a/bgpd/bgp_route_refresh.h b/bgpd/bgp_route_refresh.h
index b44ca9e0..78324ff4 100644
--- a/bgpd/bgp_route_refresh.h
+++ b/bgpd/bgp_route_refresh.h
@@ -22,16 +22,12 @@
#ifndef _QUAGGA_BGP_ROUTE_REFRESH_H
#define _QUAGGA_BGP_ROUTE_REFRESH_H
-#include <stddef.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
#include "lib/prefix.h"
#include "lib/plist.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Structures to hold ROUTE-REFRESH and ORF
*/
@@ -81,7 +77,7 @@ struct bgp_route_refresh
iAFI_t afi ; /* NB: Internet AFI/SAFI */
iSAFI_t safi ;
- struct vector entries ; /* empty => simple ROUTE-REFRESH */
+ vector_t entries ; /* empty => simple ROUTE-REFRESH */
bool defer ; /* otherwise: immediate */
@@ -120,13 +116,13 @@ bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length,
Inline unsigned
bgp_orf_get_count(bgp_route_refresh rr)
{
- return vector_end(&rr->entries) ;
+ return vector_end(rr->entries) ;
} ;
Inline bgp_orf_entry
bgp_orf_get_entry(bgp_route_refresh rr, unsigned index)
{
- return vector_get_item(&rr->entries, index) ;
+ return vector_get_item(rr->entries, index) ;
} ;
#endif /* _QUAGGA_BGP_ROUTE_REFRESH_H */
diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h
index ccc7a28e..e1a4c51b 100644
--- a/bgpd/bgp_session.h
+++ b/bgpd/bgp_session.h
@@ -22,8 +22,8 @@
#ifndef _QUAGGA_BGP_SESSION_H
#define _QUAGGA_BGP_SESSION_H
-#include <stdbool.h>
#include <zebra.h>
+#include "lib/misc.h"
#include "bgpd/bgp_common.h"
#include "bgpd/bgp_engine.h"
@@ -37,10 +37,6 @@
#include "lib/sockunion.h"
#include "lib/mqueue.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* BGP Session data structure.
*
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 222e4485..b8af6140 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -10365,8 +10365,9 @@ DEFUN (show_ip_community_list,
{
struct symbol_table* table;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
+ struct community_list *list;
table = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER);
if (table == NULL)
@@ -10375,7 +10376,11 @@ DEFUN (show_ip_community_list,
extract = symbol_table_extract(table, NULL, NULL, 0, symbol_mixed_name_cmp) ;
for (VECTOR_ITEMS(extract, sym, i))
- community_list_show (vty, symbol_get_value(sym));
+ {
+ list = symbol_get_value(sym) ;
+ if (list != NULL)
+ community_list_show (vty, list) ;
+ }
vector_free(extract) ; /* discard temporary vector */
@@ -10719,7 +10724,7 @@ DEFUN (show_ip_extcommunity_list,
{
struct symbol_table* table;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
table = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER);
@@ -10729,7 +10734,11 @@ DEFUN (show_ip_extcommunity_list,
extract = symbol_table_extract(table, NULL, NULL, 0, symbol_mixed_name_cmp) ;
for (VECTOR_ITEMS(extract, sym, i))
- extcommunity_list_show (vty, symbol_get_value(sym));
+ {
+ list = symbol_get_value(sym) ;
+ if (list != NULL)
+ extcommunity_list_show (vty, list) ;
+ }
vector_free(extract) ; /* discard temporary vector */
@@ -10785,7 +10794,7 @@ community_list_config_write_list(struct vty* vty, int what)
struct community_list *list;
struct community_entry *entry;
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
int write = 0;
@@ -10796,6 +10805,10 @@ community_list_config_write_list(struct vty* vty, int what)
for (VECTOR_ITEMS(extract, sym, i))
{
list = symbol_get_value(sym) ;
+
+ if (list == NULL)
+ continue ;
+
for (entry = list->head; entry; entry = entry->next)
{
const char* list_type = "" ;
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e88c5998..39b6b03e 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,8 +14,9 @@ libzebra_la_SOURCES = \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c qpnexus.c \
- command_queue.c qlib_init.c pthread_safe.c list_util.c \
- vty_io.c vty_cli.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
+ command_parse.c command_queue.c qlib_init.c pthread_safe.c list_util.c \
+ vty_io.c vty_io_file.c vty_io_shell.c vty_io_term.c vty_cli.c \
+ vty_io_basic.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
qiovec.c qfstring.c errno_names.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -33,9 +34,11 @@ pkginclude_HEADERS = \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
workqueue.h route_types.h symtab.h heap.h \
qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \
- command_queue.h qlib_init.h qafi_safi.h \
- confirm.h miyagi.h pthread_safe.h list_util.h node_type.h uty.h \
- vty_io.h vty_cli.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
+ command_parse.h command_queue.h qlib_init.h qafi_safi.h \
+ confirm.h misc.h vargs.h miyagi.h pthread_safe.h list_util.h \
+ tstring.h node_type.h uty.h \
+ vty_io.h vty_io_file.h vty_io_shell.h vty_io_term.h vty_cli.h \
+ vty_io_basic.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
qiovec.h qfstring.h errno_names.h \
route_types.h command_execute.h
diff --git a/lib/command.c b/lib/command.c
index a78ac05c..3c4f056a 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -36,6 +36,7 @@ Boston, MA 02111-1307, USA. */
#include "command_execute.h"
#include "workqueue.h"
#include "command_queue.h"
+#include "command_parse.h"
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -47,10 +48,13 @@ char *command_cr = NULL;
/* Host information structure. */
struct host host;
+/* Store of qstrings, used for parsing. */
+token_vector_t spare_token_strings ;
+
/* Standard command node structures. */
static struct cmd_node auth_node =
{
- AUTH_NODE,
+ .node = AUTH_NODE,
"Password: ",
};
@@ -104,16 +108,16 @@ static const struct facility_map {
size_t match;
} syslog_facilities[] =
{
- { LOG_KERN, "kern", 1 },
- { LOG_USER, "user", 2 },
- { LOG_MAIL, "mail", 1 },
+ { LOG_KERN, "kern", 1 },
+ { LOG_USER, "user", 2 },
+ { LOG_MAIL, "mail", 1 },
{ LOG_DAEMON, "daemon", 1 },
- { LOG_AUTH, "auth", 1 },
+ { LOG_AUTH, "auth", 1 },
{ LOG_SYSLOG, "syslog", 1 },
- { LOG_LPR, "lpr", 2 },
- { LOG_NEWS, "news", 1 },
- { LOG_UUCP, "uucp", 2 },
- { LOG_CRON, "cron", 1 },
+ { LOG_LPR, "lpr", 2 },
+ { LOG_NEWS, "news", 1 },
+ { LOG_UUCP, "uucp", 2 },
+ { LOG_CRON, "cron", 1 },
#ifdef LOG_FTP
{ LOG_FTP, "ftp", 1 },
#endif
@@ -128,6 +132,20 @@ static const struct facility_map {
{ 0, NULL, 0 },
};
+static struct cmd_element cmd_pipe =
+{
+ .string = "< or <|", /* Dummy */
+ .func = cmd_pipe_func,
+ .doc = "Pipe input to command processor",
+ .daemon = 0,
+ .strvec = NULL,
+ .cmdsize = 0,
+ .config = NULL,
+ .subconfig = NULL,
+ .attr = CMD_ATTR_SIMPLE,
+} ;
+
+
static const char *
facility_name(int facility)
{
@@ -284,144 +302,16 @@ sort_node ()
} ;
/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
- *
- * Discards leading and trailing white-space.
- *
- * Treats lines that start with '!' or '#' (after any leading white-space)
- * as empty -- these are comment lines.
- *
- * Tokens are non-whitespace separated by one or more white-space.
- *
- * White-space is anything that isspace() thinks is a space. (Which in the
- * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ * Take string and break it into tokens -- see cmd_make_tokens().
*
* Returns: NULL => empty line (after white-space trimming) or comment line.
- * otherwise: is vector containing one or more tokens.
- *
- * ....
- *
- *
- * Note: all the tokens in the vector have at least one character, and no
- * entries are NULL.
- *
- * NB: it is the caller's responsibility to release the vector and its contents,
- * see cmd_free_strvec().
- */
-static vector
-cmd_make_vline(vector vline, qstring qs, const char *string)
-{
- char *token, *tp ;
- const char *cp, *sp, *ep, *op ;
-
- /* Reset any existing vline, and empty the qstring if given. */
- if (vline != NULL)
- vector_set_length(vline, 0) ;
-
- qs_clear(qs) ;
-
- /* Strip leading and trailing white-space and deal with empty or effectively
- * empty lines -- comment lines are treated as effectively empty.
- */
- cp = string;
-
- if (string == NULL)
- return NULL;
-
- while (isspace((int) *cp))
- cp++;
-
- ep = cp + strlen(cp) ;
-
- while ((ep > cp) && (isspace((int)*(ep - 1))))
- --ep ;
-
- if ((cp == ep) || (*cp == '!') || (*cp == '#'))
- return NULL;
-
- /* Prepare return vector -- expect some reasonable number of tokens. */
- if (vline == NULL)
- vline = vector_init(10) ;
-
- /* If writing the words to a qstring, copy the body of the original (less
- * any leading/trailing whitespace) to the qstring and '\0' terminate.
- */
- if (qs != NULL)
- {
- qs_set_n(qs, cp, ep - cp) ;
- tp = (char*)qs->body ; /* start at the beginning */
- }
- else
- tp = NULL ; /* not used, but not undefined */
-
- op = cp ; /* original start position */
-
- /* Now have string cp..ep with no leading/trailing whitespace.
- *
- * If using a qstring, a copy of that exists at tp, complete with terminating
- * '\0'. Writes '\0' terminators after each word found -- overwriting first
- * separating white-space or the '\0' at the end.
- *
- * If not using a qstring, construct a new MTYPE_STRVEC for each word.
- */
- while (cp < ep)
- {
- while (isspace((int) *cp))
- cp++ ; /* skip white-space */
-
- sp = cp ;
- while ((cp < ep) && !isspace((int) *cp))
- cp++ ; /* eat token characters */
-
- if (qs == NULL)
- {
- /* creating array of MTYPE_STRVEC */
- size_t len ;
-
- len = cp - sp ;
- token = XMALLOC (MTYPE_STRVEC, len + 1);
- memcpy (token, sp, len);
- *(token + len) = '\0';
- }
- else
- {
- /* using qstring */
- token = tp + (sp - op) ; /* token in qstring */
- *(tp + (cp - op)) = '\0' ; /* terminate */
- } ;
-
- vector_push_item(vline, token);
- } ;
-
- return vline ;
-}
-
-/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
- *
- * Discards leading and trailing white-space.
- *
- * Treats lines that start with '!' or '#' (after any leading white-space)
- * as empty -- these are comment lines.
- *
- * Tokens are non-whitespace separated by one or more white-space.
- *
- * White-space is anything that isspace() thinks is a space. (Which in the
- * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
- *
- * Returns: NULL => empty line (after white-space trimming) or comment line.
- * otherwise: is vector containing one or more tokens.
- *
- * Note: all the tokens in the vector have at least one character, and no
- * entries are NULL.
- *
- * NB: it is the caller's responsibility to release the vector and its contents,
- * see cmd_free_strvec().
+ * otherwise: is vector containing one or more tokens in qstrings.
*/
extern vector
cmd_make_strvec (const char *string)
{
- return cmd_make_vline(NULL, NULL, string) ;
+ return cmd_tokenise(NULL, string) ;
+#error sort this one out
} ;
/*------------------------------------------------------------------------------
@@ -451,7 +341,7 @@ cmd_free_strvec (vector strvec)
char *cp;
/* Note that vector_ream_free() returns NULL if strvec == NULL */
- while((cp = vector_ream_free(strvec)) != NULL)
+ while((cp = vector_ream(strvec, free_it)) != NULL)
XFREE (MTYPE_STRVEC, cp);
} ;
@@ -554,7 +444,7 @@ cmd_make_descvec (const char *string, const char *descstr)
sp = cp;
- while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0')
+ while (! (isspace ((int) *cp) || *cp == ')' || *cp == '|') && *cp != '\0')
cp++;
len = cp - sp;
@@ -846,522 +736,6 @@ cmd_filter_by_symbol (char *command, char *symbol)
#endif
/*==============================================================================
- * Match functions.
- *
- * Is the given string a, possibly incomplete, value of the required kind ?
- */
-
-/* Completion match types. */
-enum match_type
-{
- no_match, /* nope */
- extend_match,
-
- ipv4_prefix_match,
- ipv4_match,
- ipv6_prefix_match,
- ipv6_match,
- range_match,
- vararg_match,
-
- partly_match, /* OK as far as it went */
- exact_match /* Syntactically complete */
-};
-
-/*------------------------------------------------------------------------------
- * Is this an IPv4 Address:
- *
- * 999.999.999.999 -- where no part may be > 255
- *
- * TODO: cmd_ipv4_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_match() seems to accept leading zeros ?
- *
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- */
-static enum match_type
-cmd_ipv4_match (const char *str)
-{
- const char *sp;
- int dots = 0, nums = 0;
- char buf[4];
-
- if (str == NULL)
- return partly_match;
-
- for (;;)
- {
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0')
- {
- if (*str == '.')
- {
- if (dots >= 3)
- return no_match;
-
- if (*(str + 1) == '.')
- return no_match;
-
- if (*(str + 1) == '\0')
- return partly_match;
-
- dots++;
- break;
- }
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (str - sp > 3)
- return no_match;
-
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
-
- nums++;
-
- if (*str == '\0')
- break;
-
- str++;
- }
-
- if (nums < 4)
- return partly_match;
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv4 Prefix:
- *
- * 999.999.999.999/99 -- where no part may be > 255,
- * and prefix length may not be > 32
- *
- * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
- *
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- *
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
- */
-static enum match_type
-cmd_ipv4_prefix_match (const char *str)
-{
- const char *sp;
- int dots = 0;
- char buf[4];
-
- if (str == NULL)
- return partly_match;
-
- for (;;)
- {
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0' && *str != '/')
- {
- if (*str == '.')
- {
- if (dots == 3)
- return no_match;
-
- if (*(str + 1) == '.' || *(str + 1) == '/')
- return no_match;
-
- if (*(str + 1) == '\0')
- return partly_match;
-
- dots++;
- break;
- }
-
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (str - sp > 3)
- return no_match;
-
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
-
- if (dots == 3)
- {
- if (*str == '/')
- {
- if (*(str + 1) == '\0')
- return partly_match;
-
- str++;
- break;
- }
- else if (*str == '\0')
- return partly_match;
- }
-
- if (*str == '\0')
- return partly_match;
-
- str++;
- }
-
- sp = str;
- while (*str != '\0')
- {
- if (!isdigit ((int) *str))
- return no_match;
-
- str++;
- }
-
- if (atoi (sp) > 32)
- return no_match;
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv6 Address:
- *
- * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
- *
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- */
-
-#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
-#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
-#define STATE_START 1
-#define STATE_COLON 2
-#define STATE_DOUBLE 3
-#define STATE_ADDR 4
-#define STATE_DOT 5
-#define STATE_SLASH 6
-#define STATE_MASK 7
-
-#ifdef HAVE_IPV6
-
-static enum match_type
-cmd_ipv6_match (const char *str)
-{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- const char *sp = NULL;
- struct sockaddr_in6 sin6_dummy;
- int ret;
-
- if (str == NULL)
- return partly_match;
-
- if (strspn (str, IPV6_ADDR_STR) != strlen (str))
- return no_match;
-
- /* use inet_pton that has a better support,
- * for example inet_pton can support the automatic addresses:
- * ::1.2.3.4
- */
- ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
-
- if (ret == 1)
- return exact_match;
-
- while (*str != '\0')
- {
- switch (state)
- {
- case STATE_START:
- if (*str == ':')
- {
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
- {
- sp = str;
- state = STATE_ADDR;
- }
-
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == ':')
- state = STATE_DOUBLE;
- else
- {
- sp = str + 1;
- state = STATE_ADDR;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
-
- if (*(str + 1) == ':')
- return no_match;
- else
- {
- if (*(str + 1) != '\0')
- colons++;
- sp = str + 1;
- state = STATE_ADDR;
- }
-
- double_colon++;
- nums++;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '\0')
- {
- if (str - sp > 3)
- return no_match;
-
- nums++;
- state = STATE_COLON;
- }
- if (*(str + 1) == '.')
- state = STATE_DOT;
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- default:
- break;
- }
-
- if (nums > 8)
- return no_match;
-
- if (colons > 7)
- return no_match;
-
- str++;
- }
-
-#if 0
- if (nums < 11)
- return partly_match;
-#endif /* 0 */
-
- return exact_match;
-}
-
-/*------------------------------------------------------------------------------
- * Is this an IPv6 Prefix:
- *
- * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
- * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
- *
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
- *
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
- */
-static enum match_type
-cmd_ipv6_prefix_match (const char *str)
-{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- int mask;
- const char *sp = NULL;
- char *endptr = NULL;
-
- if (str == NULL)
- return partly_match;
-
- if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
- return no_match;
-
- while (*str != '\0' && state != STATE_MASK)
- {
- switch (state)
- {
- case STATE_START:
- if (*str == ':')
- {
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
- {
- sp = str;
- state = STATE_ADDR;
- }
-
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == '/')
- return no_match;
- else if (*(str + 1) == ':')
- state = STATE_DOUBLE;
- else
- {
- sp = str + 1;
- state = STATE_ADDR;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
-
- if (*(str + 1) == ':')
- return no_match;
- else
- {
- if (*(str + 1) != '\0' && *(str + 1) != '/')
- colons++;
- sp = str + 1;
-
- if (*(str + 1) == '/')
- state = STATE_SLASH;
- else
- state = STATE_ADDR;
- }
-
- double_colon++;
- nums += 1;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '.'
- || *(str + 1) == '\0' || *(str + 1) == '/')
- {
- if (str - sp > 3)
- return no_match;
-
- for (; sp <= str; sp++)
- if (*sp == '/')
- return no_match;
-
- nums++;
-
- if (*(str + 1) == ':')
- state = STATE_COLON;
- else if (*(str + 1) == '.')
- state = STATE_DOT;
- else if (*(str + 1) == '/')
- state = STATE_SLASH;
- }
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- case STATE_SLASH:
- if (*(str + 1) == '\0')
- return partly_match;
-
- state = STATE_MASK;
- break;
- default:
- break;
- }
-
- if (nums > 11)
- return no_match;
-
- if (colons > 7)
- return no_match;
-
- str++;
- }
-
- if (state < STATE_MASK)
- return partly_match;
-
- mask = strtol (str, &endptr, 10);
- if (*endptr != '\0')
- return no_match;
-
- if (mask < 0 || mask > 128)
- return no_match;
-
-/* I don't know why mask < 13 makes command match partly.
- Forgive me to make this comments. I Want to set static default route
- because of lack of function to originate default in ospf6d; sorry
- yasu
- if (mask < 13)
- return partly_match;
-*/
-
- return exact_match;
-}
-
-#endif /* HAVE_IPV6 */
-
-/*------------------------------------------------------------------------------
- * Is this a decimal number in the allowed range:
- *
- * Returns: 1 => OK -- *including* empty string
- * 0 => not a valid number, or not in required range
- * (or invalid range !!)
- */
-
-#define DECIMAL_STRLEN_MAX 10
-
-static int
-cmd_range_match (const char *range, const char *str)
-{
- char *p;
- char buf[DECIMAL_STRLEN_MAX + 1];
- char *endptr = NULL;
- unsigned long min, max, val;
-
- if (str == NULL)
- return 1;
-
- val = strtoul (str, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- range++;
- p = strchr (range, '-');
- if (p == NULL)
- return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
- return 0;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- min = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- range = p + 1;
- p = strchr (range, '>');
- if (p == NULL)
- return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
- return 0;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- max = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return 0;
-
- if (val < min || val > max)
- return 0;
-
- return 1;
-}
-
-/*==============================================================================
* Command "filtering".
*
* The command parsing process starts with a (shallow) copy of the cmd_vector
@@ -1379,11 +753,13 @@ cmd_range_match (const char *range, const char *str)
*/
/*------------------------------------------------------------------------------
- * Make completion match and return match type flag.
+ * Make strict or completion match and return match type flag.
*
* Takes: command -- address of candidate token
* cmd_v -- vector of commands that is being reduced/filtered
* index -- index of token (position in line -- 0 == first)
+ * min_match -- any_match => allow partial matching
+ * exact_match => must match completely
*
* Returns: any of the enum match_type values:
*
@@ -1405,15 +781,18 @@ cmd_range_match (const char *range, const char *str)
* furthest down this list.
*/
static enum match_type
-cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
+cmd_filter(const char *command, vector cmd_v, unsigned int index,
+ match_type_t min_match)
{
unsigned int i;
unsigned int k;
- enum match_type match_type;
+ enum match_type best_match;
+ size_t c_len ;
- match_type = no_match;
+ best_match = no_match ;
+ c_len = strlen(command) ;
- /* If command and cmd_element string does not match, remove from vector */
+ /* If command and cmd_element string do match, keep in vector */
k = 0 ;
for (i = 0; i < vector_length (cmd_v); i++)
{
@@ -1422,11 +801,11 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
vector descvec;
struct desc *desc;
unsigned int j;
- int matched ;
+ bool matched ;
cmd_element = vector_get_item(cmd_v, i) ;
- /* Skip past cmd_v entries that have already been set NULL */
+ /* Skip past NULL cmd_v entries (just in case) */
if (cmd_element == NULL)
continue ;
@@ -1447,205 +826,78 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
if (CMD_VARARG (str))
{
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
+ if (best_match < vararg_match)
+ best_match = vararg_match;
+ matched = true ;
}
else if (CMD_RANGE (str))
{
if (cmd_range_match (str, command))
{
- if (match_type < range_match)
- match_type = range_match;
-
- matched++;
+ if (best_match < range_match)
+ best_match = range_match;
+ matched = true ;
}
}
#ifdef HAVE_IPV6
else if (CMD_IPV6 (str))
{
- if (cmd_ipv6_match (command))
+ if (cmd_ipv6_match (command) >= min_match)
{
- if (match_type < ipv6_match)
- match_type = ipv6_match;
-
- matched++;
+ if (best_match < ipv6_match)
+ best_match = ipv6_match;
+ matched = true ;
}
}
else if (CMD_IPV6_PREFIX (str))
{
- if (cmd_ipv6_prefix_match (command))
+ if (cmd_ipv6_prefix_match (command) >= min_match)
{
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
-
- matched++;
+ if (best_match < ipv6_prefix_match)
+ best_match = ipv6_prefix_match;
+ matched = true ;
}
}
#endif /* HAVE_IPV6 */
else if (CMD_IPV4 (str))
{
- if (cmd_ipv4_match (command))
+ if (cmd_ipv4_match (command) >= min_match)
{
- if (match_type < ipv4_match)
- match_type = ipv4_match;
-
- matched++;
+ if (best_match < ipv4_match)
+ best_match = ipv4_match;
+ matched = true ;
}
}
else if (CMD_IPV4_PREFIX (str))
{
- if (cmd_ipv4_prefix_match (command))
+ if (cmd_ipv4_prefix_match (command) >= min_match)
{
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
+ if (best_match < ipv4_prefix_match)
+ best_match = ipv4_prefix_match;
+ matched = true ;
}
}
else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- /* Check is this point's argument optional ? */
{
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
+ if (best_match < extend_match)
+ best_match = extend_match;
+ matched = true ;
}
- else if (strncmp (command, str, strlen (command)) == 0)
+ else
{
if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
{
- if (match_type < partly_match)
- match_type = partly_match;
+ best_match = exact_match ;
+ matched = true ;
}
- matched++;
- } ;
- } ;
-
- /* Keep cmd_v entry that has a match at this position */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return match_type;
-} ;
-
-/*------------------------------------------------------------------------------
- * Filter vector by command character with index.
- *
- * This appears to be identical to cmd_filter_by_completion(), except that
- * when matching keywords, requires an exact match.
- *
- * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string()
- */
-static enum match_type
-cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
-{
- unsigned int i ;
- unsigned int k ;
- enum match_type match_type;
-
- match_type = no_match;
-
- /* If command and cmd_element string do match, keep in vector */
- k = 0 ;
- for (i = 0; i < vector_length(cmd_v); i++)
- {
- unsigned int j;
- int matched ;
- const char *str;
- struct cmd_element *cmd_element;
- vector descvec;
- struct desc *desc;
-
- cmd_element = vector_get_item(cmd_v, i) ;
-
- /* Skip past NULL cmd_v entries (just in case) */
- if (cmd_element == NULL)
- continue ;
-
- /* Discard cmd_v entry that has no token at the current position */
- descvec = vector_get_item (cmd_element->strvec, index) ;
- if (descvec == NULL)
- continue ;
-
- /* See if have a match against any of the current possibilities */
- matched = 0 ;
- for (j = 0; j < vector_length(descvec); j++)
- {
- desc = vector_get_item (descvec, j) ;
- if (desc == NULL)
- continue ;
-
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
+ else if (min_match <= partly_match)
{
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
+ if (strncmp (command, str, c_len) == 0)
+ {
+ if (best_match < partly_match)
+ best_match = partly_match ;
+ matched = true ;
+ } ;
} ;
} ;
} ;
@@ -1657,8 +909,8 @@ cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
vector_set_length(cmd_v, k) ; /* discard what did not keep */
- return match_type;
-}
+ return best_match;
+} ;
/*------------------------------------------------------------------------------
* Check for ambiguous match
@@ -1699,7 +951,8 @@ cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
* returns 1 in preference.
*/
static int
-is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
+is_cmd_ambiguous (const char *command, vector cmd_v, int index,
+ enum match_type type)
{
unsigned int i;
unsigned int k;
@@ -1715,7 +968,7 @@ is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
const char *str_matched ;
vector descvec;
struct desc *desc;
- int matched ;
+ bool matched ;
enum match_type mt ;
cmd_element = vector_get_item (cmd_v, i) ;
@@ -1754,7 +1007,7 @@ is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
case exact_match:
if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
&& strcmp (command, str) == 0)
- matched++;
+ matched = true ;
break;
case partly_match:
@@ -1765,7 +1018,7 @@ is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
ret = 1; /* There is ambiguous match. */
else
str_matched = str;
- matched++;
+ matched = true ;
}
break;
@@ -1776,47 +1029,43 @@ is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
ret = 1;
else
str_matched = str;
- matched++;
+ matched = true ;
}
break;
#ifdef HAVE_IPV6
case ipv6_match:
if (CMD_IPV6 (str))
- matched++;
+ matched = true ;
break;
case ipv6_prefix_match:
if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
{
- if (mt == partly_match)
- if (ret != 1)
- ret = 2; /* There is incomplete match. */
-
- matched++;
+ if ((mt == partly_match) && (ret != 1))
+ ret = 2; /* There is incomplete match. */
+ matched = true ;
}
break;
#endif /* HAVE_IPV6 */
case ipv4_match:
if (CMD_IPV4 (str))
- matched++;
+ matched = true ;
break;
case ipv4_prefix_match:
if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
{
- if (mt == partly_match)
- if (ret != 1)
- ret = 2; /* There is incomplete match. */
-
- matched++;
+ if ((mt == partly_match) && (ret != 1))
+ ret = 2; /* There is incomplete match. */
+ matched = true ;
}
break;
case extend_match:
if (CMD_OPTION (str) || CMD_VARIABLE (str))
- matched++;
+ matched = true ;
break;
case no_match:
@@ -1956,195 +1205,256 @@ desc_unique_string (vector v, const char *str)
return 0;
}
+/*------------------------------------------------------------------------------
+ * Special parsing for leading 'do', if current mode allows it.
+ *
+ * If finds a valid "do", sets current node and do_shortcut flag, and discards
+ * the "do" token.
+ *
+ * Returns: true <=> dealt with the "do"
+ * false => no do, or no do allowed.
+ */
static bool
-cmd_try_do_shortcut (enum node_type node, char* first_word) {
- return (node >= MIN_DO_SHORTCUT_NODE)
- && (first_word != NULL)
- && (strcmp( "do", first_word) == 0) ? 1 : 0 ;
-}
+cmd_try_do_shortcut(cmd_parsed parsed)
+{
+ const char* ts ;
-/* '?' describe command support. */
-static vector
-cmd_describe_command_real (vector vline, int node, int *status)
+ if (parsed->cnode < MIN_DO_SHORTCUT_NODE)
+ return false ;
+
+ ts = cmd_token_string(cmd_token_get(&parsed->tokens, 0)) ;
+ if (strcmp("do", ts) != 0)
+ return false ;
+
+ parsed->cnode = ENABLE_NODE ;
+ parsed->do_shortcut = true ;
+ cmd_token_discard(cmd_token_shift(&parsed->tokens)) ;
+
+ return true ;
+} ;
+
+/*==============================================================================
+ * '?' describe command support.
+ */
+
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct desc" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
+extern vector
+cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status)
{
- unsigned int i;
- vector cmd_vector;
-#define INIT_MATCHVEC_SIZE 10
- vector matchvec;
- struct cmd_element *cmd_element;
- unsigned int index;
- int ret;
- enum match_type match;
- char *command;
+ vector ret ;
+ struct cmd_parsed parsed_s ;
+ cmd_parsed parsed ;
+ cmd_token_type_t tok_total ;
- /* Set index. */
- if (vector_length (vline) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- else
- index = vector_length (vline) - 1;
+ /* Set up a parser object and tokenise the command line */
+ parsed = cmd_parse_init_new(&parsed_s) ;
+ tok_total = cmd_tokenise(parsed, line, node) ;
- /* Make copy vector of current node's command vector. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
- /* Prepare match vector */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
- /* Filter commands. */
- /* Only words precedes current word will be checked in this loop. */
- for (i = 0; i < index; i++)
- if ((command = vector_get_item (vline, i)))
- {
- match = cmd_filter_by_completion (command, cmd_vector, i);
- if (match == vararg_match)
- {
- struct cmd_element *cmd_element;
- vector descvec;
- unsigned int j, k;
-
- for (j = 0; j < vector_length (cmd_vector); j++)
- if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL
- && (vector_length (cmd_element->strvec)))
- {
- descvec = vector_get_item (cmd_element->strvec,
- vector_length (cmd_element->strvec) - 1);
- for (k = 0; k < vector_length (descvec); k++)
- {
- struct desc *desc = vector_get_item (descvec, k);
- vector_set (matchvec, desc);
- }
- }
-
- vector_set (matchvec, &desc_cr);
- vector_free (cmd_vector);
-
- return matchvec;
- }
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- }
- /* Prepare match vector */
- /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_get_item (vline, index);
- if (command)
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ /* Stop immediately if line is empty apart from comment */
+ if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
+ return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
- /* Make description vector. */
- for (i = 0; i < vector_length (cmd_vector); i++)
+ /* Level 1 parsing
+ *
+ * Strip quotes and escapes from all the tokens.
+ */
+ if (tok_total != cmd_tok_simple)
{
- vector strvec ;
+ ret = cmd_parse_phase_one(parsed) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
- cmd_element = vector_get_item (cmd_vector, i) ;
- if (cmd_element == NULL)
- continue ;
+ /* If allowed to 'do', see if there.
+ *
+ * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ */
+ if (type & cmd_parse_do)
+ cmd_try_do_shortcut(parsed) ;
- /* Ignore cmd_element if no tokens at index position.
- *
- * Deal with special case of possible <cr> completion.
- */
- strvec = cmd_element->strvec;
- if (index >= vector_length (strvec))
+
+
+
+ return cmd_describe_command_real (tokens, node, status);
+
+
+
+ static vector
+ cmd_describe_command_real (vector tokens, int node, int *status)
+ {
+ unsigned int i;
+ vector cmd_vector;
+ #define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+
+ /* Set index. */
+ if (vector_length (tokens) == 0)
+ {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ else
+ index = vector_length (tokens) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
+
+ /* Prepare match vector */
+ matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_get_item (tokens, i)))
{
- if (command == NULL && index == vector_length (strvec))
+ match = cmd_filter(command, cmd_vector, i, any_match) ;
+
+ if (match == vararg_match)
{
- if (!desc_unique_string (matchvec, command_cr))
- vector_push_item(matchvec, &desc_cr);
- }
- continue ;
- } ;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_length (cmd_vector); j++)
+ if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL
+ && (vector_length (cmd_element->strvec)))
+ {
+ descvec = vector_get_item (cmd_element->strvec,
+ vector_length (cmd_element->strvec) - 1);
+ for (k = 0; k < vector_length (descvec); k++)
+ {
+ struct desc *desc = vector_get_item (descvec, k);
+ vector_set (matchvec, desc);
+ }
+ }
+
+ vector_set (matchvec, &desc_cr);
+ vector_free (cmd_vector);
+
+ return matchvec;
+ } ;
- /* Check if command is completed. */
- unsigned int j;
- vector descvec = vector_get_item (strvec, index);
- struct desc *desc;
+ ret = is_cmd_ambiguous (command, cmd_vector, i, match) ;
+ if (ret != 0)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ *status = (ret == 1) ? CMD_ERR_AMBIGUOUS
+ : CMD_ERR_NO_MATCH ;
+ return NULL ;
+ } ;
+ }
- for (j = 0; j < vector_length (descvec); j++)
- if ((desc = vector_get_item (descvec, j)))
- {
- const char *string;
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_get_item (tokens, index);
+ if (command)
+ match = cmd_filter(command, cmd_vector, index, any_match);
+
+ /* Make description vector. */
+ for (i = 0; i < vector_length (cmd_vector); i++)
+ {
+ vector strvec ;
+
+ cmd_element = vector_get_item (cmd_vector, i) ;
+ if (cmd_element == NULL)
+ continue ;
+
+ /* Ignore cmd_element if no tokens at index position.
+ *
+ * Deal with special case of possible <cr> completion.
+ */
+ strvec = cmd_element->strvec;
+ if (index >= vector_length (strvec))
+ {
+ if (command == NULL && index == vector_length (strvec))
{
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_push_item(matchvec, desc);
+ if (!desc_unique_string (matchvec, command_cr))
+ vector_push_item(matchvec, &desc_cr);
}
+ continue ;
} ;
- } ;
- vector_free (cmd_vector);
+ /* Check if command is completed. */
+ unsigned int j;
+ vector descvec = vector_get_item (strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_length (descvec); j++)
+ if ((desc = vector_get_item (descvec, j)))
+ {
+ const char *string;
+
+ string = cmd_entry_function_desc (command, desc->cmd);
+ if (string)
+ {
+ /* Uniqueness check */
+ if (!desc_unique_string (matchvec, string))
+ vector_push_item(matchvec, desc);
+ }
+ } ;
+ } ;
+
+ vector_free (cmd_vector);
+
+ if (vector_length(matchvec) == 0)
+ {
+ vector_free (matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ *status = CMD_SUCCESS;
+ return matchvec;
+ }
+
+
+
+
- if (vector_length(matchvec) == 0)
- {
- vector_free (matchvec);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- *status = CMD_SUCCESS;
- return matchvec;
-}
-/*------------------------------------------------------------------------------
- * Get description of current (partial) command
- *
- * Returns: NULL => no description available
- *
- * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
- *
- * or: address of vector of "struct desc" values available.
- *
- * NB: when a vector is returned it is the caller's responsibility to
- * vector_free() it. (The contents are all effectively const, so do not
- * themselves need to be freed.)
- */
-vector
-cmd_describe_command (vector vline, int node, int *status)
-{
- vector ret;
- if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
- /* We can try it on enable node, cos' the vty is authenticated */
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_length (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
- }
- ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status);
- vector_free(shifted_vline);
- return ret;
- }
- return cmd_describe_command_real (vline, node, status);
-}
+
+
+
+
+
+ cmd_parse_reset(parsed, false) ;
+
+ return
+} ;
/*------------------------------------------------------------------------------
* Check LCD of matched command.
@@ -2192,7 +1502,7 @@ cmd_lcd (vector matchvec)
* Command line completion support.
*/
static vector
-cmd_complete_command_real (vector vline, int node, int *status)
+cmd_complete_command_real (vector tokens, int node, int *status)
{
unsigned int i;
unsigned int ivl ;
@@ -2207,8 +1517,8 @@ cmd_complete_command_real (vector vline, int node, int *status)
char *token;
int n ;
- /* Stop immediately if the vline is empty. */
- if (vector_length (vline) == 0)
+ /* Stop immediately if the tokens is empty. */
+ if (vector_length (tokens) == 0)
{
*status = CMD_ERR_NO_MATCH;
return NULL;
@@ -2218,7 +1528,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
/* First, filter upto, but excluding last token */
- last_ivl = vector_length (vline) - 1;
+ last_ivl = vector_length (tokens) - 1;
for (ivl = 0; ivl < last_ivl; ivl++)
{
@@ -2226,7 +1536,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
int ret;
/* TODO: does this test make any sense ? */
- if ((token = vector_get_item (vline, ivl)) == NULL)
+ if ((token = vector_get_item (tokens, ivl)) == NULL)
continue ;
/* First try completion match, return best kind of match */
@@ -2263,7 +1573,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
/* Now we got into completion */
index = last_ivl ;
- token = vector_get_item(vline, last_ivl) ; /* is now the last token */
+ token = vector_get_item(tokens, last_ivl) ; /* is now the last token */
for (i = 0; i < vector_length (cmd_v); i++)
{
@@ -2349,31 +1659,32 @@ cmd_complete_command_real (vector vline, int node, int *status)
* Can the current command be completed ?
*/
extern vector
-cmd_complete_command (vector vline, int node, int *status)
+cmd_complete_command (vector tokens, int node, int *status)
{
vector ret;
- if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_get_item(tokens, 0) ) )
{
- vector shifted_vline;
+ vector shifted_tokens;
unsigned int index;
/* We can try it on enable node, cos' the vty is authenticated */
- shifted_vline = vector_init (vector_count(vline));
+ shifted_tokens = vector_init (vector_count(tokens));
/* use memcpy? */
- for (index = 1; index < vector_length (vline); index++)
+ for (index = 1; index < vector_length (tokens); index++)
{
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
+ vector_set_index (shifted_tokens, index-1,
+ vector_lookup(tokens, index)) ;
}
- ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status);
+ ret = cmd_complete_command_real (shifted_tokens, ENABLE_NODE, status);
- vector_free(shifted_vline);
+ vector_free(shifted_tokens);
return ret;
}
- return cmd_complete_command_real (vline, node, status);
+ return cmd_complete_command_real (tokens, node, status);
}
/*------------------------------------------------------------------------------
@@ -2400,58 +1711,12 @@ node_parent ( enum node_type node )
default:
return CONFIG_NODE;
- }
-}
-
-/*------------------------------------------------------------------------------
- * Initialise a new struct cmd_parsed, allocating if required
- */
-extern cmd_parsed
-cmd_parse_init_new(cmd_parsed parsed)
-{
- if (parsed == NULL)
- parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
- else
- memset(parsed, 0, sizeof(*parsed)) ;
-
- /* Zeroising the structure has set:
- *
- * cmd = NULL -- no command parsed, yet
- * cnode -- no node set, yet
- *
- * do_shortcut -- false
- * onode -- not material (do_shortcut is false)
- *
- * line = zeroised qstring -- empty
- * words = zeroised qstring -- empty
- *
- * vline = zeroised vector -- empty
- *
- * so nothing else to do
- */
-
- return parsed ;
+ } ;
} ;
-/*------------------------------------------------------------------------------
- * Initialise a new struct cmd_parsed, allocating if required
+/*==============================================================================
+ * Parsing of command lines
*/
-extern cmd_parsed
-cmd_parse_reset(cmd_parsed parsed, bool free_structure)
-{
- if (parsed != NULL)
- {
- qs_reset_keep(&parsed->words) ;
- vector_reset_keep(&parsed->vline) ;
-
- if (free_structure)
- XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
- else
- cmd_parse_init_new(parsed) ;
- } ;
-
- return parsed ;
-} ;
/*------------------------------------------------------------------------------
* Parse a command in the given "node", if possible, ready for execution.
@@ -2465,8 +1730,9 @@ cmd_parse_reset(cmd_parsed parsed, bool free_structure)
* current node.
*/
-static enum cmd_return_code
-cmd_parse_this(struct cmd_parsed* parsed, bool strict) ;
+static enum cmd_return_code cmd_parse_phase_one(cmd_parsed parsed) ;
+static enum cmd_return_code cmd_parse_phase_two(struct cmd_parsed* parsed,
+ bool strict) ;
/*------------------------------------------------------------------------------
* Parse a command in the given "node", or (if required) any of its ancestors.
@@ -2502,41 +1768,61 @@ cmd_parse_this(struct cmd_parsed* parsed, bool strict) ;
* See elsewhere for description of parsed structure.
*/
extern enum cmd_return_code
-cmd_parse_command(struct vty* vty, enum cmd_parse_type type)
+cmd_parse_command(struct vty* vty, cmd_parse_type_t type)
{
enum cmd_return_code ret ;
enum cmd_return_code first_ret ;
cmd_parsed parsed ;
+ cmd_token_type_t tok_total ;
+ bool varflag ;
+ unsigned int i, ivl ;
/* Initialise the parsed structure -- assuming no 'do' */
if (vty->parsed == NULL)
- vty->parsed = cmd_parse_init_new(NULL) ;
- parsed = vty->parsed ;
+ parsed = vty->parsed = cmd_parse_init_new(NULL) ;
+ else
+ {
+ parsed = vty->parsed ;
- parsed->onode = parsed->cnode = vty->node ;
+ parsed->cmd = NULL ;
+ parsed->do_shortcut = false ;
- parsed->cmd = NULL ;
- parsed->do_shortcut = 0 ;
+ if (parsed->pipes != cmd_pipe_none)
+ {
+ parsed->pipes = cmd_pipe_none ;
+ cmd_empty_parsed_tokens(parsed) ;
+ } ;
+ } ;
- /* Parse the line into words -- set up parsed->words and parsed->vline */
- cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ;
+ /* Parse the line into tokens, set parsed->line, ->cnode & ->onode */
+ tok_total = cmd_tokenise(parsed, vty->buf, vty->node) ;
- if (vector_length(&parsed->vline) == 0)
+ /* Stop immediately if line is empty apart from comment */
+ if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
- /* If allowed to 'do', see if there.
+ /* Level 1 parsing
*
- * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ * Strip quotes and escapes from all the tokens.
*/
- if ((type & cmd_parse_do) &&
- cmd_try_do_shortcut(parsed->cnode, vector_get_item(&parsed->vline, 0)))
+ if (tok_total != cmd_tok_simple)
{
- parsed->cnode = ENABLE_NODE ;
- parsed->do_shortcut = 1 ;
+ ret = cmd_parse_phase_one(parsed) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
} ;
- /* Try in the current node */
- ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
+ /* If allowed to 'do', see if there.
+ *
+ * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ */
+ if (type & cmd_parse_do)
+ cmd_try_do_shortcut(parsed) ;
+
+ /* Level 2 parsing
+ * Try in the current node
+ */
+ ret = cmd_parse_phase_two(parsed, type) ;
if (ret != CMD_SUCCESS)
{
@@ -2556,72 +1842,132 @@ cmd_parse_command(struct vty* vty, enum cmd_parse_type type)
} ;
parsed->cnode = node_parent(parsed->cnode) ;
- ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
+ ret = cmd_parse_phase_two(parsed, type) ;
} ;
} ;
- return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
- : CMD_SUCCESS ;
+ /* Parsed successfully -- construct the arg_vector */
+
+ varflag = false ;
+ ivl = vector_length(parsed->cmd->strvec) ;
+
+ cmd_arg_vector_empty(parsed) ;
+ for (i = 0; i < ivl ; i++)
+ {
+ bool take = varflag ;
+
+ if (!varflag)
+ {
+ vector descvec = vector_get_item (parsed->cmd->strvec, i);
+
+ if (vector_length (descvec) == 1)
+ {
+ struct desc *desc = vector_get_item (descvec, 0);
+
+ if (CMD_VARARG (desc->cmd))
+ take = varflag = true ;
+ else
+ take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
+ }
+ else
+ take = true ;
+ }
+
+ if (take)
+ cmd_arg_vector_push(parsed,
+ cmd_token_value(cmd_token_get(&parsed->tokens, i))) ;
+ } ;
+
+ /* Return appropriate form of success */
+ return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 1 of command parsing
+ *
+ * * At start of line look for:
+ *
+ * * '<' -- pipe-in command of some sort
+ *
+ * Sets the pipe type and the read_pipe_tokens -- all tokens up to
+ * '>', '|', '!' or '#'..
+ *
+ * Scan for '>', '|', '!' or '#'
+ *
+ * * '>' or '|' -- pipe-out command of some sort
+ *
+ * Collect type of pipe, and then all tokens up to '!' or '#'
+ *
+ *
+ *
+ * * '!', '#', comment -- discards
+ *
+ * Returns: CMD_SUCCESS -- parsed successfully
+ * CMD_ERR_NO_MATCH )
+ * CMD_ERR_AMBIGUOUS ) failed to parse
+ * CMD_ERR_INCOMPLETE )
+ */
+static enum cmd_return_code
+cmd_parse_phase_one(cmd_parsed parsed)
+{
+
+
+
+
+
+
} ;
/*------------------------------------------------------------------------------
- * Work function for cmd_parse_command
+ * Phase 2 of command parsing
*
* Takes a parsed structure, with the:
*
* cnode -- node to parse in
- * vline -- the line broken into words
+ * tokens -- the line broken into words
* do_shortcut -- true if first word is 'do' (to be ignored)
*
* and parses either strictly or with command completion.
*
- * If successful, reduces the vline structure down to the variable portions,
- * ie to the argv[] for the command function.
- *
* Returns: CMD_SUCCESS -- parsed successfully
* CMD_ERR_NO_MATCH )
* CMD_ERR_AMBIGUOUS ) failed to parse
* CMD_ERR_INCOMPLETE )
*/
static enum cmd_return_code
-cmd_parse_this(cmd_parsed parsed, bool strict)
+cmd_parse_phase_two(cmd_parsed parsed, cmd_parse_type_t type)
{
unsigned int i ;
unsigned int ivl ;
unsigned index ;
- unsigned first ;
- unsigned argc ;
vector cmd_v;
struct cmd_element *cmd_element;
struct cmd_element *matched_element;
unsigned int matched_count, incomplete_count;
- enum match_type match = 0;
- int varflag;
- char *command;
-
- /* Need length of vline, discounting the first entry if required */
- first = parsed->do_shortcut ? 1 : 0 ;
+ enum match_type match ;
+ enum match_type filter_level ;
+ const char *command;
- assert(vector_length(&parsed->vline) >= first) ;
- ivl = vector_length(&parsed->vline) - first ;
+ /* Need number of tokens */
+ ivl = cmd_token_count(&parsed->tokens) ;
/* Make copy of command elements. */
cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
/* Look for an unambiguous result */
+ filter_level = (type & cmd_parse_strict) ? exact_match : any_match ;
+ match = no_match ; /* in case of emptiness */
for (index = 0 ; index < ivl; index++)
{
int ret ;
- command = vector_get_item(&parsed->vline, index + first) ;
- if (command == NULL)
- continue ;
+ command = cmd_token_string(cmd_token_get(&parsed->tokens, index)) ;
- match = strict ? cmd_filter_by_string(command, cmd_v, index)
- : cmd_filter_by_completion(command, cmd_v, index) ;
+ match = cmd_filter(command, cmd_v, index, filter_level) ;
if (match == vararg_match)
- break;
+ break;
ret = is_cmd_ambiguous (command, cmd_v, index, match);
@@ -2630,7 +1976,7 @@ cmd_parse_this(cmd_parsed parsed, bool strict)
assert((ret == 1) || (ret == 2)) ;
vector_free (cmd_v);
return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
- }
+ }
} ;
/* Check matched count. */
@@ -2645,17 +1991,17 @@ cmd_parse_this(cmd_parsed parsed, bool strict)
continue ;
if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
+ {
+ matched_element = cmd_element;
#if 0
- printf ("DEBUG: %s\n", cmd_element->string);
+ printf ("DEBUG: %s\n", cmd_element->string);
#endif
- matched_count++;
- }
+ matched_count++;
+ }
else
- {
- incomplete_count++;
- }
+ {
+ incomplete_count++;
+ }
} ;
/* Finished with cmd_v. */
@@ -2670,37 +2016,6 @@ cmd_parse_this(cmd_parsed parsed, bool strict)
return CMD_ERR_AMBIGUOUS ;
} ;
- /* Found command -- process the arguments ready for execution */
- varflag = 0 ;
- argc = 0 ;
-
- for (index = 0; index < ivl ; index++)
- {
- int take = varflag ;
-
- if (!varflag)
- {
- vector descvec = vector_get_item (matched_element->strvec, index);
-
- if (vector_length (descvec) == 1)
- {
- struct desc *desc = vector_get_item (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- take = varflag = 1 ;
- else
- take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
- }
- else
- take = 1 ;
- }
-
- if (take)
- vector_assign_item(&parsed->vline, argc++, index + first) ;
- } ;
-
- vector_set_length(&parsed->vline, argc) ; /* set to new length */
-
/* Everything checks out... ready to execute command */
parsed->cmd = matched_element ;
@@ -2803,7 +2118,7 @@ cmd_execute_command(struct vty *vty,
*cmd = vty->parsed->cmd ; /* for vtysh */
if (ret == CMD_SUCCESS)
- ret = cmd_dispatch(vty, 0) ;
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
else if (ret == CMD_EMPTY)
ret = CMD_SUCCESS ;
@@ -2903,6 +2218,129 @@ config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd,
return ret ;
} ;
+/*==============================================================================
+ */
+
+static cmd_return_code_t cmd_fetch_command(struct vty* vty) ;
+
+/*------------------------------------------------------------------------------
+ * Command Loop
+ *
+ * Read and dispatch commands until can no longer do so for whatever reason:
+ *
+ * - reached end of command stream -- CMD_CLOSE
+ * - encounter error of some kind -- CMD_WARNING, CMD_ERROR, etc
+ * - waiting for input to arrive -- CMD_WAIT_INPUT
+ * - waiting for command to complete -- CMD_QUEUED
+ * - waiting for output to complete -- ??
+ *
+ */
+extern cmd_return_code_t
+cmd_command_loop(struct vty *vty, struct cmd_element* first_cmd,
+ qstring buf, bool ignore_warning)
+{
+ cmd_return_code_t ret ;
+
+ vty->buf = buf->body ;
+ vty->lineno = 0 ;
+
+
+ /*
+ *
+ */
+
+ vty_out_clear(vty) ;
+
+ while (1) {
+
+ /* Fetch a command line */
+
+ ret = cmd_fetch_command(vty) ;
+
+ if (ret != CMD_SUCCESS)
+ break ;
+
+ ++vty->lineno ;
+
+ /* Parse the command line we now have */
+
+ ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
+
+ if (ret == CMD_EMPTY)
+ continue ; /* skip empty/comment */
+
+ if (ret != CMD_SUCCESS)
+ break ; /* stop on *any* parsing issue */
+
+ /* special handling before of first command */
+ if (first_cmd != NULL)
+ {
+ if (first_cmd != vty->parsed->cmd)
+ {
+ ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
+ if (ret != CMD_SUCCESS)
+ break ; /* stop on *any* issue with "default" */
+ } ;
+ first_cmd = NULL ;
+ } ;
+
+ /* Reflect command line if required */
+
+ /* Standard command handling */
+
+ ret = cmd_dispatch(vty, cmd_no_queue) ;
+
+ if (ret == CMD_QUEUED)
+ break ;
+
+ /* Output Handling..... */
+
+
+ /* Return code handling.... */
+
+ if (ret != CMD_SUCCESS)
+ {
+ if ((ret == CMD_WARNING) && !ignore_warning)
+ break ;
+ if (ret != CMD_CLOSE)
+ break ;
+ } ;
+
+ vty_out_clear(vty) ;
+ } ;
+
+ return ret ;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * Fetch the next command.
+ *
+ *
+ *
+ *
+ */
+static cmd_return_code_t
+cmd_fetch_command(struct vty* vty)
+{
+
+
+
+
+} ;
+
+
+
+
+
+
+
+
+
+
+
+/*============================================================================*/
+
/*----------------------------------------------------------------------------*/
/* Configration from terminal */
@@ -2927,7 +2365,7 @@ DEFUN_CALL (enable,
{
/* If enable password is NULL, change to ENABLE_NODE */
if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty_shell_serv(vty))
+ vty_shell_server(vty))
vty_set_node(vty, ENABLE_NODE);
else
vty_set_node(vty, AUTH_ENABLE_NODE);
@@ -4061,17 +3499,20 @@ cmd_init (int terminal)
desc_cr.cmd = command_cr;
desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
- /* Allocate initial top vector of commands. */
- cmdvec = vector_init (0);
+ /* Allocate initial top vector of commands. */
+ cmdvec = vector_init(0);
+
+ /* Allocate vector of spare qstrings for tokens */
+ cmd_spare_tokens_init() ;
/* Default host value settings. */
- host.name = NULL;
+ host.name = NULL;
host.password = NULL;
- host.enable = NULL;
- host.logfile = NULL;
- host.config = NULL;
- host.lines = -1;
- host.motd = default_motd;
+ host.enable = NULL;
+ host.logfile = NULL;
+ host.config = NULL;
+ host.lines = -1;
+ host.motd = default_motd;
host.motdfile = NULL;
/* Install top nodes. */
@@ -4187,6 +3628,8 @@ cmd_terminate ()
struct desc *desc;
vector cmd_node_v, cmd_element_v, desc_v;
+ cmd_spare_tokens_free() ;
+
if (cmdvec)
{
for (i = 0; i < vector_length (cmdvec); i++)
diff --git a/lib/command.h b/lib/command.h
index ba7c4bb2..fe4f0a56 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -23,16 +23,12 @@
#ifndef _ZEBRA_COMMAND_H
#define _ZEBRA_COMMAND_H
-#include <stdbool.h>
+#include "misc.h"
#include "node_type.h"
#include "vector.h"
#include "qstring.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
struct vty ; /* in case command.h expanded first */
/* Host configuration variable */
@@ -89,6 +85,7 @@ struct cmd_node
enum
{
+ CMD_ATTR_SIMPLE = 0x00,
/* bit significant */
CMD_ATTR_DEPRECATED = 0x01,
CMD_ATTR_HIDDEN = 0x02,
@@ -113,6 +110,7 @@ enum cmd_return_code
CMD_EMPTY,
CMD_SUCCESS_DAEMON,
+ CMD_WAIT_INPUT,
CMD_CLOSE,
CMD_QUEUED,
@@ -126,6 +124,8 @@ enum cmd_return_code
CMD_COMPLETE_ALREADY
} ;
+typedef enum cmd_return_code cmd_return_code_t ;
+
#define MSG_CMD_ERR_AMBIGUOUS "Ambiguous command"
#define MSG_CMD_ERR_NO_MATCH "Unrecognised command"
#define MSG_CMD_ERR_NO_MATCH_old "There is no matched command"
@@ -148,58 +148,24 @@ typedef DEFUN_CMD_FUNCTION((cmd_function)) ;
struct cmd_element
{
- const char *string; /* Command specification by string. */
- cmd_function* func ;
- const char *doc; /* Documentation of this command. */
- int daemon; /* Daemon to which this command belong. */
- vector strvec; /* Pointing out each description vector. */
- unsigned int cmdsize; /* Command index count. */
- char *config; /* Configuration string */
- vector subconfig; /* Sub configuration string */
- u_char attr; /* Command attributes */
+ const char* string ; /* Command specification by string. */
+ cmd_function* func ;
+ const char* doc ; /* Documentation of this command. */
+ int daemon ; /* Daemon to which this command belong. */
+ vector strvec ; /* Vector of vectors of struct desc */
+ unsigned int cmdsize ; /* Command index count. */
+ char* config ; /* Configuration string */
+ vector subconfig ; /* Sub configuration string */
+ u_char attr ; /* Command attributes */
};
-/* Command description structure. */
+/* Command description structure. */
struct desc
{
- char *cmd; /* Command string. */
- char *str; /* Command's description. */
+ char* cmd; /* Command string. */
+ char* str; /* Command's description. */
};
-/* Command parsing options */
-enum cmd_parse_type /* bit significant */
-{
- cmd_parse_completion = 0x00,
- cmd_parse_strict = 0x01,
-
- cmd_parse_do = 0x02,
- cmd_parse_tree = 0x04,
-} ;
-
-/* Parsed command */
-typedef struct cmd_parsed* cmd_parsed ;
-struct cmd_parsed
-{
- struct cmd_element *cmd ; /* NULL if empty command
- or fails to parse */
-
- enum node_type cnode ; /* node command is in */
- enum node_type onode ; /* node the parser started in */
-
- bool do_shortcut ; /* true => is "do" command */
-
- qstring_t words ; /* the words, '\0' separated */
-
- vector_t vline ; /* pointers to the words */
-} ;
-
-
-/* Command dispatch options */
-enum {
- cmd_no_queue = true,
- cmd_may_queue = false,
-} ;
-
/*------------------------------------------------------------------------------
* Can now include these
*/
@@ -313,9 +279,9 @@ enum {
#define CMD_VARARG(S) ((S[0]) == '.')
#define CMD_RANGE(S) ((S[0] == '<'))
-#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
-#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
-#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
/* Common descriptions. */
diff --git a/lib/command_execute.h b/lib/command_execute.h
index a5a807c8..6a7e2e4b 100644
--- a/lib/command_execute.h
+++ b/lib/command_execute.h
@@ -24,11 +24,14 @@
#define _ZEBRA_COMMAND_EXECUTE_H
#include "command.h"
+#include "command_parse.h"
+#include "node_type.h"
extern vector cmd_make_strvec (const char *);
extern vector cmd_add_to_strvec (vector v, const char* str) ;
extern void cmd_free_strvec (vector);
-extern vector cmd_describe_command (vector, int, int *status);
+extern vector cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status) ;
extern vector cmd_complete_command (vector, int, int *status);
extern const char *cmd_prompt (enum node_type);
extern enum cmd_return_code
@@ -50,9 +53,8 @@ Inline enum cmd_return_code
cmd_dispatch_call(struct vty* vty)
{
cmd_parsed parsed = vty->parsed ;
- return (*(parsed->cmd->func))(parsed->cmd, vty,
- vector_length(&parsed->vline),
- (const char * const*)vector_body(&parsed->vline)) ;
+ return (*(parsed->cmd->func))(parsed->cmd, vty, cmd_arg_vector_argc(parsed),
+ cmd_arg_vector_argv(parsed)) ;
} ;
#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
diff --git a/lib/command_parse.c b/lib/command_parse.c
new file mode 100644
index 00000000..d8960b8d
--- /dev/null
+++ b/lib/command_parse.c
@@ -0,0 +1,903 @@
+/* Quagga command line parsing -- header
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command_parse.h"
+#include "memory.h"
+
+/*==============================================================================
+ * Token handling
+ */
+
+/* Store of qstrings, used for parsing. */
+token_vector_t spare_tokens ;
+
+static char cmd_token_escape(char e) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new token vector -- empty.
+ */
+static inline void
+cmd_token_vector_init(token_vector tokens)
+{
+ vector_init_new(tokens->body, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise empty spare tokens vector
+ */
+extern void
+cmd_spare_tokens_init(void)
+{
+ cmd_token_vector_init(spare_tokens) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty out the spare_tokens vector and release all memory
+ */
+extern void
+cmd_spare_tokens_free(void)
+{
+ token tok ;
+
+ while ((tok = vector_ream(spare_tokens->body, keep_it)) != NULL)
+ qs_reset(tok->qs, keep_it) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take string and break it into tokens.
+ *
+ * Discards leading and trailing ' ' or '\t'.
+ *
+ * Expects string to have been preprocessed, if required, to ensure that any
+ * unwanted control characters have been removed. This code only recognises
+ * '\t'.
+ *
+ * Anything between '....' is ignored by the tokenizer. NB: this follows the
+ * shell convention, so '\' is also ignored and there is no way to include "'"
+ * in a single quoted string.
+ *
+ * Anything immediately preceded by '\' is ignored by the tokenizer. This
+ * includes blanks and quotes.
+ *
+ * Anything inside "...." is ignored by the tokenizer, including '\"' escapes.
+ *
+ * Unbalanced "'" or '"' are treated as if eol was a "'" or '"'.
+ *
+ * Of the things which are not ignored by the tokenizer:
+ *
+ * * tokens are separated by whitespace -- one ' ' or '\t' characters
+ * The whitespace is discarded.
+ *
+ * * tokens are separated by "separators", which start with any of:
+ *
+ * '!', '#', '<', '>' and '|'
+ *
+ * which may be followed by one or more characters to form a separator
+ * token.
+ *
+ * - from '!' or '#' to end of line is a comment token.
+ *
+ * - '<' opt and '<|' opt are separators, where opt is any combination
+ * of '+', '*' and '-'.
+ *
+ * - '>', '>>' and '|' are separators.
+ *
+ * NB: the tokenization mimics the standard shell which makes the piping stuff
+ * straightforward. It's also well known. Apart from the "'" rule, it
+ * also seems fine !
+ *
+ * NB: any control characters other than those spotted by isspace() are accepted
+ * as part of the current token !
+ *
+ * The tokens returned contain all the original characters of the line, except
+ * for the removal of '\t' between tokens.
+ *
+ * Returns: the types of all tokens or'd together.
+ * returns cmd_tok_null if the line is empty (apart from ' ' and '\t')
+ * or if the pointer was NULL.
+ *
+ * Note: all the tokens in the vector have at least one character, and no
+ * entries are NULL.
+ *
+ * NB: it is the callers responsibility to release the token objects in due
+ * course.
+ */
+extern cmd_token_type_t
+cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
+{
+ const char *cp, *ep ;
+ cmd_token_type_t total ;
+
+ cmd_empty_token_vector(parsed->tokens) ; /* Empty the token vector */
+
+ parsed->line = line ;
+ parsed->onode = parsed->cnode = node ;
+
+ total = cmd_tok_null ; /* nothing yet */
+
+ if (line == NULL) /* tolerate NULL */
+ return total ;
+
+ cp = line ;
+ ep = cp + strlen(cp) ;
+
+ while (cp < ep) /* process to end */
+ {
+ const char* sp ;
+ bool end ;
+ cmd_token_type_t type ;
+
+ if ((*cp == ' ') || (*cp == '\t'))
+ {
+ /* skip white-space */
+ do { ++cp ; } while ((*cp == ' ') || (*cp == '\t')) ;
+
+ if (cp == ep)
+ {
+ if (total != cmd_tok_null)
+ total |= cmd_tok_trailing ;
+ break ;
+ } ;
+ } ;
+
+ sp = cp ;
+ end = false ;
+ type = cmd_tok_simple ;
+ do
+ {
+ switch (*cp)
+ {
+ case '\t': /* whitespace at end of token */
+ case ' ':
+ end = true ;
+ break ;
+
+ case '\'': /* proceed to matching '\'' or end */
+ type |= cmd_tok_sq ;
+ ++cp ;
+ while (cp < ep)
+ {
+ if (*cp++ == '\'')
+ break ;
+ } ;
+ break ;
+
+ case '\\': /* step past escaped character, if any */
+ type |= cmd_tok_esc ;
+ ++cp ;
+ if (cp < ep)
+ ++cp ;
+ break ;
+
+ case '"': /* proceed to matching '"' or end... */
+ type |= cmd_tok_dq ;
+ ++cp ;
+ while (cp < ep) /* NB: do not register '\\' separately */
+ {
+ if (*cp++ == '"')
+ if (*(cp - 2) != '\\') /* ignore escaped '"' */
+ break ;
+ } ;
+ break ;
+
+ case '>': /* '>' or '>>' separators. */
+ end = true ;
+ if (cp == sp) /* if at start of token */
+ {
+ type = cmd_tok_pipe_out ;
+ ++cp ;
+ if ((cp < ep) && (*cp == '>'))
+ ++cp ;
+ } ;
+ break ;
+
+ case '|': /* '|' separator. */
+ end = true ;
+ if (cp == sp)
+ type = cmd_tok_pipe_out ;
+ ++cp ;
+ break ;
+
+ case '<': /* '<' or '<|' separators. */
+ end = true ;
+ if (cp == sp)
+ {
+ type = cmd_tok_pipe_in ;
+ ++cp ;
+ if ((cp < ep) && (*cp == '|'))
+ ++cp ;
+ if ( (cp < ep) &&
+ ((*cp == '+') || (*cp == '-') || (*cp == '*')) )
+ ++cp ;
+ } ;
+ break ;
+
+ case '!': /* '!' and '#' separators. */
+ case '#':
+ end = true ;
+ if (cp == sp)
+ {
+ type = cmd_tok_comment ;
+ cp = ep ;
+ } ;
+ break ;
+
+ default:
+ ++cp ;
+ break ;
+ } ;
+ } while (!end && (cp < ep)) ;
+
+ cmd_token_push(parsed->tokens,
+ cmd_token_new(type, sp, cp - sp, sp - line)) ;
+ } ;
+
+ return total ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process token to remove quotes and escapes (if any).
+ *
+ * Returns: true <=> OK
+ * false => invalid escape or incomplete quotes
+ *
+ * NB: if fails, returns token completed as far as possible.
+ */
+extern bool
+cmd_token_do_complete(token t)
+{
+ char *s, *p, *q ;
+ char ch ;
+ bool ok = true ;
+ bool dq = false ;
+
+ p = s = cmd_token_value(t) ;
+ q = p ;
+ while (*p != '\0')
+ {
+ switch (*p)
+ {
+ case '\'':
+ ++p ; /* skip leading '\'' */
+ while (1)
+ {
+ if (*p == '\0')
+ {
+ ok = false ; /* broken '...' */
+ break ;
+ } ;
+
+ if (*p == '\'')
+ {
+ ++p ; /* skip trailing '\'' */
+ break ; /* done '....' */
+ } ;
+
+ *q++ = *p++ ;
+ } ;
+ break ;
+
+ case '"':
+ ++p ; /* skip '"' */
+ dq = !dq ;
+ break ;
+
+ case '\\':
+ ++p ; /* step past '\\' */
+ ch = cmd_token_escape(*p) ;
+ if (ch == '\0')
+ {
+ ok = false ;
+ ch = *p ;
+ if (ch == '\0')
+ ch = '\\' ; /* \ at end is kept */
+ else
+ *q++ = '\\' ; /* otherwise keep \x */
+ } ;
+ *q++ = ch ;
+ break ;
+
+ default:
+ *q++ = *p++ ;
+ } ;
+ } ;
+
+ qs_term_here(t->qs, q) ;
+
+ return ok && !dq ;
+}
+
+/*------------------------------------------------------------------------------
+ * Return escaped value of \e.
+ *
+ * Everything except '0'..'9', 'A'..'Z', 'a'..'z' can be escaped -- these are
+ * reserved for future actual escapes !
+ *
+ * Returns '\0' if e == '\0' or if \e is an invalid escape.
+ */
+static char
+cmd_token_escape(char e)
+{
+ if ((e < '0') || (e > 'z'))
+ return e ;
+ return isalpha(e) ? '\0' : e ;
+} ;
+
+/*==============================================================================
+ * Parser object
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new cmd_parsed object, allocating if required
+ */
+extern cmd_parsed
+cmd_parse_init_new(cmd_parsed parsed)
+{
+ if (parsed == NULL)
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
+ else
+ memset(parsed, 0, sizeof(*parsed)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * cmd = NULL -- no command parsed, yet
+ * cnode -- no node set, yet
+ *
+ * do_shortcut -- false
+ * onode -- not material (do_shortcut is false)
+ *
+ * pipes = 0 -- cmd_pipe_none
+ */
+ confirm(cmd_pipe_none == 0) ;
+
+ cmd_token_vector_init(parsed->tokens) ;
+ cmd_token_vector_init(parsed->read_pipe_tokens) ;
+ cmd_token_vector_init(parsed->write_pipe_tokens) ;
+ cmd_arg_vector_init(parsed) ;
+
+ return parsed ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty out and (if required) free a cmd_parsed object
+ */
+extern cmd_parsed
+cmd_parse_reset(cmd_parsed parsed, bool free_structure)
+{
+ if (parsed != NULL)
+ {
+ cmd_empty_parsed_tokens(parsed) ; /* give back tokens */
+ cmd_arg_vector_free(parsed) ; /* give back vector body */
+
+ if (free_structure)
+ XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ else
+ cmd_parse_init_new(parsed) ;
+ } ;
+
+ return parsed ;
+} ;
+
+/*==============================================================================
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
+ */
+
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address:
+ *
+ * 999.999.999.999 -- where no part may be > 255
+ *
+ * TODO: cmd_ipv4_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
+extern match_type_t
+cmd_ipv4_match (const char *str)
+{
+ const char *sp;
+ int dots = 0, nums = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;)
+ {
+ memset (buf, 0, sizeof (buf));
+ sp = str;
+ while (*str != '\0')
+ {
+ if (*str == '.')
+ {
+ if (dots >= 3)
+ return no_match;
+
+ if (*(str + 1) == '.')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+ if (!isdigit ((int) *str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy (buf, sp, str - sp);
+ if (atoi (buf) > 255)
+ return no_match;
+
+ nums++;
+
+ if (*str == '\0')
+ break;
+
+ str++;
+ }
+
+ if (nums < 4)
+ return partly_match;
+
+ return exact_match;
+}
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Prefix:
+ *
+ * 999.999.999.999/99 -- where no part may be > 255,
+ * and prefix length may not be > 32
+ *
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+extern match_type_t
+cmd_ipv4_prefix_match (const char *str)
+{
+ const char *sp;
+ int dots = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;)
+ {
+ memset (buf, 0, sizeof (buf));
+ sp = str;
+ while (*str != '\0' && *str != '/')
+ {
+ if (*str == '.')
+ {
+ if (dots == 3)
+ return no_match;
+
+ if (*(str + 1) == '.' || *(str + 1) == '/')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+
+ if (!isdigit ((int) *str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy (buf, sp, str - sp);
+ if (atoi (buf) > 255)
+ return no_match;
+
+ if (dots == 3)
+ {
+ if (*str == '/')
+ {
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ str++;
+ break;
+ }
+ else if (*str == '\0')
+ return partly_match;
+ }
+
+ if (*str == '\0')
+ return partly_match;
+
+ str++;
+ }
+
+ sp = str;
+ while (*str != '\0')
+ {
+ if (!isdigit ((int) *str))
+ return no_match;
+
+ str++;
+ }
+
+ if (atoi (sp) > 32)
+ return no_match;
+
+ return exact_match;
+}
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address:
+ *
+ * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
+
+#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
+#define STATE_START 1
+#define STATE_COLON 2
+#define STATE_DOUBLE 3
+#define STATE_ADDR 4
+#define STATE_DOT 5
+#define STATE_SLASH 6
+#define STATE_MASK 7
+
+#ifdef HAVE_IPV6
+
+extern match_type_t
+cmd_ipv6_match (const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ const char *sp = NULL;
+ struct sockaddr_in6 sin6_dummy;
+ int ret;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn (str, IPV6_ADDR_STR) != strlen (str))
+ return no_match;
+
+ /* use inet_pton that has a better support,
+ * for example inet_pton can support the automatic addresses:
+ * ::1.2.3.4
+ */
+ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+
+ if (ret == 1)
+ return exact_match;
+
+ while (*str != '\0')
+ {
+ switch (state)
+ {
+ case STATE_START:
+ if (*str == ':')
+ {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ }
+ else
+ {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else
+ {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else
+ {
+ if (*(str + 1) != '\0')
+ colons++;
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums++;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '\0')
+ {
+ if (str - sp > 3)
+ return no_match;
+
+ nums++;
+ state = STATE_COLON;
+ }
+ if (*(str + 1) == '.')
+ state = STATE_DOT;
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 8)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+#if 0
+ if (nums < 11)
+ return partly_match;
+#endif /* 0 */
+
+ return exact_match;
+}
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Prefix:
+ *
+ * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
+ * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+extern match_type_t
+cmd_ipv6_prefix_match (const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ int mask;
+ const char *sp = NULL;
+ char *endptr = NULL;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
+ return no_match;
+
+ while (*str != '\0' && state != STATE_MASK)
+ {
+ switch (state)
+ {
+ case STATE_START:
+ if (*str == ':')
+ {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ }
+ else
+ {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == '/')
+ return no_match;
+ else if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else
+ {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else
+ {
+ if (*(str + 1) != '\0' && *(str + 1) != '/')
+ colons++;
+ sp = str + 1;
+
+ if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ else
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums += 1;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '.'
+ || *(str + 1) == '\0' || *(str + 1) == '/')
+ {
+ if (str - sp > 3)
+ return no_match;
+
+ for (; sp <= str; sp++)
+ if (*sp == '/')
+ return no_match;
+
+ nums++;
+
+ if (*(str + 1) == ':')
+ state = STATE_COLON;
+ else if (*(str + 1) == '.')
+ state = STATE_DOT;
+ else if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ }
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ case STATE_SLASH:
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ state = STATE_MASK;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 11)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+ if (state < STATE_MASK)
+ return partly_match;
+
+ mask = strtol (str, &endptr, 10);
+ if (*endptr != '\0')
+ return no_match;
+
+ if (mask < 0 || mask > 128)
+ return no_match;
+
+/* I don't know why mask < 13 makes command match partly.
+ Forgive me to make this comments. I Want to set static default route
+ because of lack of function to originate default in ospf6d; sorry
+ yasu
+ if (mask < 13)
+ return partly_match;
+*/
+
+ return exact_match;
+}
+
+#endif /* HAVE_IPV6 */
+
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: true <=> OK -- *including* empty string
+ * false => not a valid number, or not in required range
+ * (or invalid range !!)
+ */
+
+#define DECIMAL_STRLEN_MAX 10
+
+extern bool
+cmd_range_match (const char *range, const char *str)
+{
+ char *p;
+ char buf[DECIMAL_STRLEN_MAX + 1];
+ char *endptr = NULL;
+ unsigned long min, max, val;
+
+ if (str == NULL)
+ return true ;
+
+ val = strtoul (str, &endptr, 10);
+ if (*endptr != '\0')
+ return false ;
+
+ range++;
+ p = strchr (range, '-');
+ if (p == NULL)
+ return false ;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return false ;
+ strncpy (buf, range, p - range);
+ buf[p - range] = '\0';
+ min = strtoul (buf, &endptr, 10);
+ if (*endptr != '\0')
+ return false ;
+
+ range = p + 1;
+ p = strchr (range, '>');
+ if (p == NULL)
+ return false ;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return false ;
+ strncpy (buf, range, p - range);
+ buf[p - range] = '\0';
+ max = strtoul (buf, &endptr, 10);
+ if (*endptr != '\0')
+ return false ;
+
+ if (val < min || val > max)
+ return false ;
+
+ return true ;
+}
+
+
diff --git a/lib/command_parse.h b/lib/command_parse.h
new file mode 100644
index 00000000..ab91e7f4
--- /dev/null
+++ b/lib/command_parse.h
@@ -0,0 +1,478 @@
+/* Quagga command line parsing -- header
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_PARSE_H
+#define _ZEBRA_COMMAND_PARSE_H
+
+#include <zebra.h>
+#include "misc.h"
+
+#include "node_type.h"
+#include "vector.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Parsing of tokens
+ */
+
+enum cmd_token_spec
+{
+ cmd_ts_simple = 0,
+
+ cmd_ts_keyword,
+ cmd_ts_number,
+ cmd_ts_word,
+ cmd_ts_option,
+
+ cmd_ts_ipv4_addr,
+ cmd_ts_ipv4_prefix,
+
+ cmd_ts_ipv6_addr,
+ cmd_ts_ipv6_prefix,
+
+ cmd_ts_vararg,
+
+
+
+} ;
+typedef enum cmd_token_spec cmd_token_spec_t ;
+
+/*==============================================================================
+ * Completion match types.
+ *
+ * NB: the order of these is significant -- in particular as confirmed below.
+ */
+enum match_type
+{
+ no_match = 0, /* nope */
+ any_match = 1,
+
+ extend_match = any_match,
+
+ ipv4_prefix_match,
+ ipv4_match,
+ ipv6_prefix_match,
+ ipv6_match,
+ range_match,
+ vararg_match,
+
+ partly_match, /* OK as far as it went */
+ exact_match, /* Syntactically complete -- greatest match */
+
+ match_type_count /* Number of match types */
+} ;
+typedef enum match_type match_type_t ;
+
+CONFIRM(no_match == false) ;
+CONFIRM(extend_match == (no_match + 1)) ;
+CONFIRM(partly_match == (exact_match - 1)) ;
+CONFIRM(exact_match == (match_type_count - 1)) ;
+
+/*==============================================================================
+ *
+ */
+
+/* Command parsing options */
+enum cmd_parse_type /* bit significant */
+{
+ cmd_parse_completion = 0,
+ cmd_parse_strict = BIT(0),
+
+ cmd_parse_do = BIT(1),
+ cmd_parse_tree = BIT(2),
+} ;
+typedef enum cmd_parse_type cmd_parse_type_t ;
+
+/* Pipe types */
+enum cmd_pipe_type /* bit significant */
+{
+ cmd_pipe_none = 0,
+
+ cmd_pipe_in_file = BIT(0),
+ cmd_pipe_in_shell = BIT(1),
+
+ cmd_pipe_reflect = BIT(4),
+ cmd_pipe_output = BIT(5),
+ cmd_pipe_more = BIT(6),
+
+ cmd_pipe_out_file = BIT( 8),
+ cmd_pipe_out_file_append = BIT( 9),
+ cmd_pipe_out_shell = BIT(10),
+} ;
+typedef enum cmd_pipe_type cmd_pipe_type_t ;
+
+/*------------------------------------------------------------------------------
+ * Token object -- a qstring and some other properties.
+ */
+enum cmd_token_type /* *bit* significant */
+{
+ cmd_tok_null = 0, /* used for empty lines */
+
+ cmd_tok_simple = BIT( 0),
+ cmd_tok_trailing = BIT( 1),
+
+ cmd_tok_sq = BIT( 8),
+ cmd_tok_dq = BIT( 9), /* '\\' within "..." are not
+ registered separately. */
+ cmd_tok_esc = BIT(10),
+
+ cmd_tok_incomplete = (cmd_tok_sq | cmd_tok_dq | cmd_tok_esc),
+
+ cmd_tok_pipe_in = BIT(12),
+ cmd_tok_pipe_out = BIT(13),
+ cmd_tok_comment = BIT(14),
+} ;
+typedef enum cmd_token_type cmd_token_type_t ;
+
+struct token
+{
+ cmd_token_type_t type ;
+ qstring_t qs ;
+ size_t tp ;
+} ;
+typedef struct token token_t[1] ;
+typedef struct token* token ;
+
+/*------------------------------------------------------------------------------
+ * Token vector -- a vector of token objects
+ */
+struct token_vector
+{
+ vector_t body ;
+} ;
+
+typedef struct token_vector token_vector_t[1] ;
+typedef struct token_vector* token_vector ;
+
+/*------------------------------------------------------------------------------
+ * Argument vector -- a vector of const char*
+ */
+struct arg_vector
+{
+ vector_t body ;
+} ;
+typedef struct arg_vector arg_vector_t[1] ;
+typedef struct arg_vector* arg_vector ;
+
+/*------------------------------------------------------------------------------
+ * Parsed command line
+ *
+ * The current command line is only valid while command is being parsed and
+ * executed -- in between it is nonsense. The pointer can be overwritten at
+ * any time -- responsibility for the memory lies elsewhere.
+ *
+ * The args vector is a set of pointers to the strings in the relevant tokens.
+ * This vector is only valid while command is being parsed and executed -- in
+ * between it too is nonsense. The pointers can be overwritten or discarded at
+ * any time -- responsibility for the memory lies elsewhere.
+ *
+ * The token vectors contain the tokens for the current command being parsed
+ * and executed. After the command has completed these vectors can be
+ * emptied -- see cmd_empty_parsed_tokens() -- but the next command to be
+ * parsed will tidy up before proceeding.
+ */
+typedef struct cmd_parsed* cmd_parsed ;
+struct cmd_parsed
+{
+ struct cmd_element *cmd ; /* NULL if empty command
+ or fails to parse */
+
+ const char* line ; /* the current line */
+
+ enum node_type cnode ; /* node command is in */
+ enum node_type onode ; /* node the parser started in */
+
+ bool do_shortcut ; /* true => is "do" command */
+
+ token_vector_t tokens ; /* vector of token objects */
+ arg_vector_t args ; /* vector of arguments */
+
+ cmd_pipe_type_t pipes ; /* if any */
+
+ token_vector_t read_pipe_tokens ;
+ token_vector_t write_pipe_tokens ;
+} ;
+
+/* Command dispatch options */
+enum {
+ cmd_no_queue = true,
+ cmd_may_queue = false,
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector of spare token objects -- declared here to allow inlines, defined
+ * in command_parse.c
+ */
+extern token_vector_t spare_tokens ;
+
+/*==============================================================================
+ * Prototypes
+ */
+extern void cmd_spare_tokens_init(void) ;
+extern void cmd_spare_tokens_free(void) ;
+
+extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
+extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
+
+extern cmd_token_type_t cmd_tokenise(cmd_parsed parsed, const char *line,
+ node_type_t node) ;
+Inline const char* cmd_token_string(token t) ;
+Inline int cmd_token_count(token_vector tv) ;
+Inline token cmd_token_get(token_vector tv, vector_index_t i) ;
+Inline token cmd_token_pop(token_vector tv) ;
+Inline void cmd_token_push(token_vector tv, token t) ;
+Inline token cmd_token_shift(token_vector tv) ;
+Inline void cmd_token_unshift(token_vector tv, token t) ;
+Inline token cmd_token_make(void) ;
+Inline token cmd_token_new(cmd_token_type_t type, const char* p,
+ size_t len, size_t tp) ;
+
+Inline void cmd_empty_token_vector(token_vector tv) ;
+Inline void cmd_empty_parsed_tokens(cmd_parsed parsed) ;
+
+Inline bool cmd_token_complete(token t) ;
+extern bool cmd_token_do_complete(token t) ;
+
+extern match_type_t cmd_ipv4_match (const char *str) ;
+extern match_type_t cmd_ipv4_prefix_match (const char *str) ;
+#if HAVE_IPV6
+extern match_type_t cmd_ipv6_match (const char *str) ;
+extern match_type_t cmd_ipv6_prefix_match (const char *str) ;
+#endif
+extern bool cmd_range_match (const char *range, const char *str) ;
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Get pointer to token value.
+ *
+ * Returns NULL if token NULL or no string.
+ */
+Inline char*
+cmd_token_value(token t)
+{
+ return (t == NULL) ? NULL : qs_chars(t->qs) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get string value of given token.
+ *
+ * Returns an empty (not NULL) string if token NULL or no string.
+ */
+Inline const char*
+cmd_token_string(token t)
+{
+ const char* s = cmd_token_value(t) ;
+ return (s == NULL) ? "" : s ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get number of tokens in the given token vector
+ */
+Inline int
+cmd_token_count(token_vector tv)
+{
+ return vector_length(tv->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get i'th token from given token vector -- zero origin
+ */
+Inline token
+cmd_token_get(token_vector tv, vector_index_t i)
+{
+ return vector_get_item(tv->body, i) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop token from end of given token vector -- if any.
+ */
+Inline token
+cmd_token_pop(token_vector tv)
+{
+ return vector_pop_item(tv->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push token onto end of given token vector.
+ */
+Inline void
+cmd_token_push(token_vector tv, token t)
+{
+ vector_push_item(tv->body, t) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shift first token off front of given token vector -- if any.
+ */
+Inline token
+cmd_token_shift(token_vector tv)
+{
+ return vector_shift_item(tv->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unshift token onto front of given token vector.
+ */
+Inline void
+cmd_token_unshift(token_vector tv, token t)
+{
+ vector_unshift_item(tv->body, t) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a brand new token object
+ */
+Inline token
+cmd_token_make(void)
+{
+ return XCALLOC(MTYPE_TOKEN, sizeof(struct token)) ;
+
+ /* Zeroising the new structure sets:
+ *
+ * type = 0 -- cmd_tok_null
+ * qs = zeroised qstring -- empty string
+ */
+ confirm(cmd_tok_null == 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new 'cmd_tok_simple' token from given characters + length
+ */
+Inline token
+cmd_token_new(cmd_token_type_t type, const char* p, size_t len, size_t tp)
+{
+ token t ;
+
+ t = cmd_token_pop(spare_tokens) ;
+ if (t == NULL)
+ t = cmd_token_make() ;
+
+ t->type = type ;
+ qs_set_n(&t->qs, p, len) ;
+ t->tp = tp ;
+
+ return t ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Discard given token -- give back to spare tokens list
+ */
+Inline void
+cmd_token_discard(token t)
+{
+ if (t != NULL)
+ vector_push_item(spare_tokens->body, t) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Release contents of token vector -- move to the spare_token_strings.
+ */
+Inline void
+cmd_empty_token_vector(token_vector tv)
+{
+ if (cmd_token_count(tv) != 0)
+ vector_move_append(spare_tokens->body, tv->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Release contents of all token vectors in given parsed object.
+ */
+Inline void
+cmd_empty_parsed_tokens(cmd_parsed parsed)
+{
+ cmd_empty_token_vector(parsed->tokens) ;
+ cmd_empty_token_vector(parsed->read_pipe_tokens) ;
+ cmd_empty_token_vector(parsed->write_pipe_tokens) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If token is incomplete (contains quotes or escapes) process those down.
+ *
+ * Returns: true <=> OK
+ * false => invalid escape
+ */
+Inline bool
+cmd_token_complete(token t)
+{
+ return ((t->type & cmd_tok_incomplete) == 0) ? true
+ : cmd_token_do_complete(t) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise arg_vector object in cmd_parsed.
+ */
+Inline void
+cmd_arg_vector_init(cmd_parsed parsed)
+{
+ vector_init_new(parsed->args->body, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the body of the arg_vector object in cmd_parsed.
+ */
+Inline void
+cmd_arg_vector_free(cmd_parsed parsed)
+{
+ vector_reset(parsed->args->body, keep_it) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty the body of the arg_vector object in cmd_parsed.
+ */
+Inline void
+cmd_arg_vector_empty(cmd_parsed parsed)
+{
+ vector_set_length(parsed->args->body, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty the body of the arg_vector object in cmd_parsed.
+ */
+Inline void
+cmd_arg_vector_push(cmd_parsed parsed, char* arg)
+{
+ vector_push_item(parsed->args->body, arg) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the body of the argument vector.
+ */
+Inline const char * const*
+cmd_arg_vector_argv(cmd_parsed parsed)
+{
+ return (const char* const*)vector_body(parsed->args->body) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get length of the body of the argument vector.
+ */
+Inline unsigned
+cmd_arg_vector_argc(cmd_parsed parsed)
+{
+ return vector_length(parsed->args->body) ;
+} ;
+
+#endif /* _ZEBRA_COMMAND_PARSE_H */
diff --git a/lib/elstring.h b/lib/elstring.h
new file mode 100644
index 00000000..051e5962
--- /dev/null
+++ b/lib/elstring.h
@@ -0,0 +1,354 @@
+/* Length/String string handling -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_ELSTRING_H
+#define _ZEBRA_ELSTRING_H
+
+#include "misc.h"
+#include "zassert.h"
+#include "memory.h"
+
+/*==============================================================================
+ * This is some very simple support for strings which are Length/Body
+ * objects.
+ *
+ * NB: this object knows NOTHING about memory allocation etc. The objective
+ * is the simplest possible encapsulation of strings which are NOT '\0'
+ * terminated.
+ *
+ *
+ *
+ */
+struct elstring
+{
+ union
+ {
+ void* v ; /* may be NULL iff len == 0 */
+ const void* cv ;
+ } body ;
+
+ ulen len ;
+ bool term ; /* true <=> body is '\0' terminated */
+} ;
+
+typedef struct elstring elstring_t[1] ;
+typedef struct elstring* elstring ;
+
+/* Setting an elstring object to all zeros is enough to initialise it to
+ * an empty string.
+ */
+enum
+{
+ ELSTRING_INIT_ALL_ZEROS = true
+} ;
+
+/*------------------------------------------------------------------------------
+ * Various forms of body -- NB:
+ */
+
+Inline void*
+els_body_nn(elstring els)
+{
+ return els->body.v ;
+} ;
+
+Inline void*
+els_body(elstring els)
+{
+ return (els != NULL) ? els_body_nn(els) : NULL ;
+} ;
+
+Inline ulen
+els_len_nn(elstring els)
+{
+ return els->len ;
+} ;
+
+Inline ulen
+els_len(elstring els)
+{
+ return (els != NULL) ? els_len_nn(els) : 0 ;
+} ;
+
+Inline bool
+els_term_nn(elstring els)
+{
+ return els->term ;
+} ;
+
+Inline bool
+els_term(elstring els)
+{
+ return (els != NULL) ? els_term_nn(els) : false ;
+} ;
+
+/*==============================================================================
+ * All so simple that everything is implemented as Inline
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise or create a new elstring
+ */
+Inline elstring
+els_init_new(elstring els)
+{
+ if (els == NULL)
+ els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ else
+ memset(els, 0, sizeof(elstring_t)) ;
+
+ confirm(ELSTRING_INIT_ALL_ZEROS) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from ordinary string.
+ *
+ * NB: elstring MUST NOT be NULL.
+ *
+ * NB: treats str == NULL as a zero length string.
+ *
+ * NB: sets "term" unless str == NULL.
+ */
+Inline void
+els_set_nn(elstring els, const void* str)
+{
+ els->body.cv = str ;
+ els->len = (str != NULL) ? strlen(str) : 0 ;
+ els->term = (str != NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from ordinary string.
+ *
+ * Creates elstring object if required (ie if els == NULL).
+ *
+ * See: els_set_nn.
+ */
+Inline elstring
+els_set(elstring els, const void* str)
+{
+ if (els == NULL)
+ els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+
+ els_set_nn(els, str) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from body + length.
+ *
+ * NB: sets term = false.
+ *
+ * NB: elstring MUST NOT be NULL.
+ *
+ * NB: treats str == NULL as a zero length string.
+ */
+Inline void
+els_set_n_nn(elstring els, const void* body, ulen len)
+{
+ els->body.cv = body ;
+ els->len = len ;
+ els->term = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring value from body + length.
+ *
+ * Creates elstring object if required (ie if els == NULL).
+ *
+ * See els_set_n_nn.
+ */
+Inline elstring
+els_set_n(elstring els, const void* body, ulen len)
+{
+ if (els == NULL)
+ els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+
+ els_set_n_nn(els, body, len) ;
+
+ return els ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear contents of an elstring (if any)
+ *
+ * NB: it is the callers responsibility to free the contents of the elstring.
+ * if that is required, before freeing the elstring itself.
+ */
+Inline void
+els_clear(elstring els)
+{
+ if (els != NULL)
+ {
+ els->body.v = NULL ;
+ els->len = 0 ;
+ els->term = false ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Release dynamically allocated elstring.
+ *
+ * Returns NULL.
+ *
+ * NB: it is the callers responsibility to free the contents of the elstring.
+ * if that is required, before freeing the elstring itself.
+ */
+Inline elstring
+els_free(elstring els)
+{
+ if (els != NULL)
+ XFREE(MTYPE_TMP, els) ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring length. And set term false.
+ *
+ * NB: it is the caller's responsibility to set a valid length !!
+ *
+ * NB: elstring MUST NOT be NULL.
+ */
+Inline void
+els_set_len_nn(elstring els, ulen len)
+{
+ els->len = len ;
+ els->term = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring body. And set term false.
+ *
+ * NB: it is the caller's responsibility to set a valid body !!
+ *
+ * NB: elstring MUST NOT be NULL.
+ */
+Inline void
+els_set_body_nn(elstring els, const void* body)
+{
+ els->body.cv = body ;
+ els->term = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set elstring terminated.
+ *
+ * NB: it is the caller's responsibility to set a valid body !!
+ *
+ * NB: elstring MUST NOT be NULL.
+ */
+Inline void
+els_set_term_nn(elstring els, bool term)
+{
+ els->term = term ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
+ */
+Inline int
+els_cmp(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen al, bl ;
+ ulen n ;
+
+ ap = els_body(a) ;
+ bp = els_body(b) ;
+ al = els_len(a) ;
+ bl = els_len(b) ;
+
+ n = (al <= bl) ? al : bl ;
+
+ while (n)
+ {
+ int d = *ap++ - *bp++ ;
+ if (d != 0)
+ return d ;
+ --n ;
+ } ;
+
+ return al < bl ? -1 : (al == bl) ? 0 : +1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Are two elstrings equal ? -- returns true if strings equal.
+ */
+Inline bool
+els_equal(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen n ;
+
+ n = els_len(b) ;
+ if (n != els_len(a))
+ return false ;
+
+ ap = els_body(a) ;
+ bp = els_body(b) ;
+
+ while (n)
+ {
+ if (*ap++ != *bp++)
+ return false ;
+ --n ;
+ } ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is 'b' a leading substring of 'a' ? -- returns true if it is.
+ *
+ * If 'b' is empty it is always a leading substring.
+ */
+Inline int
+els_substring(elstring a, elstring b)
+{
+ const uchar* ap ;
+ const uchar* bp ;
+ ulen n ;
+
+ n = els_len(b) ;
+ if (n > els_len(a))
+ return false ;
+
+ ap = els_body(a) ;
+ bp = els_body(b) ;
+
+ while (n)
+ {
+ if (*ap++ != *bp++)
+ return false ;
+ --n ;
+ } ;
+
+ return true ;
+} ;
+
+#endif /* _ZEBRA_ELSTRING_H */
+
diff --git a/lib/heap.c b/lib/heap.c
index 3a7ed360..7821a9c9 100644
--- a/lib/heap.c
+++ b/lib/heap.c
@@ -79,7 +79,7 @@
*/
static heap
-heap_setup(heap h, int new_vector, vector_index size, heap_cmp* cmp,
+heap_setup(heap h, int new_vector, vector_length_t size, heap_cmp* cmp,
int with_backlink, unsigned int backlink_offset) ;
/* Initialize heap -- allocating heap structure if required.
@@ -166,12 +166,13 @@ heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
* *before* doing this.
*/
heap
-heap_reset(heap h, int free_structure)
+heap_reset(heap h, free_keep_b free_structure)
{
- vector_reset_keep(&h->v) ; /* vector structure is embedded in the heap */
+ vector_reset(h->v, keep_it) ; /* vector structure is embedded in the heap */
+ confirm(free_it == true) ;
if (free_structure)
- XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
+ XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
return h ;
} ;
@@ -189,9 +190,9 @@ heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
h->backlink_offset = backlink_offset ;
if (new_vector)
- vector_init_new(&h->v, size) ;
+ vector_init_new(h->v, size) ;
else
- vector_re_init(&h->v, size) ;
+ vector_re_init(h->v, size) ;
return h ;
} ;
@@ -220,14 +221,14 @@ heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
* NB: items are reamed out in no defined order.
*/
p_vector_item
-heap_ream(heap h, int free_structure)
+heap_ream(heap h, free_keep_b free_structure)
{
p_vector_item p_v ;
if (h == NULL)
return NULL ;
- if ((p_v = vector_ream_keep(&h->v)) == NULL)
+ if ((p_v = vector_ream(h->v, keep_it)) == NULL)
heap_reset(h, free_structure) ;
return p_v ;
@@ -247,11 +248,11 @@ heap_pop_item(heap h)
p_vector_item p_v ;
p_vector_item p_x ;
- p_v = vector_pop_item(&h->v) ; /* extract last item, if any */
- if ((p_v == NULL) || (h->v.end == 0))
+ p_v = vector_pop_item(h->v) ; /* extract last item, if any */
+ if ((p_v == NULL) || (h->v->end == 0))
return p_v ; /* done if empty or last was also first */
- p_x = h->v.p_items[0] ; /* this is what we are popping */
+ p_x = h->v->p_items[0] ; /* this is what we are popping */
heap_bubble_down(h, 0, p_v) ; /* reposition what was the last item */
/* updating any backlink */
@@ -296,13 +297,13 @@ void
heap_delete_item(heap h, p_vector_item p_v)
{
p_vector_item p_x ;
- vector_index i ;
+ vector_index_t i ;
i = heap_find_item(h, p_v) ; /* index of item to be deleted */
- p_x = vector_pop_item(&h->v) ; /* extract last item, if any */
+ p_x = vector_pop_item(h->v) ; /* extract last item, if any */
- if (i < h->v.end) /* if not deleting the last item... */
+ if (i < h->v->end) /* if not deleting the last item... */
heap_bubble(h, i, p_x) ; /* ...reinsert what was last, at the delete */
/* position, updating any backlink */
} ;
@@ -322,25 +323,25 @@ heap_delete_item(heap h, p_vector_item p_v)
void
heap_push_vector(heap h, vector v, int move_vector)
{
- vector_index i = h->v.end ;
- vector_index e ;
- vector_index n = v->end ;
+ vector_index_t i = h->v->end ;
+ vector_index_t e ;
+ vector_length_t n = v->end ;
p_vector_item p_v ;
if (move_vector)
- vector_move_append(&h->v, v) ;
+ vector_move_append(h->v, v) ;
else
- vector_copy_append(&h->v, v) ;
+ vector_copy_append(h->v, v) ;
e = i ; /* old end of the heap. */
while (n--) {
- p_v = h->v.p_items[i++] ;
+ p_v = h->v->p_items[i++] ;
if (p_v != NULL)
heap_bubble_up(h, e++, p_v) ; /* move new item into position in heap */
/* setting any backlink */
} ;
- h->v.end = e ; /* new end of heap */
+ h->v->end = e ; /* new end of heap */
} ;
/* Pop given heap to vector -- creating vector if required (v == NULL).
@@ -363,8 +364,8 @@ heap_push_vector(heap h, vector v, int move_vector)
vector
heap_pop_vector(vector v, heap h, int move_heap)
{
- vector_index n = h->v.end ;
- vector_index i ;
+ vector_length_t n = h->v->end ;
+ vector_index_t i ;
v = vector_re_init(v, n) ; /* guarantees >= 'n' items in vector */
v->end = n ;
@@ -373,7 +374,7 @@ heap_pop_vector(vector v, heap h, int move_heap)
v->p_items[i] = heap_pop_item(h) ;
if (!move_heap)
- vector_copy_here(&h->v, v) ; /* fully sorted is also heap ordered ! */
+ vector_copy_here(h->v, v) ; /* fully sorted is also heap ordered ! */
return v ;
} ;
@@ -401,11 +402,11 @@ heap_pop_vector(vector v, heap h, int move_heap)
*
* Note that this sets the backlink on the given item.
*/
-private void
-heap_bubble(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble(heap h, vector_index_t i, p_vector_item p_v)
{
/* If this is < parent, we bubble upwards. */
- if ((i != 0) && (h->cmp(&p_v, &h->v.p_items[HEAP_UP(i)]) < 0))
+ if ((i != 0) && (h->cmp(&p_v, h->v->p_items[HEAP_UP(i)]) < 0))
heap_bubble_up(h, i, p_v) ;
/* Otherwise we try bubbling downwards. */
else
@@ -421,11 +422,11 @@ heap_bubble(heap h, vector_index i, p_vector_item p_v)
* v.end at all. So this can be used to work along a vector and bring
* items into heap order.
*/
-private void
-heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble_up(heap h, vector_index_t i, p_vector_item p_v)
{
- p_vector_item* ha = h->v.p_items ; /* underlying array */
- vector_index ip ; /* index of parent */
+ p_vector_item* ha = h->v->p_items ; /* underlying array */
+ vector_index_t ip ; /* index of parent */
p_vector_item p_p ; /* pointer to parent item */
dassert(ha != NULL) ;
@@ -453,16 +454,16 @@ heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
*
* Note that this sets the backlink on the given item.
*/
-private void
-heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
+Private void
+heap_bubble_down(heap h, vector_index_t i, p_vector_item p_v)
{
- vector_index e = h->v.end ; /* end of heap */
- vector_index ic ; /* index of child */
- vector_index is ; /* index of sibling */
- p_vector_item p_c ; /* pointer to child */
- p_vector_item p_s ; /* pointer to sibling */
+ vector_length_t e = h->v->end ; /* end of heap */
+ vector_index_t ic ; /* index of child */
+ vector_index_t is ; /* index of sibling */
+ p_vector_item p_c ; /* pointer to child */
+ p_vector_item p_s ; /* pointer to sibling */
- p_vector_item* ha = h->v.p_items ; /* underlying array */
+ p_vector_item* ha = h->v->p_items ; /* underlying array */
dassert(ha != NULL) ;
while (1)
@@ -497,21 +498,21 @@ heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
} ;
/* Find index of given item in the given heap. */
-private vector_index
+Private vector_index_t
heap_find_item(heap h, p_vector_item p_v)
{
- vector_index i ;
+ vector_index_t i ;
if (h->state & Heap_Has_Backlink)
i = HEAP_BACKLINK(h, p_v) ;
else
{
- for (i = 0 ; i < h->v.end ; ++i)
- if (h->v.p_items[i] == p_v)
+ for (i = 0 ; i < h->v->end ; ++i)
+ if (h->v->p_items[i] == p_v)
return i ;
} ;
- assert((i < h->v.end) && (h->v.p_items[i] == p_v)) ;
+ assert((i < h->v->end) && (h->v->p_items[i] == p_v)) ;
return i ;
} ;
diff --git a/lib/heap.h b/lib/heap.h
index bd984398..9f981c8d 100644
--- a/lib/heap.h
+++ b/lib/heap.h
@@ -17,13 +17,9 @@
#ifndef _ZEBRA_HEAP_H
#define _ZEBRA_HEAP_H
+#include "misc.h"
#include "vector.h"
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
-
/*==============================================================================
* Data structures etc.
*/
@@ -34,7 +30,7 @@ enum heap_state {
Heap_Has_Backlink = 0x01, /* Set if backlink set */
} ;
-typedef vector_index heap_backlink_t ;
+typedef vector_index_t heap_backlink_t ;
typedef struct heap* heap ;
@@ -45,7 +41,7 @@ struct heap
enum heap_state state ;
unsigned int backlink_offset ;
- struct vector v ;
+ vector_t v ;
} ;
/*==============================================================================
@@ -66,17 +62,8 @@ extern heap heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
#define heap_re_init_backlinked(h, size, cmp, offset) \
heap_re_init(h, size, cmp, 1, offset)
-extern heap heap_reset(heap h, int free_structure) ;
-extern p_vector_item heap_ream(heap h, int free_structure) ;
-
-/* Reset heap and free the heap structure. */
-#define heap_reset_free(h) heap_reset(h, 1)
-/* Reset heap but keep the heap structure. */
-#define heap_reset_keep(h) heap_reset(h, 0)
-/* Ream out heap and free the heap structure. */
-#define heap_ream_free(h) heap_ream(h, 1)
-/* Ream out heap but keep the heap structure. */
-#define heap_ream_keep(h) heap_ream(h, 0)
+extern heap heap_reset(heap h, free_keep_b free_structure) ;
+extern p_vector_item heap_ream(heap h, free_keep_b free_structure) ;
Inline void heap_push_item(heap h, p_vector_item p_v) ;
extern p_vector_item heap_pop_item(heap h) ;
@@ -102,20 +89,16 @@ extern vector heap_pop_vector(vector v, heap h, int move_heap) ;
* This are extern only for use in Inline and other friends
*/
-#ifndef private
- #define private extern
-#endif
-
-private void
-heap_bubble(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble(heap h, vector_index_t i, p_vector_item p_v) ;
-private void
-heap_bubble_up(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble_up(heap h, vector_index_t i, p_vector_item p_v) ;
-private void
-heap_bubble_down(heap h, vector_index i, p_vector_item p_v) ;
+Private void
+heap_bubble_down(heap h, vector_index_t i, p_vector_item p_v) ;
-private vector_index
+Private vector_index_t
heap_find_item(heap h, p_vector_item p_v) ;
/*==============================================================================
@@ -128,7 +111,7 @@ Inline void
heap_push_item(heap h, p_vector_item p_v)
{
dassert(p_v != NULL) ; /* no NULLs, thank you. */
- heap_bubble_up(h, vector_extend_by_1(&h->v), p_v) ;
+ heap_bubble_up(h, vector_extend_by_1(h->v), p_v) ;
} ;
/* Get copy of top heap item (does not pop).
@@ -138,7 +121,7 @@ heap_push_item(heap h, p_vector_item p_v)
Inline p_vector_item
heap_top_item(heap h)
{
- return vector_get_first_item(&h->v) ; /* if any */
+ return vector_get_first_item(h->v) ; /* if any */
} ;
/* Update heap to reflect new value of top item.
diff --git a/lib/keystroke.h b/lib/keystroke.h
index 2b1d4d93..10494df0 100644
--- a/lib/keystroke.h
+++ b/lib/keystroke.h
@@ -22,19 +22,13 @@
#ifndef _ZEBRA_KEYSTROKE_H
#define _ZEBRA_KEYSTROKE_H
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include <arpa/telnet.h>
#include "zassert.h"
#include "vio_fifo.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Keystroke buffering
*/
diff --git a/lib/list_util.c b/lib/list_util.c
index 720b8ca7..8ce01c03 100644
--- a/lib/list_util.c
+++ b/lib/list_util.c
@@ -19,8 +19,7 @@
* 02111-1307, USA.
*/
-#include <list_util.h>
-
+#include "list_util.h"
/*==============================================================================
* Single Base, Single Link
diff --git a/lib/list_util.h b/lib/list_util.h
index 876b7b11..bd796779 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -22,12 +22,7 @@
#ifndef _ZEBRA_LIST_UTIL_H
#define _ZEBRA_LIST_UTIL_H
-#include <stddef.h>
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "misc.h"
/*------------------------------------------------------------------------------
* Note that the following fell foul of "strict-aliasing":
@@ -47,7 +42,7 @@
* } ;
*
* the assignment to *p_base is, apparently, unacceptable. This works
- * perfectly well as am ordinary function. Using a GNUC extension it is
+ * perfectly well as an ordinary function. Using a GNUC extension it is
* possible to avoid the function call... hence the ugly skips.
*/
#ifdef __GNUC__
diff --git a/lib/log.h b/lib/log.h
index dda773ea..256d09a3 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -26,6 +26,7 @@
#define _ZEBRA_LOG_H
#include <syslog.h>
+#include "vargs.h"
#include "pthread_safe.h"
/* Here is some guidance on logging levels to use:
diff --git a/lib/memory.h b/lib/memory.h
index 6c95d73a..6cc95a5b 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -39,6 +39,8 @@ extern struct mlist mlists[];
#include "lib/memtypes.h"
+typedef enum MTYPE mtype_t ;
+
/* #define MEMORY_LOG */
#ifdef MEMORY_LOG
#define XMALLOC(mtype, size) \
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 98b53209..e5a591d6 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -15,6 +15,7 @@
struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
+ { MTYPE_STRING, "String (general)" },
{ MTYPE_STRVEC, "String vector" },
{ MTYPE_VECTOR, "Vector structure" },
{ MTYPE_VECTOR_BODY, "Vector body" },
@@ -44,6 +45,7 @@ struct memory_list memory_list_lib[] =
{ MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
{ MTYPE_CMD_PARSED, "Parsed command" },
+ { MTYPE_TOKEN, "Command token" },
{ MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
{ MTYPE_VTY_HIST, "VTY history" },
@@ -53,7 +55,6 @@ struct memory_list memory_list_lib[] =
{ MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO Lump" },
{ MTYPE_VIO_LC, "VTY IO Line Control" },
{ MTYPE_QSTRING, "qstring structure" },
- { MTYPE_QSTRING_BODY, "qstring body" },
{ MTYPE_QIOVEC, "qiovec structure" },
{ MTYPE_QIOVEC_VEC, "qiovec iovec vector" },
{ MTYPE_IF, "Interface" },
diff --git a/lib/misc.h b/lib/misc.h
new file mode 100644
index 00000000..9437b617
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,80 @@
+/* Miscellaneous basic definitions
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_MISC_H
+#define _ZEBRA_MISC_H
+
+/* Stuff which we generally expect to have */
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zassert.h"
+
+/* Bit number to bit mask */
+#define BIT(b) (1 << b)
+
+/* Just in case there are compiler issues */
+#define Inline static inline
+
+/* For things which have to be made extern -- typically because they are
+ * used by an inline function -- but are not for public consumption.
+ */
+#define Private extern
+
+/* Other names of true/false */
+enum on_off
+{
+ on = true,
+ off = false
+} ;
+typedef enum on_off on_off_b ;
+
+/* Whether to add or not on lookup. */
+enum add
+{
+ add = true,
+ no_add = false
+} ;
+typedef enum add add_b ;
+
+/* Used in object "reset" functions (destructors) */
+enum free_keep
+{
+ free_it = true,
+ keep_it = false
+} ;
+typedef enum free_keep free_keep_b ;
+
+/* We really want to be able to assume that an int is at least 32 bits */
+CONFIRM(UINT_MAX >= 0xFFFFFFFF) ;
+
+/* Some useful shorthand */
+typedef unsigned char byte ;
+typedef unsigned char uchar ;
+typedef unsigned int uint ;
+typedef unsigned int usize ;
+typedef unsigned int ulen ;
+
+#endif /* _ZEBRA_MISC_H */
diff --git a/lib/miyagi.h b/lib/miyagi.h
index 569d2da9..639b962d 100644
--- a/lib/miyagi.h
+++ b/lib/miyagi.h
@@ -17,9 +17,7 @@
#ifndef _ZEBRA_MIYAGI_H
#define _ZEBRA_MIYAGI_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "misc.h"
/*==============================================================================
* Ghastly kludge to discard "const" from pointer
diff --git a/lib/mqueue.h b/lib/mqueue.h
index a28b6606..68dceb15 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -22,16 +22,11 @@
#ifndef _ZEBRA_MQUEUE_H
#define _ZEBRA_MQUEUE_H
-#include <stddef.h>
-#include <stdbool.h>
+#include "misc.h"
#include "qpthreads.h"
#include "qtime.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Message Queue Blocks -- mqb
*
diff --git a/lib/node_type.h b/lib/node_type.h
index 7ec1107d..b1f26187 100644
--- a/lib/node_type.h
+++ b/lib/node_type.h
@@ -76,6 +76,7 @@ enum node_type
FORWARDING_NODE, /* IP forwarding node. */
PROTOCOL_NODE, /* protocol filtering node */
VTY_NODE, /* Vty node. */
-};
+} ;
+typedef enum node_type node_type_t ;
#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/plist.c b/lib/plist.c
index b2163e2b..c761c5c5 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -268,7 +268,7 @@ static void
prefix_master_reset(struct prefix_master * pm)
{
struct prefix_list* plist ;
- while ((plist = symbol_table_ream_keep(&(pm->table))))
+ while ((plist = symbol_table_ream(&(pm->table), keep_it)))
prefix_list_delete(plist) ;
pm->seqnum_flag = 1 ; /* Default is to generate sequence numbers. */
@@ -326,7 +326,8 @@ prefix_list_set_ref(prefix_list_ref* p_ref, afi_t afi, const char* name)
if (pm == NULL)
return NULL ;
- return *p_ref = symbol_set_ref(*p_ref, symbol_find(&(pm->table), name)) ;
+ return *p_ref = symbol_set_ref(*p_ref,
+ symbol_lookup(&(pm->table), name, add)) ;
} ;
/* Copy reference to prefix_list.
@@ -389,7 +390,7 @@ prefix_list_apply (struct prefix_list *plist, void *object)
struct prefix *p ;
int plen ;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
in_addr_t ip ;
#ifdef s6_addr32
@@ -575,7 +576,7 @@ prefix_list_delete (struct prefix_list* plist)
static struct prefix_list *
prefix_list_seek (struct prefix_master* pm, const char *name)
{
- return symbol_get_value(symbol_seek(&(pm->table), name)) ;
+ return symbol_get_value(symbol_lookup(&(pm->table), name, no_add)) ;
} ;
/* Lookup prefix_list by afi and name -- if afi is known, and name not NULL.
@@ -603,7 +604,7 @@ prefix_list_get (struct prefix_master* pm, const char *name, afi_t afi)
assert((pm != NULL) && (name != NULL)) ;
- sym = symbol_find(&(pm->table), name) ; /* creates if required */
+ sym = symbol_lookup(&(pm->table), name, add) ; /* creates if required */
plist = symbol_get_value(sym) ;
return plist ? plist : prefix_list_new(pm, sym, afi) ;
@@ -691,7 +692,7 @@ prefix_list_entry_ge_le_check(struct prefix_list_entry* pe, afi_t afi)
* result > 0 -- not found. index returned is of the entry with the largest
* sequence number smaller than the given one.
*/
-static vector_index
+static vector_index_t
prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
{
return vector_bsearch(&plist->list, (vector_bsearch_cmp*)prefix_seq_cmp,
@@ -725,7 +726,7 @@ prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result)
*
* Note that the cache is never empty.
*/
-static vector_index
+static vector_index_t
prefix_list_entry_lookup_val(struct prefix_list *plist,
struct prefix_list_entry* temp,
vector* cache,
@@ -753,7 +754,7 @@ prefix_list_entry_lookup_val(struct prefix_list *plist,
else
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
*cache = NULL ; /* Not found in cache. */
*result = 0 ; /* Assume found ! */
for (VECTOR_ITEMS(&plist->list, pe, i))
@@ -781,12 +782,12 @@ prefix_list_entry_lookup_val(struct prefix_list *plist,
* -- result != 0 not found -- index is immaterial
*
* */
-static vector_index
+static vector_index_t
prefix_list_entry_lookup (struct prefix_list* plist,
struct prefix_list_entry* pe_seek, int* result)
{
struct prefix_list_entry* pe_found ;
- vector_index i ;
+ vector_index_t i ;
if (pe_seek->flags & PREFIX_SEQ)
{
@@ -840,7 +841,7 @@ prefix_list_entry_insert(struct prefix_list *plist,
{
struct prefix_list_entry* pe ;
vector cache ;
- vector_index i, ic ;
+ vector_index_t i, ic ;
int ret, retc ;
u_int32_t mask ;
int pl, sh ;
@@ -901,7 +902,7 @@ prefix_list_entry_insert(struct prefix_list *plist,
if (cache)
{
/* We need to know where the old value was. */
- vector_index io ;
+ vector_index_t io ;
int reto ;
io = vector_bsearch(cache, (vector_bsearch_cmp*)plist->cmp, pe,
&reto) ;
@@ -1005,7 +1006,7 @@ prefix_list_entry_delete (struct prefix_list *plist,
struct prefix_list_entry *pe_seek)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
int ret ;
i = prefix_list_entry_lookup (plist, pe_seek, &ret) ;
@@ -1155,7 +1156,7 @@ static void __attribute__ ((unused))
prefix_list_print (struct prefix_list *plist)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
struct vty* vty = NULL ;
if (plist == NULL)
@@ -1461,7 +1462,7 @@ vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
if (dtype != summary_display)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
int with_seq = pm->seqnum_flag ;
int with_stats = (dtype == detail_display)
||(dtype == sequential_display) ;
@@ -1507,7 +1508,7 @@ vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name,
else
{
vector extract ;
- vector_index i ;
+ vector_index_t i ;
struct symbol* sym ;
if (dtype == detail_display || dtype == summary_display)
@@ -1542,7 +1543,7 @@ vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name,
{
struct prefix_list *plist;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
struct prefix p;
int ret;
int match;
@@ -1598,7 +1599,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
int ret;
struct prefix p;
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
pm = prefix_master_get (afi);
if (pm == NULL)
@@ -1609,8 +1610,13 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name,
struct symbol_walker walker ;
symbol_walk_start(&pm->table, &walker) ;
while ((plist = symbol_get_value(symbol_walk_next(&walker))))
- for (VECTOR_ITEMS(&plist->list, pe, i))
- pe->hitcnt = 0 ;
+ {
+ if (plist == NULL)
+ continue ;
+
+ for (VECTOR_ITEMS(&plist->list, pe, i))
+ pe->hitcnt = 0 ;
+ } ;
}
else
{
@@ -2815,7 +2821,7 @@ config_write_prefix_afi (afi_t afi, struct vty *vty)
struct prefix_master *pm;
int write = 0;
vector extract ;
- vector_index i, ipe ;
+ vector_index_t i, ipe ;
struct symbol* sym ;
pm = prefix_master_get (afi);
@@ -2870,7 +2876,7 @@ prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
u_char init_flag, u_char permit_flag, u_char deny_flag)
{
struct prefix_list_entry *pe;
- vector_index i ;
+ vector_index_t i ;
struct prefix_list *plist = prefix_list_ref_plist(ref) ;
@@ -2895,7 +2901,7 @@ prefix_bgp_orf_entry (struct stream *s, prefix_list_ref ref,
* return 0 - no such entry
*/
int
-prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+prefix_bgp_orf_get(struct prefix_list *plist, vector_index_t i,
struct orf_prefix *orfpe, enum prefix_list_type *pe_type)
{
struct prefix_list_entry *pe = NULL;
@@ -2986,7 +2992,7 @@ prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name)
if (vty)
{
struct prefix_list_entry* pe ;
- vector_index i ;
+ vector_index_t i ;
vty_prefix_list_name_count_print(vty, plist, VTY_NEWLINE) ;
diff --git a/lib/plist.h b/lib/plist.h
index 4f383417..4abddcd0 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -62,7 +62,7 @@ extern const char* prefix_list_get_name(struct prefix_list* plist) ;
extern struct stream * prefix_bgp_orf_entry (struct stream *,
prefix_list_ref ref,
u_char, u_char, u_char);
-extern int prefix_bgp_orf_get(struct prefix_list *plist, vector_index i,
+extern int prefix_bgp_orf_get(struct prefix_list *plist, vector_index_t i,
struct orf_prefix *orfpe, enum prefix_list_type *pe_type);
extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int);
extern void prefix_bgp_orf_remove_all (char *);
diff --git a/lib/prefix.c b/lib/prefix.c
index f13050e5..8774475a 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -27,6 +27,7 @@
#include "sockunion.h"
#include "memory.h"
#include "log.h"
+#include "tstring.h"
/* Maskbit. */
static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
@@ -272,18 +273,19 @@ prefix_ipv4_free (struct prefix_ipv4 *p)
int
str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
{
- char* pnt ;
- char* cp ;
- int ret ;
- unsigned plen ;
+ tstring_t(ipv4, 24) ;
+ char* pnt ;
+ const char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
if (pnt == NULL)
{
/* No / => simple address */
- plen = IPV4_MAX_BITLEN;
- ret = inet_aton (str, &p->prefix);
+ plen = IPV4_MAX_BITLEN ;
+ cp = str ;
}
else
{
@@ -292,19 +294,18 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
if (plen > IPV4_MAX_PREFIXLEN)
return 0;
- cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
- strncpy (cp, str, pnt - str);
- *(cp + (pnt - str)) = '\0';
- ret = inet_aton (cp, &p->prefix);
- XFREE (MTYPE_TMP, cp);
- }
+ cp = tstring_set_n(ipv4, str, (pnt - str)) ;
+ } ;
+ ret = inet_aton (cp, &p->prefix);
if (ret <= 0) /* should not return < 0, but it would not be valid ! */
return 0;
p->family = AF_INET;
p->prefixlen = plen;
+ tstring_free(ipv4) ;
+
return 1 ;
}
@@ -434,10 +435,11 @@ prefix_ipv6_free (struct prefix_ipv6 *p)
int
str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
- char* pnt ;
- char* cp ;
- int ret ;
- unsigned plen ;
+ tstring_t(ipv6, 64) ;
+ char* pnt ;
+ const char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
@@ -445,7 +447,7 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
/* No / => simple address */
plen = IPV6_MAX_BITLEN;
- ret = inet_pton (AF_INET6, str, &p->prefix);
+ cp = str ;
}
else
{
@@ -454,19 +456,18 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
if (plen > IPV6_MAX_PREFIXLEN)
return 0 ;
- cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
- strncpy (cp, str, pnt - str);
- *(cp + (pnt - str)) = '\0';
- ret = inet_pton (AF_INET6, cp, &p->prefix);
- XFREE (MTYPE_TMP, cp);
- }
+ cp = tstring_set_n(ipv6, str, (pnt - str)) ;
+ } ;
+ ret = inet_pton (AF_INET6, cp, &p->prefix);
if (ret <= 0)
return 0 ;
p->family = AF_INET6;
p->prefixlen = plen;
+ tstring_free(ipv6) ;
+
return 1 ;
}
diff --git a/lib/prefix.h b/lib/prefix.h
index 74f32e94..d8ed9a8a 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -23,12 +23,9 @@
#ifndef _ZEBRA_PREFIX_H
#define _ZEBRA_PREFIX_H
+#include "misc.h"
#include "sockunion.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
typedef const union sockunion* const_sockunion ;
/*
diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h
index 99c86055..c93b741e 100644
--- a/lib/qafi_safi.h
+++ b/lib/qafi_safi.h
@@ -23,7 +23,7 @@
#ifndef _QUAGGA_AFI_SAFI_H
#define _QUAGGA_AFI_SAFI_H
-#include <stdint.h>
+#include "misc.h"
#include "lib/zassert.h"
/*==============================================================================
diff --git a/lib/qfstring.c b/lib/qfstring.c
index 583d729f..3db3fedf 100644
--- a/lib/qfstring.c
+++ b/lib/qfstring.c
@@ -19,10 +19,8 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdbool.h>
-#include <stdint.h>
-
#include "qfstring.h"
+#include "zassert.h"
/*==============================================================================
*/
diff --git a/lib/qfstring.h b/lib/qfstring.h
index 83caa13d..990b4ef4 100644
--- a/lib/qfstring.h
+++ b/lib/qfstring.h
@@ -22,21 +22,8 @@
#ifndef _ZEBRA_QFSTRING_H
#define _ZEBRA_QFSTRING_H
-#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifndef Inline
-#define Inline static inline
-#endif
-
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
+#include "misc.h"
+#include "vargs.h"
/*==============================================================================
* These "qfstrings" address the issues of dealing with *fixed* length
@@ -68,21 +55,21 @@ enum pf_flags
pf_none = 0,
/* The following correspond to the "flags" */
- pf_commas = 1 << 0, /* "'" seen */
- pf_plus = 1 << 1, /* "+" seen */
- pf_space = 1 << 2, /* " " seen */
- pf_zeros = 1 << 3, /* "0" seen */
- pf_alt = 1 << 4, /* "#" seen */
+ pf_commas = BIT( 0), /* "'" seen */
+ pf_plus = BIT( 1), /* "+" seen */
+ pf_space = BIT( 2), /* " " seen */
+ pf_zeros = BIT( 3), /* "0" seen */
+ pf_alt = BIT( 4), /* "#" seen */
- pf_precision = 1 << 7, /* '.' seen */
+ pf_precision = BIT( 7), /* '.' seen */
/* The following signal how to render the value */
- pf_hex = 1 << 8, /* hex */
- pf_uc = 1 << 9, /* upper-case */
+ pf_hex = BIT( 8), /* hex */
+ pf_uc = BIT( 9), /* upper-case */
/* The following signal the type of value */
- pf_ptr = 1 << 14, /* is a pointer */
- pf_unsigned = 1 << 15, /* unsigned value */
+ pf_ptr = BIT(14), /* is a pointer */
+ pf_unsigned = BIT(15), /* unsigned value */
/* Common combination */
pf_hex_x = pf_unsigned | pf_hex,
diff --git a/lib/qiovec.h b/lib/qiovec.h
index ee03d2f2..f1498146 100644
--- a/lib/qiovec.h
+++ b/lib/qiovec.h
@@ -24,13 +24,7 @@
#include "zebra.h"
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "misc.h"
/*==============================================================================
* Flexible size "struct iovec"
diff --git a/lib/qpath.c b/lib/qpath.c
new file mode 100644
index 00000000..f6157b89
--- /dev/null
+++ b/lib/qpath.c
@@ -0,0 +1,819 @@
+/* Some primitive path handling
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "qpath.h"
+#include "qstring.h"
+
+#include "zassert.h"
+
+/*==============================================================================
+ * Some primitive path handling, based on qstrings.
+ *
+ *
+ *==============================================================================
+ * Path Reduction
+ *
+ * As per POSIX, multiple '/' count as single '/', except for the very special
+ * case of exactly two '/' at the start of a path. (That case is referred to
+ * here as a "double root".)
+ *
+ * So this code reduces runs of '/' to single '/' -- except for the special
+ * case.
+ *
+ * This code also replaces "/./" by "/".
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new qpath -- allocate if required.
+ *
+ * If a path is given, set that path -- allocating body even if path is zero
+ * length.
+ *
+ * If no path is given, leaves qpath with no body.
+ *
+ * If path is given, the qpath is set and reduced (see above).
+ *
+ * Returns: address of qpath
+ *
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qpath_reset() or qs_clear().
+ */
+extern qpath
+qpath_init_new(qpath qp, const char* path)
+{
+ if (qp == NULL)
+ qp = XCALLOC(MTYPE_QPATH, sizeof(qpath_t)) ;
+ else
+ memset(qp, 0, sizeof(qpath_t)) ;
+
+ /* Worry about fields other than the path */
+
+ qs_init_new(&qp->path, 0) ;
+
+ if (path != NULL)
+ qpath_set(qp, path) ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset contents of given qpath, freeing the structure if required.
+ *
+ * Discards all the contents of the qpath.
+ */
+extern qpath
+qpath_reset(qpath qp, bool free_structure)
+{
+ if (qp == NULL)
+ return NULL ;
+
+ qs_reset_keep(&qp->path, keep_it) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QPATH, qp) ; /* sets qp = NULL */
+ else
+ /* Worry about fields other than the path */
+ ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given qpath to copy of the given string -- allocate if required.
+ *
+ * If setting an existing qpath, discards any existing contents -- so the qpath
+ * MUST have been initialised at some time (qpath_init_new). Keeps any body
+ * that has been allocated if possible.
+ *
+ * Reduces the path (see above).
+ *
+ * Sets the path len, but does not touch the path cp.
+ */
+extern qpath
+qpath_set(qpath qp, const char* path)
+{
+ if (qp == NULL)
+ qp = qpath_init_new(NULL, path) ;
+ else
+ {
+ if (path != NULL)
+ qs_set(&qp->path, path) ;
+ else
+ qs_clear(&qp->path) ;
+ /* Worry about fields other than the path */
+ } ;
+
+ qpath_reduce(qp) ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reduce multiple '/' to single '/' (except for exactly "//" at start).
+ *
+ * Reduce "/./" to "/".
+ */
+static void
+qpath_reduce(qpath qp, size_t off)
+{
+ qpath part ;
+ qstring qs ;
+ char* sp ;
+ char* p ;
+ char* q ;
+
+ if (qp == NULL)
+ {
+ assert(off == 0) ;
+ return ; /* NULL qpath is empty */
+ } ;
+
+ qs = &qp->path ;
+ assert(off <= qs->len) ; /* Make sure 'off' is kosher */
+
+ sp = qs_chars(qs) ; /* NULL if qpath is completely empty */
+
+ if (sp == NULL)
+ return ; /* NULL path part is completely empty */
+
+ p = sp + off ;
+
+ /* Deal with special case of "//" at start.
+ *
+ * If find "//x", where x is anything other than '/', step past the first
+ * '/'. Could step past both "//", but that stops it seeing "//./zzz"
+ */
+ if ((*p == '/') && (*(p + 1) == '/') && (*(p + 2) != '/'))
+ ++p ;
+
+ /* Scan to see if there is anything that needs to be fixed.
+ *
+ * Looking for "//" and "/./".
+ */
+ while (1)
+ {
+ if (*p++ == '\0')
+ return ; /* nothing to do if hit end of string */
+
+ if ( (*p == '/') || ((*p == '.') && (*(p + 1) == '/')) )
+ {
+ if (*(p - 1) == '/')
+ break ; /* found "//" or "/./" */
+ }
+ } ;
+
+ /* Rats... there is something to be fixed.
+ *
+ * *p is second '/' of "//" or '.' of "/./".
+ */
+ q = p ;
+
+ while (*p != '\0')
+ {
+ /* Step past any number of '/' and any number of "./". */
+ while (1)
+ {
+ while (*p == '/')
+ ++p ;
+
+ if ((*p != '.') || (*p != '/'))
+ break ;
+
+ p += 2 ;
+ } ;
+
+ /* Scan, copying stuff, until get to '\0' or find "//" or "/./" */
+ while (*p != '\0')
+ {
+ *q++ = *p++ ; /* copy non-'\0' */
+
+ if ( (*p == '/') || ((*p == '.') && (*(p + 1) == '/')) )
+ {
+ if (*(p - 1) == '/')
+ break ; /* found "//" or "/./" */
+ } ;
+ } ;
+ } ;
+
+ /* Adjust the length and terminate */
+
+ qs->len = (q - sp) ; /* set the new length (shorter !) */
+ *q = '\0' ; /* and terminate */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a copy of the given qpath.
+ *
+ * Creates a brand new qpath object, which is a full copy of the given one.
+ *
+ * The result is an empty qpath if the given one is NULL.
+ */
+extern qpath
+qpath_copy(qpath qp_x)
+{
+ return qpath_init_new(NULL, qpath_path(qp_x)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a copy of a qpath to the given qpath.
+ *
+ * If required, creates new qpath object -- so qpath_copy_to(NULL, ...) is the
+ * same as qpath_copy(...).
+ *
+ * The result is an empty qpath if the given one is NULL.
+ */
+extern qpath
+qpath_copy_to(qpath qp, qpath qp_x)
+{
+ return qpath_set(qp, qpath_path(qp_x)) ;
+} ;
+
+/*==============================================================================
+ * Pop the last part of the given path.
+ *
+ * The cases are (where 'a' is anything except '/' and 'z' is anything):
+ *
+ * 1. "" -- empty path
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 2. "aaa" -- the path is a single part
+ *
+ * - part removed from path, leaves ""
+ * - return the part
+ *
+ * Note that in (1) and (2) there is no '/', in the following there is at least
+ * one '/'.
+ *
+ * 3. "/" -- root only, or
+ * "//" double root only
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 4. "/aaa" -- one remaining part before the root,
+ * "//aaa" or one remaining part after double route
+ *
+ * - part removed from path, leaves "/" or "//"
+ * - return the part
+ *
+ * 5. "zzz/" -- empty last part
+ *
+ * - "/" removed from end of path
+ * - return empty part
+ *
+ * 6. "zzz/aaa" -- non-empty last part
+ *
+ * - part and "/" removed from end of path
+ * - return part
+ *
+ * Note that other forms of multiple '/' have been reduced, already.
+ */
+extern qpath
+qpath_pop(qpath qp)
+{
+ qpath part ;
+ qstring qs ;
+ char* sp ;
+ char* p ;
+
+ /* Get pointers to start and end of path */
+ if (qp != NULL)
+ {
+ qs = &qp->path ;
+
+ sp = qs_chars(qs) ; /* NULL if qpath is completely empty */
+ p = qs_ep_char(qs) ; /* points at trailing '\0' */
+ }
+ else
+ qs = sp = p = NULL ;
+
+ /* Deal with NULL-ness */
+ if (sp == NULL)
+ {
+ assert(p == sp) ; /* ie qs->len == 0 if qs->body == NULL */
+
+ return qpath_init_new(NULL, NULL) ;
+ } ;
+
+ /* Track back to last '/' */
+ while ( (p > sp) && (*(p - 1) != '/') )
+ --p ;
+
+ /* Construct result which is from p forwards
+ *
+ * p == NULL if the given path is completely empty.
+ */
+ part = qpath_init_new(NULL, p) ;
+
+ /* If what remains is not empty, and not just "/" or "//", remove trailing '/'
+ *
+ * If p == sp, there was no '/', so path ends up empty.
+ * If p == sp + 1, there is one '/', and that is at the front of the path
+ * If p == sp + 2, there is either "//" or "a/"
+ * If p > sp + 2, there is "aa/"
+ */
+ if (p >= (sp + 2)) /* if "//", "a/" or "aa/" etc. */
+ {
+ /* Unless is special case of "//"... */
+ if ( ! ((p == (sp + 2)) && (*sp == '/')) )
+ --p ; /* ... discard trailing '/' */
+ } ;
+
+ qs->len = (p - sp) ; /* set the new length (shorter !) */
+ *p = '\0' ; /* and terminate */
+
+ /* Return the part we hacked off */
+ return part ;
+} ;
+
+/*==============================================================================
+ * Shift off the first part of the given path.
+ *
+ * The cases are (where 'a' is anything except '/' and 'z' is anything):
+ *
+ * 1. "" -- empty path
+ *
+ * - path unchanged
+ * - return empty part
+ *
+ * 2. "aaa" -- the path is a single part
+ *
+ * - part removed from path, leaves ""
+ * - return the part
+ *
+ * Note that in (1) and (2) there is no '/', in the following there is at least
+ * one '/'.
+ *
+ * 3. "/" -- root only, or
+ * "//" double root
+ *
+ * - remove the "/" or "//" -- result is empty
+ * - return "/" or "//" part
+ *
+ * 4. "/azz" -- root followed by stuff, or
+ * "//zzz" double root followed by stuff.
+ *
+ * - remove the "/" or "//"
+ * - return "/" or "//" part
+ *
+ * 5. "aaa/" -- something followed by first '/' which is end of path
+ *
+ * - remove upto and including '/' -- result is empty
+ * - return upto but excluding '/'
+ *
+ * 6. "aaa/azz" -- something followed by first '/' followed by something else
+ *
+ * - remove upto and including '/'
+ * - return upto but excluding '/'
+ *
+ * Note that other forms of multiple '/' have been reduced, already.
+ */
+extern qpath
+qpath_shift(qpath qp)
+{
+ qpath part ;
+ qstring qs ;
+ char* sp ;
+ char* p ;
+ char* q ;
+
+ /* Get pointers to start and end of path */
+ if (qp != NULL)
+ {
+ qs = &qp->path ;
+ sp = qs_chars(qs) ; /* NULL if qpath is completely empty */
+ }
+ else
+ qs = sp = NULL ;
+
+ /* Deal with NULL-ness */
+ if (sp == NULL)
+ return qpath_init_new(NULL, NULL) ;
+
+ p = sp ;
+
+ /* Set p such that sp..p-1 is to be shifted off.
+ * And q such that q..end-1 is to be kept.
+ */
+ if (*sp == '/')
+ {
+ ++p ; /* single root */
+ if (*p == '/')
+ ++p ; /* double root */
+ q = p ;
+ }
+ else
+ {
+ while ((*p != '/') && (*p != '/0'))
+ ++p ; /* step to '/' or end */
+
+ if (*p == '/')
+ q = p + 1 ; /* don't keep '/' */
+ else
+ q = p ; /* keep '\0' ! */
+ } ;
+
+ /* Construct qpath for shifted off stuff */
+ part = qpath_init_new(NULL, NULL) ;
+
+ /* Hack off the shifted off stuff & '/' if required */
+
+
+ /* Return the part we hacked off */
+ return part ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push one path onto the end of the given path.
+ *
+ * If the given path is NULL, creates a new, empty qpath to push onto.
+ *
+ * The given path is assumed to be the path to a "directory". An empty
+ * given path is treated as "the current directory".
+ *
+ * If the path to be pushed starts '/' or '~', then it is trimmed, removing
+ * leading characters upto and including '/' (stopping at '\0' if no '/' found).
+ *
+ * If path to be pushed onto is not empty, and does not end '/', then an '/'
+ * is appended before the path is pushed.
+ *
+ * Note that this means:
+ *
+ * -- pushing an empty path or one which is just "/", will leave the path
+ * ending "/" -- unless the given path is empty.
+ *
+ * -- cannot create a rooted path by pushing a path onto an empty path.
+ *
+ * -- pushing a "homed" path "~...." is assumed to be pushing onto the
+ * required "home".
+ *
+ * The resulting path is reduced (see above).
+ */
+
+extern qpath
+qpath_push(qpath qp, qpath qp_a)
+{
+ return qpath_push_str(qp, qpath_path(qp_a)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push path string onto the end of the given path.
+ *
+ * See above for discussion of "push" operation.
+ */
+extern qpath
+qpath_push_str(qpath qp, const char* path)
+{
+ qstring qs ;
+ char* ep ;
+ char* sp ;
+ size_t len ;
+ size_t off ;
+
+ if (qp == NULL)
+ qp = qpath_init_new(NULL, NULL) ;
+
+ qs = &qp->path ;
+
+ /* Trim the path to be pushed:
+ *
+ * 1. discard from any leading '~' to the first '/' or to '\0'.
+ *
+ * 2. then discard any leading '/'
+ *
+ * 3. then establish length of result.
+ */
+ if (path != NULL)
+ {
+ if (*path == '~')
+ do
+ {
+ ++path ;
+ } while ((*path != '/') && (*path != '\0')) ;
+
+ while (*path == '/')
+ ++path ; /* Step past leading '/' */
+ len = strlen(path) ;
+ }
+ else
+ len = 0 ;
+
+ /* Worry about whether need to add a '/' to the path before pushing */
+ sp = qs_chars(qs) ;
+ ep = qs_ep_char(qs) ; /* points at trailing '\0' */
+
+ if (sp == NULL)
+ assert(ep == sp) ; /* ie qs->len == 0 if qs->body == NULL */
+
+ off = qs->len ; /* where new stuff starts */
+
+ if (ep != sp)
+ {
+ if (*(ep - 1) == '/')
+ --off ; /* step back to the '/' */
+ else
+ {
+ /* Destination is not empty and does not end '/', so append one.
+ *
+ * Note that we ensure there is space for the path which are
+ * about to push, so at most one allocation required.
+ */
+ qs_need(qs, (ep - sp) + 1 + len) ;
+ qs_append_n(qs, "/", 1) ;
+ } ;
+ } ;
+
+ /* Now push path */
+ qs_append_n(qs, path, len) ;
+
+ /* Reduce the new part of the result, and return
+ *
+ * Note that the 'off' points at the '/' which precedes the new stuff.
+ * So will spot "/./" where the new stuff starts "./".
+ */
+ qpath_reduce(qp, off) ;
+
+ return qp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Join two paths to create a new path.
+ *
+ * Copies the destination path and then pushes the other path onto it.
+ */
+extern qpath
+qpath_join(qpath qp, qpath qp_a)
+{
+ qpath qp_n ;
+
+ qp_n = qpath_copy(qp) ;
+ return qpath_push(qp_n, qp_a) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Join path string to the given path to create a new path.
+ *
+ * Copies the destination path and then pushes the path string onto it.
+ */
+extern qpath
+qpath_join_str(qpath qp, const char* path)
+{
+ qpath qp_n ;
+
+ qp_n = qpath_copy(qp) ;
+ return qpath_push_str(qp_n, path) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_sex(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return qp_empty ; /* NULL qpath is empty */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp == '\0'))
+ return qp_empty ; /* NULL body or just '\0' */
+
+ if (*sp == '~')
+ return qp_homed ;
+
+ if (*sp == '/')
+ {
+ ++sp ;
+ if (*sp == '\0')
+ return qp_root ;
+ if (*sp != '/')
+ return qp_absolute ;
+
+ ++sp ;
+ if (*sp == '\0')
+ return qp_double_root ;
+ else
+ return qp_double_absolute ;
+ } ;
+
+
+
+ qp_empty, /* nothing at all */
+ qp_relative, /* something, not starting '/' */
+ qp_root, /* "/" all on its own */
+ qp_absolute, /* something, starting with single '/' */
+ qp_homed, /* something, starting with '~' */
+ qp_double_root, /* "//" all on its own */
+ qp_double_absolute /* something, starting with "//" */
+
+
+
+ return ((qp->path).len == 1) && (*sp == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is there anything there ?
+ */
+extern bool
+qpath_is_empty(qpath qp)
+{
+ if (qp == NULL)
+ return true ; /* NULL qpath is empty */
+
+ return (qp->path).len == 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is it: not empty, not starting '/' (or '//') and not starting '~'
+ */
+extern bool
+qpath_is_relative(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath is not relative */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp != '/') && (*sp != '.') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_is_root(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be root */
+
+ sp = qs_chars(&qp->path) ;
+
+ return ((qp->path).len == 1) && (*sp == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '/' (and not '//') ?
+ *
+ * Note that just "/" (ie root) will return true => it is also absolute.
+ */
+extern bool
+qpath_is_absolute(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be absolute */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp == '/') && (*(sp + 1) != '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '~'
+ */
+extern bool
+qpath_is_homed(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be homed */
+
+ sp = qs_chars( &qp->path) ;
+
+ return (sp != NULL) && (*sp == '~') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start and end '/' ?
+ */
+extern bool
+qpath_is_double_root(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be root */
+
+ sp = qs_chars(&qp->path) ;
+
+ return ((qp->path).len == 2) && (*sp == '/') && (*(sp + 1) == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path start '/' (and not '//') ?
+ *
+ * Note that just "/" (ie root) will return true => it is also absolute.
+ */
+extern bool
+qpath_is_double_absolute(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be absolute */
+
+ sp = qs_chars(&qp->path) ;
+
+ return (sp != NULL) && (*sp == '/') && (*(sp + 1) == '/') ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Does the given path *end* '/', or is it ~aaaa with no '/' at all.
+ *
+ * Note that root and double route return directory true.
+ */
+extern bool
+qpath_is_directory(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be directory */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp = '\0'))
+ return false ; /* Empty qpath cannot be directory */
+
+ ep = qs_ep_char(qs) ;
+
+ if (*(ep - 1) == '/')
+ return true ; /* Ends '/' */
+
+ if (*sp == '~')
+ {
+ while (*(++sp) != '/')
+ {
+ if (*sp == '\0') /* Starts '~' and no '/' found */
+ return true ;
+ } ;
+ } ;
+
+ return false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is the given path an atom
+ *
+ * Note that root and double route are .
+ */
+extern bool
+qpath_is_atom(qpath qp)
+{
+ char* sp ;
+
+ if (qp == NULL)
+ return false ; /* NULL qpath cannot be directory */
+
+ sp = qs_chars(&qp->path) ;
+
+ if ((sp == NULL) || (*sp = '\0'))
+ return false ; /* Empty qpath cannot be directory */
+
+ ep = qs_ep_char(qs) ;
+
+ if (*(ep - 1) == '/')
+ return true ; /* Ends '/' */
+
+ if (*sp == '~')
+ {
+ while (*(++sp) != '/')
+ {
+ if (*sp == '\0') /* Starts '~' and no '/' found */
+ return true ;
+ } ;
+ } ;
+
+ return false ;
+} ;
+
diff --git a/lib/qpath.h b/lib/qpath.h
new file mode 100644
index 00000000..27c61dff
--- /dev/null
+++ b/lib/qpath.h
@@ -0,0 +1,124 @@
+/* Some primitive path handling -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_QPATH_H
+#define _ZEBRA_QPATH_H
+
+#include "zebra.h"
+#include "misc.h"
+#include "memory.h"
+
+
+
+
+
+
+
+/*==============================================================================
+ * For these purposes a path is "parts" separated by one more '/' characters.
+ *
+ * As per POSIX, a pair of leading "//" (not three or more leading '/') is
+ * very special.
+ *
+ * The following sexes of qpath are established after any "path reduction"
+ * has been done. Path reduction removes extraneous '/'.
+ */
+
+typedef enum qpath_sex qpath_sex_t ;
+
+enum qpath_sex
+{
+ qp_empty, /* nothing at all */
+ qp_relative, /* something, not starting '/' */
+ qp_root, /* "/" all on its own */
+ qp_absolute, /* something, starting with single '/' */
+ qp_homed, /* something, starting with '~' */
+ qp_double_root, /* "//" all on its own */
+ qp_double_absolute /* something, starting with "//" */
+} ;
+
+typedef struct qpath qpath_t ;
+typedef struct qpath* qpath ;
+
+/* The qpath structure is largely a qstring, but one in which there is always
+ * a body, even if it only contains "\0", and the len is kept up to date.
+ */
+struct qpath
+{
+ qstring_t path ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qpath
+qpath_init_new(qpath qp, const char* path) ;
+
+extern qpath
+qpath_reset(qpath qp, bool free_structure) ;
+
+Inline qpath
+qpath_reset_free(qpath qp) { qpath_reset(qp, true) ; } ;
+
+Inline qpath
+qpath_reset_keep(qpath qp) { qpath_reset(qp, false) ; } ;
+
+Inline const char*
+qpath_path(qpath qp) ;
+
+extern qpath
+qpath_trim(qpath qp) ;
+
+extern qpath
+qpath_copy(qpath qp) ;
+
+extern qpath
+qpath_pop(qpath qp) ;
+
+extern qpath
+qpath_push(qpath qp, qpath qp_a) ;
+
+extern qpath
+qpath_join(qpath qp, qpath qp_a) ;
+
+/*==============================================================================
+ * Inline stuff
+ */
+
+/*------------------------------------------------------------------------------
+ * Get *temporary* pointer to actual path contained in the given qpath.
+ *
+ * This is *temporary* to the extent that when the qpath is changed or freed,
+ * this pointer will be INVALID -- you have been warned.
+ *
+ * This is a *const* pointer.
+ *
+ * For a NULL qpath, or an empty qpath, returns pointer to an empty string
+ * ('\0' terminated "").
+ */
+Inline const char*
+qpath_path(qpath qp)
+{
+ return qs_string(qp != NULL ? &qp->path : qp) ;
+} ;
+
+#endif /* _ZEBRA_QPATH_H */
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index 0cf5a824..03b52876 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_QPNEXUS_H
#define _ZEBRA_QPNEXUS_H
-#include <stdint.h>
+#include "misc.h"
#include <time.h>
#include <pthread.h>
#include <unistd.h>
@@ -34,10 +34,6 @@
#include "mqueue.h"
#include "qpselect.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga Nexus Interface -- qpn_xxxx
*
diff --git a/lib/qpselect.c b/lib/qpselect.c
index fd20d421..c15f112e 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -683,7 +683,7 @@ static qps_file
qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
{
qps_file qf ;
- vector_index i ;
+ vector_index_t i ;
int ret ;
dassert((fd >= 0) && (fd < (int)FD_SETSIZE)) ;
@@ -772,9 +772,9 @@ qps_file_remove(qps_selection qps, qps_file qf)
{
qps_file qf_last ;
int ret ;
- vector_index i = vector_bsearch(&qps->files,
- (vector_bsearch_cmp*)qps_fd_cmp,
- &qf->fd, &ret) ;
+ vector_index_t i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &qf->fd, &ret) ;
if (ret == 0)
qfd = vector_delete_item(&qps->files, i) ;
else
@@ -1279,9 +1279,9 @@ qps_selection_validate(qps_selection qps)
int enabled_count[qps_mnum_count] ;
fd_full_set enabled ;
- qps_file qf ;
- int fd, n, mnum, p_mnum ;
- vector_index i ;
+ qps_file qf ;
+ int fd, n, mnum, p_mnum ;
+ vector_index_t i ;
/* 1..4) Run down the selection vector and check. */
/* Collect new enabled_count and enabled bit vectors. */
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 8901ea36..48c86fe8 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -25,14 +25,11 @@
#include <sys/select.h>
#include <errno.h>
+#include "misc.h"
#include "zassert.h"
#include "qtime.h"
#include "vector.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga pselect -- qps_xxxx
*
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index d73182ef..959194ad 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -22,24 +22,15 @@
#ifndef _ZEBRA_QPTHREADS_H
#define _ZEBRA_QPTHREADS_H
-#include <stdint.h>
+#include "misc.h"
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
-#include <stdbool.h>
#include "zassert.h"
#include "qtime.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
-#ifndef private
-#define private extern
-#endif
-
/*==============================================================================
* Quagga Pthread Interface -- qpt_xxxx
*
@@ -133,13 +124,13 @@ qpt_thread_join(qpt_thread_t thread_id) ;
/*==============================================================================
* qpthreads_enabled support -- NOT FOR PUBLIC CONSUMPTION !
*/
-private uint8_t qpthreads_enabled_flag ; /* DO NOT TOUCH THIS PLEASE */
-private uint8_t qpthreads_thread_created_flag ; /* DO NOT TOUCH THIS PLEASE */
+Private uint8_t qpthreads_enabled_flag ; /* DO NOT TOUCH THIS PLEASE */
+Private uint8_t qpthreads_thread_created_flag ; /* DO NOT TOUCH THIS PLEASE */
-private int
+Private int
qpt_set_qpthreads_enabled(int how) ; /* qpthreads_enabled := how */
-private int
+Private int
qpt_freeze_qpthreads_enabled(void) ; /* get and freeze qpthreads_enabled */
/*==============================================================================
diff --git a/lib/qstring.c b/lib/qstring.c
index 5ca4d868..c7e5b81c 100644
--- a/lib/qstring.c
+++ b/lib/qstring.c
@@ -19,6 +19,9 @@
* Boston, MA 02111-1307, USA.
*/
+#include <stdio.h>
+#include <ctype.h>
+
#include "qstring.h"
#include "memory.h"
@@ -28,131 +31,57 @@
*/
/*------------------------------------------------------------------------------
- * Initialise qstring -- allocate if required.
- *
- * If non-zero len is given, a body is allocated (for at least len + 1).
- *
- * Returns: address of qstring
- *
- * NB: assumes initialising a new structure. If not, then caller should
- * use qs_reset() or qs_clear().
+ * Create a new, empty qs
*/
extern qstring
-qs_init_new(qstring qs, size_t len)
+qs_new(void)
{
- if (qs == NULL)
- qs = qs_new() ;
- else
- memset(qs, 0, sizeof(qstring_t)) ; /* see qs_new() */
-
- if (len != 0)
- return qs_make_to_length(qs, len) ;
-
- return qs ;
+ /* zeroising sets a completely empty qstring -- see qs_init_new() */
+ return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
} ;
/*------------------------------------------------------------------------------
- * Allocate or reallocate so that string is big enough for the given length.
+ * Initialise qstring -- allocate if required.
*
- * Allocate qstring if required. Returns with a body with size > 0.
+ * If non-zero slen is given, a body is allocated (for at least slen + 1).
+ * If zero slen is given, no body is allocated.
*
- * Allocates to 16 byte boundaries with space for '\0' beyond given length.
+ * Sets qs->len = qs->cp = 0. '\0' terminates body if allocates one.
*
* Returns: address of qstring
*
- * NB: allocates new body if the size == 0.
- *
- * If the qstring is a "dummy", its contents are now copied to the new
- * real qstring body -- up to a maximum of the new length.
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qs_reset() or qs_clear().
*/
extern qstring
-qs_make_to_length(qstring qs, size_t len)
+qs_init_new(qstring qs, usize slen)
{
- size_t size = (len + 0x10) & ~(size_t)(0x10 - 1) ;
-
if (qs == NULL)
qs = qs_new() ;
+ else
+ memset(qs, 0, sizeof(qstring_t)) ;
- if (size > qs->size)
- {
- if (qs->size == 0)
- {
- void* old ;
- old = qs->body ;
-
- qs->size = size ;
- qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ;
-
- if ((qs->len != 0) && (old != NULL))
- memcpy(qs->body, old, (qs->len <= len) ? qs->len : len) ;
- }
- else
- {
- qs->size *= 2 ;
- if (qs->size < size)
- qs->size = size ;
- qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
- } ;
- };
-
- return qs ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Add 'n' to the current string length, allocating or extending the body as
- * required.
- *
- * Allocate qstring if required. Returns with a body with size > 0.
- *
- * Allocates to 16 byte boundaries with space for '\0' beyond new length.
- *
- * Returns: address of qstring
- *
- * also: sets char** p_ep to point at the *end* of the new len.
- *
- * NB: allocates new body if the size == 0.
- *
- * If the qstring is a "dummy", its contents are now copied to the new
- * real qstring body -- up to a maximum of the new length.
- */
-extern qstring
-qs_add_len(qstring qs, size_t n, char** p_ep)
-{
- size_t len ;
- len = (qs != NULL) ? qs->len + n : n ;
-
- qs = qs_make_to_length(qs, len) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
- qs->len = len ;
+ /* Zeroising has set:
+ *
+ * body = NULL -- no body
+ * size = 0 -- no body
+ *
+ * len = 0
+ * cp = 0
+ *
+ * b_body = NULL -- no body buffer
+ * b_size = 0 -- no body buffer
+ */
- *p_ep = (char*)qs->body + len ;
+ if (slen != 0)
+ qs_make_to_size(qs, slen, false) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Free body of qstring -- zeroise size, len and cp
- *
- * Does nothing if qstring is NULL
- *
- * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
- * the old body.
- */
-extern void
-qs_free_body(qstring qs)
-{
- if (qs != NULL)
- {
- if (qs->size != 0)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
-
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
* Reset qstring -- free body and, if required, free the structure.
*
* If not freeing the structure, zeroise size, len and cp -- qs_free_body()
@@ -164,110 +93,97 @@ qs_free_body(qstring qs)
* the old body.
*/
extern qstring
-qs_reset(qstring qs, int free_structure)
+qs_reset(qstring qs, free_keep_b free_structure)
{
if (qs != NULL)
{
- if (qs->size != 0)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+ if (qs->b_body != NULL)
+ XFREE(MTYPE_STRING, qs->b_body) ; /* sets qs->b_body = NULL */
if (free_structure)
- XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
+ XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
else
- {
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
- } ;
+ memset(qs, 0, sizeof(qstring_t)) ; /* see qs_init_new */
} ;
return qs ;
} ;
-/*==============================================================================
- * printf() and vprintf() type functions
- */
-
/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf printf()
+ * Allocate or reallocate body so that is big enough for the given "slen".
*
- * Allocate qstring if required.
+ * If the qstring is currently an alias, copies all of the alias to a new
+ * body -- so always returns a non-alias qstring.
*
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
- */
-extern qstring
-qs_printf(qstring qs, const char* format, ...)
-{
- va_list args;
-
- va_start (args, format);
- qs = qs_vprintf(qs, format, args);
- va_end (args);
-
- return qs;
-} ;
-
-/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf vprintf()
+ * Returns with a body with size > 0. Allocates to 16 byte boundaries with
+ * space for '\0' beyond given length.
*
- * Allocate qstring if required.
+ * Does NOT affect qs->len or qs->cp. Does NOT re-terminate.
*
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
+ * NB: will allocate a new body even if the slen == 0.
+ *
+ * NB: always copies all of any aliased string (even if the slen == 0).
+ *
+ * NB: sets terminated false
*/
-extern qstring
-qs_vprintf(qstring qs, const char *format, va_list args)
+Private void
+qs_make_to_size(qstring qs, usize slen, bool keep)
{
- va_list ac ;
- int len ;
- qstring qqs ;
-
- qqs = qs ;
- if (qs == NULL)
- qs = qs_new() ;
+ usize size ;
+ usize alen ;
- while (1)
+ /* Worry about alias. If we keep it, we keep all of it. */
+ if (keep && (qs->size == 0))
{
- /* Note that vsnprintf() returns the length of what it would like to have
- * produced, if it had the space. That length does not include the
- * trailing '\0'.
- *
- * Also note that given a zero length the string address may be NULL, and
- * the result is still the length required.
- */
- va_copy(ac, args);
- qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ;
- va_end(ac);
-
- if (len < 0)
- break ;
+ alen = qs_len_nn(qs) ; /* alias stuff to keep */
+ if (slen <= alen)
+ slen = alen + 1 ; /* making sure can do that. */
+ }
+ else
+ alen = 0 ; /* no alias stuff to keep */
- if (len < (int)qs->size)
- return qs ;
+ /* Calculate the new size -- multiple of 16, >= 16. */
+ size = (slen + 0x10) & ~(usize)(0x10 - 1) ;
+ dassert(size != 0) ;
- qs_make_to_length(qs, len) ;
+ /* If that requires the body to be extended, do that now */
+ if (size > qs->b_size)
+ {
+ /* Need to allocate or extend the buffer.
+ *
+ * If current size is not zero, extend by doubling size or making at
+ * least the multiple of 16 calculated for new len.
+ */
+ qs->b_size *= 2 ;
+ if (qs->b_size < size)
+ qs->b_size = size ;
+ qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
} ;
- if (qqs == NULL)
- qs_reset_free(qs) ; /* discard what was allocated */
- else
- qs->len = 0 ;
+ /* If this is a non-empty alias, copy all or part of it. */
+ if (alen != 0)
+ memcpy(qs->b_body, qs_body_nn(qs), alen) ;
+
+ /* Update body and size, and no longer known to be terminated */
+ qs_set_body_nn(qs, qs->b_body) ;
+ qs->size = qs->b_size ;
+
- return NULL ;
} ;
/*==============================================================================
- * Other operations
+ * Setting value of qstring
*/
/*------------------------------------------------------------------------------
* Set qstring to be copy of the given string.
*
- * Allocates a qstring, if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Sets qs->len to the length of the string (excluding trailing '\0')
+ * Allocates a body and copies src to it, adding '\0'. Treats src == NULL as
+ * an empty string.
*
- * NB: if src == NULL, sets qstring to be zero length string.
+ * Sets qs->len to the length of the string (excluding trailing '\0').
+ * Sets qs->cp == 0.
*
* Returns: address of the qstring copied to.
*
@@ -276,101 +192,97 @@ qs_vprintf(qstring qs, const char *format, va_list args)
extern qstring
qs_set(qstring qs, const char* src)
{
- qs = qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ;
- if (qs->len != 0)
- memcpy(qs->body, src, qs->len + 1) ;
- else
- *((char*)qs->body) = '\0' ;
-
- return qs ;
+ return qs_set_n(qs, src, (src != NULL ? strlen(src) : 0)) ;
} ;
/*------------------------------------------------------------------------------
* Set qstring to be leading 'n' bytes of given string.
*
- * Allocates qstring if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Inserts '\0' terminator after the 'n' bytes copied.
+ * Allocates a body and copies 'n' bytes from src to it, adding '\0'. The
+ * src pointer is ignored if n == 0.
+ *
+ * Sets qs->len to the length of the string (excluding trailing '\0').
+ * Sets qs->cp == 0.
*
* Returns: address of the qstring copied to.
*
- * NB: src string MUST be at least 'n' bytes long.
+ * NB: if n == 0, src may be NULL
*
- * NB: src may not be NULL unless n == 0.
+ * NB: if n > 0, src string MUST be at least 'n' bytes long.
*
* NB: if copying to a dummy qstring, the old body is simply discarded.
*/
extern qstring
-qs_set_n(qstring qs, const char* src, size_t n)
+qs_set_n(qstring qs, const char* src, usize len)
{
- qs = qs_set_len(qs, n) ; /* ensures have body > n */
- if (n != 0)
- memcpy(qs->body, src, n) ;
+ char* p ;
+
+ qs = qs_new_len(qs, len) ; /* ensures have body > n */
+
+ p = qs_char_nn(qs) ;
+
+ if (len != 0)
+ memcpy(p, src, len) ;
+
+ *(p + len) = '\0' ;
- *((char*)qs->body + n) = '\0' ;
+ qs_set_term_nn(qs, true) ;
+ qs->cp = 0 ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Append given string to a qstring.
+ * Append given string to a qstring -- adding at qs->len position.
*
- * Allocates a qstring, if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Sets qs->len to the length of the result (excluding trailing '\0')
+ * Allocates or extends the body and copies bytes from src to it, adding '\0'.
+ * Treats src == NULL as an empty string.
*
- * NB: if src == NULL, appends nothing -- but result will be '\0' terminated.
+ * Sets qs->len to the length of the result (excluding trailing '\0')
+ * Does not change qs->cp.
*
- * Returns: address of the qstring copied to.
+ * Returns: address of the qstring appended to.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * NB: if appending to a dummy qstring, the old body is copied first.
*/
extern qstring qs_append(qstring qs, const char* src)
{
- size_t n ;
- char* ep ;
-
- n = (src != NULL) ? strlen(src) : 0 ;
-
- qs = qs_add_len(qs, n, &ep) ;
- ep = (char*)qs->body + qs->len ;
-
- if (n != 0)
- memcpy(ep - n, src, n + 1) ;
- else
- *ep = '\0' ;
-
- return qs ;
+ return qs_append_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
} ;
/*------------------------------------------------------------------------------
- * Set qstring to be leading 'n' bytes of given string.
+ * Append leading 'n' bytes of given string to a qstring -- adding at qs->len
+ * position.
*
- * Allocates qstring if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Returns: address of the qstring copied to.
+ * Allocates or extends the body and copies 'n' bytes from src to it,
+ * adding '\0'. The src pointer is ignored if n == 0.
*
- * NB: src string MUST be at least 'n' bytes long.
+ * Sets qs->len to the length of the result (excluding trailing '\0')
+ * Does not change qs->cp.
*
- * NB: src may not be NULL unless n == 0.
+ * Returns: address of the qstring appended to.
*
- * NB: if n == 0, appends nothing -- but result will be '\0' terminated.
+ * NB: if n == 0, src may be NULL
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * NB: if n > 0, src string MUST be at least 'n' bytes long.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * NB: if appending to a dummy qstring, the old body is copied first.
*/
extern qstring
-qs_append_n(qstring qs, const char* src, size_t n)
+qs_append_n(qstring qs, const char* src, usize n)
{
char* ep ;
qs = qs_add_len(qs, n, &ep) ;
if (n != 0)
- memcpy(ep - n, src, n) ;
-
- *ep = '\0' ;
+ memcpy(ep, src, n) ;
return qs ;
} ;
@@ -398,7 +310,7 @@ qs_append_n(qstring qs, const char* src, size_t n)
extern qstring
qs_copy(qstring dst, qstring src)
{
- size_t n ;
+ usize n ;
if (src == NULL)
{
@@ -417,17 +329,263 @@ qs_copy(qstring dst, qstring src)
dst->cp = src->cp ;
} ;
- qs_set_len(dst, n) ;
+ qs_set_len(dst, n) ; /* TODO: Copies alias !! */
if (n > 0)
- memcpy(dst->body, src->body, n) ;
+ memcpy(dst->s.body, src->s.body, n) ;
- *((char*)dst->body + n) = '\0' ;
+ *(dst->s.char_body + n) = '\0' ;
return dst ;
} ;
/*------------------------------------------------------------------------------
+ * Construct a qstring which is an alias for the given string.
+ *
+ * Allocates a qstring if required.
+ *
+ * Given string must be '\0' terminated.
+ *
+ * Does NOT copy the given string, but sets the qstring to be a pointer to it.
+ *
+ * NB: it is the caller's responsibility to ensure that the original string
+ * stays put for however long the qstring is an alias for it.
+ *
+ * It is also the caller's responsibility to see that the original string
+ * is discarded as required (once the alias is no longer required.)
+ *
+ * NB: if the qstring is changed in any way, a copy of the aliased string will
+ * be made first.
+ *
+ * NB: if a pointer to the body of the qstring is taken, then while that is in
+ * use, the qstring must not be released, so that the alias is not
+ * released.
+ *
+ * Returns: the address of the qstring.
+ */
+extern qstring
+qs_set_alias(qstring qs, const char* src)
+{
+ if (qs == NULL)
+ qs = qs_init_new(NULL, 0) ;
+
+ /* Make the alias. Note that any existing b_body and b_size are preserved,
+ * so that any current body can be reused at a later date.
+ */
+ qs->s.const_body = (src != NULL) ? src : "" ;
+ qs->len = strlen(src) ;
+ qs->cp = 0 ;
+ qs->size = 0 ; /* <=> this is an alias ! */
+ qs->terminated = true ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct a qstring which is an alias for the 'n' given characters.
+ *
+ * Allocates a qstring if required.
+ *
+ * Given characters are assumed not to be '\0' terminated.
+ *
+ * Does NOT copy the given characters, but sets the qstring to be a pointer to
+ * them.
+ *
+ * NB: it is the caller's responsibility to ensure that the original characters
+ * stays put for however long the qstring is an alias for them.
+ *
+ * It is also the caller's responsibility to see that the original
+ * characters are discarded as required (once the alias is no longer
+ * required.)
+ *
+ * NB: if the qstring is changed in any way, a copy of the aliased characters
+ * will be made first.
+ *
+ * NB: if a pointer to the body of the qstring is taken, then while that is in
+ * use, the qstring must not be released, so that the alias is not
+ * released.
+ *
+ * Returns: the address of the qstring.
+ */
+extern qstring
+qs_set_alias_n(qstring qs, const char* src, usize len)
+{
+ if (qs == NULL)
+ qs = qs_init_new(NULL, 0) ;
+
+ if (len == 0)
+ src = "" ;
+ else
+ assert(src != NULL) ;
+
+ /* Make the alias. Note that any existing b_body and b_size are preserved,
+ * so that any current body can be reused at a later date.
+ */
+ qs->s.const_body = src ;
+ qs->len = len ;
+ qs->cp = 0 ;
+ qs->size = 0 ; /* <=> this is an alias ! */
+ qs->terminated = false ;
+
+ return qs ;
+} ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*------------------------------------------------------------------------------
+ * Add 'n' to the current string length, allocating or extending the body.
+ *
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ *
+ * Returns with a body with size > 0. Allocates to 16 byte boundaries with
+ * space for '\0' beyond given length.
+ *
+ * Does NOT affect qs->cp.
+ * Does set the new qs->len -- qs->len += n
+ * Does NOT reterminate.
+ *
+ * Returns: address of qstring
+ *
+ * also: sets char** p_ep to point at the *end* of the old len.
+ *
+ * NB: will allocate a new body even if the new len == 0.
+ *
+ * NB: always copies all of any aliased string (even if the slen == 0).
+ */
+extern qstring
+qs_add_len(qstring qs, usize n, char** p_ep)
+{
+ usize slen ;
+ usize len ;
+
+ len = qs_len(qs) ;
+ slen = len + n ;
+
+ /* Set the new length -- creating if required.
+ *
+ * Will always return with a body and no longer an alias (if was one).
+ */
+ qs = qs_set_len(qs, slen) ;
+
+ /* Set pointer to old end (len) position. */
+ *p_ep = ((char*)qs_body_nn(qs)) + len ;
+
+ return qs ;
+} ;
+
+/*==============================================================================
+ * printf() and vprintf() type functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
+ *
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ *
+ * If OK:
+ *
+ * Sets qs->len to the length of the null terminated result.
+ * Does NOT affect qs->cp.
+ *
+ * If fails:
+ *
+ * Sets qs->len = qs->cp = 0 and terminates to zero length.
+ *
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is) -- qstring set empty.
+ */
+extern qstring
+qs_printf(qstring qs, const char* format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ qs = qs_vprintf(qs, format, args);
+ va_end (args);
+
+ return qs;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf vprintf()
+ *
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ *
+ * If OK:
+ *
+ * Sets qs->len to the length of the null terminated result.
+ * Does NOT affect qs->cp.
+ *
+ * If fails:
+ *
+ * Sets qs->len = qs->cp = 0 and terminates to zero length.
+ *
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
+ */
+extern qstring
+qs_vprintf(qstring qs, const char *format, va_list args)
+{
+ va_list ac ;
+ int slen ;
+ qstring qqs ;
+
+ qqs = qs ;
+ if (qs == NULL)
+ qs = qs_new() ; /* sets size == 0 */
+ else
+ qs_set_len_nn(qs, 0) ; /* Forget current contents */
+
+ while (1)
+ {
+ /* Note that vsnprintf() returns the length of what it would like to have
+ * produced, if it had the space. That length does not include the
+ * trailing '\0'.
+ *
+ * Also note that given a zero length the string address may be NULL, and
+ * the result is still the length required.
+ */
+ va_copy(ac, args);
+ slen = vsnprintf (qs_body_nn(qs), qs->size, format, ac) ;
+ va_end(ac);
+
+ if (slen < 0)
+ break ; /* Quit if failed */
+
+ if ((usize)slen < qs->size)
+ {
+ qs_set_len_nn(qs, slen) ;
+ return qs ; /* Exit if succeeded */
+ } ;
+
+ qs_make_to_size(qs, slen) ; /* Extend body to required len */
+ } ;
+
+ if (qqs == NULL)
+ qs_reset(qs, free_it) ; /* discard what was allocated */
+ else
+ qs_clear(qs) ;
+
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Other operations
+ */
+
+/*------------------------------------------------------------------------------
* Compare significant parts of two qstrings.
*
* By significant, mean excluding leading/trailing isspace() and treating
@@ -455,7 +613,7 @@ qs_cmp_sig(qstring a, qstring b)
*/
if (a != NULL)
{
- p_a = a->body ;
+ p_a = a->s.uchar_body ;
e_a = p_a + a->len ;
while ((p_a < e_a) && isspace(*p_a))
@@ -471,7 +629,7 @@ qs_cmp_sig(qstring a, qstring b)
if (b != NULL)
{
- p_b = b->body ;
+ p_b = b->s.uchar_body ;
e_b = p_b + b->len ;
while ((p_b < e_b) && isspace(*p_b))
@@ -509,3 +667,152 @@ qs_cmp_sig(qstring a, qstring b)
else
return 0 ;
} ;
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
+ *
+ * Increases 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ *
+ * NB: if this is a aliased qstring, a copy is made of the original body.
+ */
+extern usize
+qs_insert(qstring qs, const void* src, usize n)
+{
+ usize after ;
+ usize len ;
+ char* p ;
+
+ qs->terminated = false ; /* NB: require qs != NULL ! */
+
+ len = qs_len_nn(qs) ;
+ if (len < qs->cp) /* make len = max(len, cp) ! */
+ len = qs->cp ;
+
+ after = len - qs->cp ;
+
+ qs_set_len(qs, len + n) ; /* set len and ensure have space
+ Makes copy of any aliased string. */
+ p = qs_cp_char(qs) ;
+ if (after > 0)
+ memmove (p + n, p, after) ;
+
+ if (n > 0)
+ memmove(p, src, n) ;
+
+ return after ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace 'n' bytes at 'cp' -- extending if required.
+ *
+ * May increase 'len'. but does not affect 'cp'.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ *
+ * NB: if this is a aliased qstring, a copy is made of the original body.
+ */
+extern void
+qs_replace(qstring qs, const void* src, usize n)
+{
+ usize len ;
+
+ qs->terminated = false ; /* NB: require qs != NULL ! */
+
+ len = qs_len_nn(qs) ;
+ if (len < (qs->cp + n)) /* make len = max(len, cp + n) */
+ len = qs->cp + n ;
+
+ qs_set_len(qs, len) ; /* set len and ensure have space.
+ Makes copy of any aliased string. */
+
+ if (n > 0)
+ memmove(qs_cp_char(qs), src, n) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove 'n' bytes at 'cp' -- extending if required.
+ *
+ * May change 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: qstring MUST NOT be NULL
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ *
+ * NB: if this is a aliased qstring, a copy is made of the original body.
+ */
+extern usize
+qs_delete(qstring qs, usize n)
+{
+ usize after ;
+ char* p ;
+ usize len ;
+
+ qs->terminated = false ; /* NB: require qs != NULL ! */
+
+ len = qs_len_nn(qs) ;
+
+ /* If deleting to or beyond 'len', force len to cp */
+ if ((qs->cp + n) >= len)
+ {
+ len = qs->cp ;
+ qs_set_len_nn(qs, len) ; /* truncate now, so that if this is an
+ aliased string, only copy what is
+ going to be kept. */
+ after = 0 ; /* nothing to move */
+ }
+ else
+ after = len - (qs->cp + n) ;
+
+ qs_set_len(qs, len) ; /* set len and ensure have space.
+ Makes copy of any aliased string. */
+
+
+
+ /* Watch out for "dummy" */
+ if (qs->size == 0)
+ qs_make_to_size(qs, len) ;
+
+ /* If deleting up to or beyond len, then simply set len == cp
+ * note that this may reduce or increase len !
+ */
+ if ((qs->cp + n) >= len)
+ {
+ if (qs->cp < len)
+ qs_set_len_nn(qs, qs->cp) ; /* discard stuff after qs->cp */
+
+ qs_set_len(qs, qs->cp) ; /* set len */
+ return 0 ; /* nothing after */
+ }
+
+ /* There is at least one byte after cp (so body must exist) */
+ after = len - (qs->cp + n) ;
+
+ if (n > 0)
+ {
+ p = qs_cp_char(qs) ;
+ memmove (p, p + n, after) ;
+
+ qs_set_len_nn(qs, len - n) ;
+ } ;
+
+ return after ;
+} ;
diff --git a/lib/qstring.h b/lib/qstring.h
index 0597eda8..5b1d4932 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -22,23 +22,11 @@
#ifndef _ZEBRA_QSTRING_H
#define _ZEBRA_QSTRING_H
-#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
+#include "misc.h"
+#include "vargs.h"
+#include "zassert.h"
#include "memory.h"
-
-#ifndef Inline
-#define Inline static inline
-#endif
-
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
+#include "elstring.h"
/*==============================================================================
* These "qstrings" address address the lack of a flexible length string in 'C'.
@@ -49,24 +37,32 @@
*
* The caller does, however, have to explicitly release the contents of a
* qstring when it is done with.
+ *
+ *
+ *
*/
+struct qstring
+{
+ elstring_t els ; /* *embedded* */
+
+ usize size ; /* of the els body */
-typedef struct qstring qstring_t ;
+ usize cp ;
+
+ usize b_size ;
+ void* b_body ;
+} ;
+
+typedef struct qstring qstring_t[1] ;
typedef struct qstring* qstring ;
-struct qstring
+/* Setting an qstring object to all zeros is enough to initialise it to
+ * an empty string -- including the embedded elstring.
+ */
+CONFIRM(ELSTRING_INIT_ALL_ZEROS) ;
+enum
{
- union
- {
- void* body ;
- const void* const_body ;
- char* char_body ;
- unsigned char* uchar_body ;
- } ;
- size_t size ;
-
- size_t len ;
- size_t cp ;
+ QSTRING_INIT_ALL_ZEROS = true
} ;
/*------------------------------------------------------------------------------
@@ -75,92 +71,139 @@ struct qstring
* NB: if the body has not yet been allocated, these functions will return
* NULL or NULL + the offset.
*/
-Inline char* /* pointer to body of qstring */
-qs_chars(qstring qs)
+Inline elstring
+qs_els_nn(qstring qs)
{
- return (char*)qs->body ;
+ return qs->els ;
} ;
-Inline unsigned char* /* pointer to body of qstring */
-qs_bytes(qstring qs)
+Inline elstring
+qs_els(qstring qs)
{
- return (unsigned char*)qs->body ;
+ return qs->els ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_chars_at(qstring qs, size_t off)
+Inline void*
+qs_body_nn(qstring qs) /* pointer to body of qstring (not NULL) */
{
- return qs_chars(qs) + off ;
+ return els_body_nn(qs->els) ;
} ;
-Inline unsigned char* /* pointer to given offset in qstring */
-qs_bytes_at(qstring qs, size_t off)
+Inline void* /* pointer to body of qstring */
+qs_body(qstring qs)
{
- return qs_bytes(qs) + off ;
+ return (qs != NULL) ? qs_body_nn(qs) : NULL ;
} ;
-Inline char* /* pointer to 'cp' offset in qstring */
-qs_cp_char(qstring qs)
+Inline void /* set pointer to body of qstring (not NULL) */
+qs_set_body_nn(qstring qs, const void* body)
+{
+ els_set_body_nn(qs->els, body) ; /* sets term = fase */
+} ;
+
+Inline ulen /* length of qstring (not NULL) */
+qs_len_nn(qstring qs)
+{
+ return els_len_nn(qs->els) ;
+} ;
+
+Inline ulen /* length of qstring */
+qs_len(qstring qs)
+{
+ return (qs != NULL) ? qs_len_nn(qs) : 0 ;
+} ;
+
+Inline void /* set length of qstring (not NULL) */
+qs_do_set_len_nn(qstring qs, ulen len)
+{
+ els_set_len_nn(qs->els, len) ; /* sets term = false */
+} ;
+
+Inline ulen /* cp of qstring (not NULL) */
+qs_cp_nn(qstring qs)
+{
+ return qs->cp ;
+} ;
+
+Inline ulen /* cp of qstring */
+qs_cp(qstring qs)
+{
+ return (qs != NULL) ? qs_cp_nn(qs) : 0 ;
+} ;
+
+Inline void /* set cp of qstring (not NULL) */
+qs_do_set_cp_nn(qstring qs, ulen cp)
+{
+ qs->cp = cp ;
+} ;
+
+Inline char* /* pointer to given offset in qstring */
+qs_char_at_nn(qstring qs, usize off)
+{
+ char* p ;
+ p = qs_body_nn(qs) ;
+ return (p != NULL) ? p + off : NULL ;
+} ;
+
+Inline char* /* pointer to given offset in qstring */
+qs_char_at(qstring qs, usize off)
{
- return qs_chars_at(qs, qs->cp) ;
+ return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
} ;
-Inline unsigned char* /* pointer to 'cp' offset in qstring */
-qs_cp_byte(qstring qs)
+Inline char* /* pointer to 'cp' offset in qstring */
+qs_cp_char(qstring qs)
{
- return qs_bytes_at(qs, qs->cp) ;
+ return (qs != NULL) ? qs_char_at_nn(qs, qs_cp_nn(qs)) : NULL ;
} ;
-Inline char* /* pointer to 'len' offset in qstring */
+Inline char* /* pointer to 'len' offset in qstring */
qs_ep_char(qstring qs)
{
- return qs_chars_at(qs, qs->len) ;
+ return (qs != NULL) ? qs_char_at_nn(qs, qs_len_nn(qs)) : NULL ;
+} ;
+
+Inline bool /* whether qstring is known to be terminated */
+qs_term_nn(qstring qs)
+{
+ return els_term_nn(qs->els) ;
} ;
-Inline unsigned char* /* pointer to 'len' offset in qstring */
-qs_ep_byte(qstring qs)
+Inline void /* set qstring is known to be terminated */
+qs_set_term_nn(qstring qs, bool how)
{
- return qs_bytes_at(qs, qs->len) ;
+ return els_set_term_nn(qs->els, how) ;
} ;
/*==============================================================================
* Functions
*/
-extern qstring qs_init_new(qstring qs, size_t len) ;
-extern qstring qs_make_to_length(qstring qs, size_t len) ;
-extern void qs_free_body(qstring qs) ;
-extern qstring qs_reset(qstring qs, int free_structure) ;
+extern qstring qs_new(void) ;
+extern qstring qs_init_new(qstring qs, usize len) ;
+extern qstring qs_reset(qstring qs, free_keep_b free_structure) ;
-#define qs_reset_keep(qs) qs_reset(qs, 0)
-#define qs_reset_free(qs) qs_reset(qs, 1)
-
-Inline qstring qs_new(void) ;
-Inline qstring qs_dummy(qstring qs, const char* src, int pos) ;
-
-extern qstring qs_printf(qstring qs, const char* format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ;
+Private void qs_make_to_size(qstring qs, usize len, free_keep_b free) ;
extern qstring qs_set(qstring qs, const char* src) ;
-extern qstring qs_set_n(qstring qs, const char* src, size_t n) ;
+extern qstring qs_set_n(qstring qs, const char* src, usize n) ;
extern qstring qs_append(qstring qs, const char* src) ;
-extern qstring qs_append_n(qstring qs, const char* src, size_t n) ;
+extern qstring qs_append_n(qstring qs, const char* src, usize n) ;
-Inline qstring qs_need(qstring qs, size_t len) ;
-Inline qstring qs_set_len(qstring qs, size_t len) ;
-extern qstring qs_add_len(qstring qs, size_t n, char** p_ep) ;
-Inline void qs_clear(qstring qs) ;
-Inline size_t qs_len(qstring qs) ;
-Inline size_t qs_size(qstring qs) ;
-Inline void* qs_term(qstring qs) ;
+extern qstring qs_copy(qstring dst, qstring src) ;
-Inline size_t qs_insert(qstring qs, const void* src, size_t n) ;
-Inline void qs_replace(qstring qs, const void* src, size_t n) ;
-Inline size_t qs_delete(qstring qs, size_t n) ;
+extern qstring qs_set_alias(qstring qs, const char* src) ;
+extern qstring qs_set_alias_n(qstring qs, const char* src, usize len) ;
+
+extern qstring qs_printf(qstring qs, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ;
+
+extern usize qs_insert(qstring qs, const void* src, usize n) ;
+extern void qs_replace(qstring qs, const void* src, usize n) ;
+extern usize qs_delete(qstring qs, usize n) ;
-extern qstring qs_copy(qstring dst, qstring src) ;
extern int qs_cmp_sig(qstring a, qstring b) ;
/*==============================================================================
@@ -168,301 +211,213 @@ extern int qs_cmp_sig(qstring a, qstring b) ;
*/
/*------------------------------------------------------------------------------
- * Make a brand new, completely empty qstring
- */
-Inline qstring
-qs_new(void)
-{
- /* Zeroising has set:
- *
- * body = NULL -- no body
- * size = 0 -- no body
- *
- * len = 0
- * cp = 0
- *
- * Nothing more to do unless initial size != 0
- */
- return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Construct a "dummy" qstring from the given string.
+ * Clear contents of qstring -- preserves any qstring body, but sets len = 0.
*
- * Allocates a qstring if required.
+ * Does nothing if qstring is NULL
*
- * This sets: body = the src
- * len = strlen(src) (0 if src is NULL)
- * cp = 0 if 'pos' is zero
- * len otherwise
- * size = 0
+ * Sets 'cp' = 'len' = 0.
*
- * The zero size means that the qstring handling will not attempt to free
- * the body, nor will it write to it... Operations which require the qstring
- * to have a size will allocate a new body, and discard this one.
+ * If is an alias qstring, discard the alias.
*
- * Returns: the address of the dummy qstring.
+ * NB: does not create a qstring body if there isn't one.
*/
-Inline qstring
-qs_dummy(qstring qs, const char* src, int pos)
+Inline void
+qs_clear(qstring qs)
{
- if (qs == NULL)
- qs = qs_new() ;
-
- qs->const_body = src ;
- qs->len = (src != NULL) ? strlen(src) : 0 ;
- qs->cp = (pos == 0) ? 0 : qs->len ;
- qs->size = 0 ;
-
- return qs ;
-}
+ if (qs != NULL)
+ {
+ qs_do_set_len_nn(qs, 0) ; /* sets term == false */
+ if (qs->size == 0)
+ {
+ qs_set_body_nn(qs, qs->b_body) ;
+ qs->size = qs->b_size ;
+ } ;
+ qs->cp = 0 ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Need space for a string of 'len' characters (plus possible '\0').
+ * Need space for a string of 'slen' characters (plus possible '\0').
*
- * Allocates the qstring, if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Returns: address of qstring
+ * Returns: address of qstring -- with body that can be written upto and
+ * including 'slen' + 1.
+ *
+ * NB: has no effect on 'len' -- even if 'len' > 'slen'.
*
- * NB: asking for 0 bytes will cause a body to be allocated, ready for any
- * '\0' !
+ * NB: has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
*
- * NB: has no effect on 'cp' or 'len'. (Will be zero if new qstring allocated.)
+ * NB: if this is a aliased qstring, the alias is discarded and term = false.
*/
Inline qstring
-qs_need(qstring qs, size_t len)
+qs_need(qstring qs, usize slen)
{
- if ((qs == NULL) || (len >= qs->size))
- return qs_make_to_length(qs, len) ;
+ if (qs == NULL)
+ qs = qs_init_new(NULL, slen) ; /* Make the qstring if required */
+ else
+ if (slen >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, slen, free_it) ;
- assert(qs->body != NULL) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
* Set 'len' -- allocate or extend body as required.
*
- * Allocates the qstring, if required.
+ * Allocate qstring if required (setting qs->len = qs->cp = 0).
*
- * Returns: address of qstring
+ * Returns: address of qstring -- with body that can be written upto and
+ * including 'len' + 1.
*
- * NB: setting len == 0 bytes will cause a body to be allocated, ready for any
- * '\0' !
+ * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
*
- * NB: has no effect on 'cp' -- even if 'cp' > 'len'.
- *
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: if this is a aliased qstring, a copy is made of all of the original body,
+ * even if that is longer than the required 'slen'. (And term = false.)
*/
Inline qstring
-qs_set_len(qstring qs, size_t len)
+qs_set_len(qstring qs, usize len)
{
- qs = qs_need(qs, len) ;
- qs->len = len ;
+ if (qs == NULL)
+ qs = qs_init_new(NULL, len) ; /* Make the qstring if required */
+ else
+ if (len >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, len, keep_it) ;
+
+ qs_do_set_len_nn(qs, len) ;
+
+ if (qs->cp > len)
+ qs->cp = len ;
+
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Reset contents of qstring.
+ * Chop to given length -- will neither allocate nor extend body.
*
- * Does nothing if qstring is NULL
+ * Does nothing if qstring is NULL.
+ *
+ * Does not change the 'len' if it is <= length to chop to.
*
- * Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL.
+ * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
*
- * For "dummy" qstring, discards the body.
+ * NB: if this is a aliased qstring, then it remains an aliased string, but
+ * shorter and term = false (unless no change made to the length).
*/
Inline void
-qs_clear(qstring qs)
+qs_chop(qstring qs, usize clen)
{
if (qs != NULL)
{
- qs->len = 0 ;
- qs->cp = 0 ;
- if (qs->size > 0)
- *((char*)qs->body) = '\0' ;
- else
- qs->body = NULL ;
+ usize len = qs_len_nn(qs) ;
+ if (len > clen)
+ qs_do_set_len_nn(qs, (len = clen)) ; /* sets term = false */
+ if (qs->cp > len)
+ qs->cp = len ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Get length of qstring -- by doing strlen() -- and record it in qs->len.
+ * Set 'cp' -- allocate or extend body as required.
*
- * Returns: the string length
- *
- * NB: if no body has been allocated, length = 0
- */
-Inline size_t
-qs_len(qstring qs)
-{
- return (qs != NULL) ? (qs->len = (qs->body != NULL) ? strlen(qs_chars(qs))
- : 0)
- : 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Get size of qstring body.
- *
- * NB: if no body has been allocated, size == 0
- * if qstring is NULL, size == 0
- *
- * NB: if this is a "dummy" qstring, size == 0.
- */
-Inline size_t
-qs_size(qstring qs)
-{
- return (qs != NULL) ? qs->size : 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Get address of current end of qstring body -- ie byte at 'len'.
- *
- * NB: allocates body if required.
- *
- * There will be space for '\0' after 'len', so the address returned
- * is within the real body of the string.
- *
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * Allocates the qstring, if required.
*
- * NB: address of qstring may NOT be NULL.
- */
-Inline void*
-qs_end(qstring qs)
-{
- if (qs->len >= qs->size)
- qs_make_to_length(qs, qs->len) ; /* allows for trailing '\0' */
-
- return (char*)qs->body + qs->len ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set '\0' at qs->len -- allocate or extend body as required.
+ * Returns: address of qstring
*
- * Returns address of body -- NULL if the qstring is NULL
+ * NB: if there was no body, allocates a body for the string, even if 'cp' == 0.
*
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: if new 'cp' > 'len', extends body (or allocates one), and sets 'len' to
+ * 'cp'. If this is an alias qstring, a copy of the string is made.
*/
-Inline void*
-qs_term(qstring qs)
+Inline qstring
+qs_set_cp(qstring qs, usize cp)
{
- size_t len ;
-
if (qs == NULL)
- return NULL ;
+ qs = qs_new(qs) ;
- if ((len = qs->len) >= qs->size)
- qs_make_to_length(qs, len) ;
+ if (qs->size <= cp)
- *qs_chars_at(qs, len) = '\0' ;
- return qs->body ;
+ if ((qs == NULL) || (cp >(cp > qs_len_nn(qs)))
+ qs = qs_set_len(qs, cp) ;
+ qs->cp = cp ;
+ return qs ;
} ;
/*------------------------------------------------------------------------------
- * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
- *
- * Increases 'len'. but does not affect 'cp'.
- *
- * Returns: number of bytes beyond 'cp' that were moved before insert.
+ * If not "term": set '\0' at qs->len -- extending body as required.
*
- * NB: qstring MUST NOT be NULL
+ * Does NOT affect qs->cp or qs->len.
*
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
+ * Returns address of body -- NULL if the qstring is NULL
*
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: if this is an alias, and it is not terminated, make a copy before adding
+ * terminating '\0'.
*/
-Inline size_t
-qs_insert(qstring qs, const void* src, size_t n)
+Inline void
+qs_terminate_nn(qstring qs)
{
- size_t after ;
- char* p ;
-
- if (qs->len < qs->cp)
- qs->len = qs->cp ;
- after = qs->len - qs->cp ;
+ if (!qs_term_nn(qs))
+ {
+ usize len ;
- qs_set_len(qs, qs->len + n) ; /* set len and ensure have space */
+ len = qs_len_nn(qs) ;
- p = qs_cp_char(qs) ;
- if (after > 0)
- memmove (p + n, p, after) ;
+ if (len >= qs->size) /* alias has size == 0 */
+ qs_make_to_size(qs, len) ; /* make sure can insert '\0' */
- if (n > 0)
- memmove(p, src, n) ;
+ *qs_char_at_nn(qs, len) = '\0' ;
- return after ;
+ qs_set_term_nn(qs, true) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Replace 'n' bytes at 'cp' -- extending if required.
+ * Return pointer to '\0' terminated string value.
*
- * May increase 'len'. but does not affect 'cp'.
+ * If qs is NULL or body is NULL returns pointer to constant empty '\0'
+ * terminated string.
*
- * NB: qstring MUST NOT be NULL
+ * If string is terminated, return address of string, which may be an alias
+ * address.
*
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
+ * Otherwise, makes sure that there is a '\0' at the qs->len position, making
+ * a copy of any aliased string if required, and returns address of result.
*
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ * NB: value returned may be the address of the qstring body, or the address of
+ * an aliased string. In any event, the string should not be changed or
+ * reset until this pointer has been discarded !
*/
-Inline void
-qs_replace(qstring qs, const void* src, size_t n)
+Inline const char*
+qs_string(qstring qs)
{
- if ((qs->len < qs->cp + n) || (qs->size == 0))
- qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */
+ if ((qs == NULL) || (qs_len_nn(qs) == 0))
+ return "" ;
- if (n > 0)
- memmove(qs_cp_char(qs), src, n) ;
+ qs_terminate_nn(qs) ;
+
+ return qs_body_nn(qs) ;
} ;
/*------------------------------------------------------------------------------
- * Remove 'n' bytes at 'cp' -- extending if required.
- *
- * May change 'len'. but does not affect 'cp'.
+ * Assuming the given address is within the size of the given qstring,
+ * set qs->len and insert '\0' terminator there.
*
- * Returns: number of bytes beyond 'cp' that were moved before insert.
+ * Does NOT affect qs->cp.
*
- * NB: qstring MUST NOT be NULL
+ * NB: must NOT be a NULL qs.
*
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
+ * NB: must NOT be an aliased qstring.
*
- * NB: the string is NOT re-terminated.
+ * NB: must NOT have a NULL body.
*/
-Inline size_t
-qs_delete(qstring qs, size_t n)
+Inline void
+qs_term_here(qstring qs, char* here)
{
- size_t after ;
- char* p ;
+ assert((here >= qs->s.char_body) && (here < (qs->s.char_body + qs->size))) ;
- /* Watch out for "dummy" */
- if (qs->size == 0)
- qs_make_to_length(qs, qs->len) ;
-
- /* If deleting up to or beyond len, then simply set len == cp */
- if ((qs->cp + n) >= qs->len)
- {
- qs_set_len(qs, qs->cp) ; /* set len, looks after cp > len */
- return 0 ; /* nothing after */
- }
-
- /* There is at least one byte after cp (so body must exist) */
- after = qs->len - (qs->cp + n) ;
-
- if (n > 0)
- {
- p = qs_cp_char(qs) ;
- memmove (p, p + n, after) ;
-
- qs->len -= n ;
- } ;
-
- return after ;
+ qs->len = (here - qs->s.char_body) ;
+ *here = '\0' ;
} ;
-
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtime.h b/lib/qtime.h
index 35e1a51b..38e9ac1a 100644
--- a/lib/qtime.h
+++ b/lib/qtime.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_QTIME_H
#define _ZEBRA_QTIME_H
-#include <stdint.h>
+#include "misc.h"
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
@@ -31,10 +31,6 @@
#include "zassert.h"
#include "config.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* qtime_t -- signed 64-bit integer.
*
diff --git a/lib/qtimers.c b/lib/qtimers.c
index 8c08a6bc..5c0f1518 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -213,11 +213,12 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
* and the process MUST be run to completion.
*/
qtimer
-qtimer_pile_ream(qtimer_pile qtp, int free_structure)
+qtimer_pile_ream(qtimer_pile qtp, free_keep_b free_structure)
{
qtimer qtr ;
+ confirm(free_it == true) ;
- qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
+ qtr = heap_ream(&qtp->timers, keep_it) ; /* ream, keeping the heap */
if (qtr != NULL)
qtr->active = false ; /* has been removed from pile */
else
@@ -413,8 +414,8 @@ qtimer_pile_verify(qtimer_pile qtp)
{
heap th = &qtp->timers ;
vector v ;
- vector_index i ;
- vector_index e ;
+ vector_index_t i ;
+ vector_length_t e ;
qtimer qtr ;
bool seen ;
@@ -429,7 +430,7 @@ qtimer_pile_verify(qtimer_pile qtp)
assert(th->state == Heap_Has_Backlink) ;
assert(th->backlink_offset == offsetof(qtimer_t, backlink)) ;
- v = &th->v ;
+ v = th->v ;
e = vector_end(v) ;
for (i = 0 ; i < e ; ++i)
{
diff --git a/lib/qtimers.h b/lib/qtimers.h
index 5beb931b..8534a789 100644
--- a/lib/qtimers.h
+++ b/lib/qtimers.h
@@ -22,16 +22,12 @@
#ifndef _ZEBRA_QTIMERS_H
#define _ZEBRA_QTIMERS_H
-#include <stdbool.h>
+#include "misc.h"
#include "zassert.h"
#include "qtime.h"
#include "heap.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -76,7 +72,7 @@ struct qtimer_pile
extern qtimer_pile qtimer_pile_init_new(qtimer_pile qtp) ;
extern bool qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
extern qtime_t qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait) ;
-extern qtimer qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+extern qtimer qtimer_pile_ream(qtimer_pile qtp, free_keep_b free_structure) ;
/* Ream out qtimer pile and free the qtimer structure. */
#define qtimer_pile_ream_free(qtp) qtimer_pile_ream(qtp, 1)
diff --git a/lib/routemap.h b/lib/routemap.h
index ac6cb999..05f037f1 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_ROUTEMAP_H
#define _ZEBRA_ROUTEMAP_H
-#include <stdint.h>
+#include "misc.h"
/* Route map's type. */
enum route_map_type
diff --git a/lib/symtab.c b/lib/symtab.c
index 57a49396..cac52962 100644
--- a/lib/symtab.c
+++ b/lib/symtab.c
@@ -212,11 +212,11 @@ symbol_base(symbol_table table, u_int32_t hash)
* any existing chain base array, symbols and symbol references are simply
* discarded -- which will leak memory and is probably a mistake.
*/
-symbol_table
+extern symbol_table
symbol_table_init_new(symbol_table table,
- void* parent,
- unsigned int base_count,
- unsigned int density,
+ void* parent,
+ uint base_count,
+ uint density,
symbol_hash_function* hash_function,
symbol_call_back_function* value_call_back)
{
@@ -240,21 +240,21 @@ symbol_table_init_new(symbol_table table,
} ;
/* Set "parent" of symbol table. */
-void
+extern void
symbol_table_set_parent(symbol_table table, void* parent)
{
table->parent = parent ;
} ;
/* Get "parent" of symbol table. */
-void*
+extern void*
symbol_table_get_parent(symbol_table table)
{
return table->parent ;
} ;
/* Set the value_call_back */
-void
+extern void
symbol_table_set_value_call_back(symbol_table table,
symbol_call_back_function* value_call_back)
{
@@ -311,8 +311,8 @@ symbol_table_setup(symbol_table table)
*
* NB: must only be done when the table is empty -- see assertion !
*/
-symbol_table
-symbol_table_reset(symbol_table table, int free_structure)
+extern symbol_table
+symbol_table_reset(symbol_table table, free_keep_b free_structure)
{
if (table== NULL)
return NULL ; /* allow for already freed table */
@@ -322,6 +322,8 @@ symbol_table_reset(symbol_table table, int free_structure)
if (table->bases)
XFREE(MTYPE_SYMBOL_BASES, table->bases);
+ confirm(free_it == true) ;
+
if (free_structure)
{
XFREE(MTYPE_VECTOR, table) ;
@@ -416,8 +418,8 @@ symbol_free(symbol sym)
* NB: it is the caller's responsibility to unset all references and release
* any that need to be released -- either before or after this operation.
*/
-void*
-symbol_table_ream(symbol_table table, int free_structure)
+extern void*
+symbol_table_ream(symbol_table table, free_keep_b free_structure)
{
void* value ;
symbol sym ;
@@ -470,8 +472,8 @@ symbol_table_ream(symbol_table table, int free_structure)
* value and no references. Where that distinction matters, it is
* necessary to do an extra lookup.
*/
-symbol
-symbol_lookup(symbol_table table, const void* name, int add)
+extern symbol
+symbol_lookup(symbol_table table, const void* name, add_b add)
{
struct symbol* this ;
struct symbol** base ;
@@ -485,7 +487,7 @@ symbol_lookup(symbol_table table, const void* name, int add)
base = symbol_base(table, hash.hash) ;
this = *base ;
- while (this)
+ while (this != NULL)
{
if ((this->hash == hash.hash)
&& (this->name_len == hash.name_len)
@@ -554,7 +556,7 @@ symbol_lookup(symbol_table table, const void* name, int add)
* NB: orphan symbols can be deleted. The effect is to free the symbol if
* possible.
*/
-void*
+extern void*
symbol_delete(symbol sym)
{
void* old_value = symbol_unset_value(sym) ;
@@ -578,7 +580,7 @@ symbol_delete(symbol sym)
static u_int32_t crc_table[] ;
/* Standard symbol string hash function. */
-void
+extern void
symbol_hash_string(symbol_hash p_hash, const char* string) {
u_int32_t h = 0 ;
const char* p = string ;
@@ -595,7 +597,7 @@ symbol_hash_string(symbol_hash p_hash, const char* string) {
} ;
/* Standard symbol byte vector hash function. */
-void
+extern void
symbol_hash_bytes(symbol_hash p_hash, const void* bytes, size_t len) {
assert(len < 0xFFFF) ;
@@ -670,8 +672,8 @@ symbol_extend_bases(symbol_table table)
/* Zeroise the reference count.*/
-symbol
-symbol_zero_ref(symbol sym, int force)
+Private symbol
+symbol_zero_ref(symbol sym, bool force)
{
assert((sym->ref_count == 1) || force) ;
@@ -807,7 +809,7 @@ symbol_ref_is_bookmark(symbol_ref ref)
} ;
/* Start walk of symbol references */
-void
+extern void
symbol_ref_walk_start(symbol sym, symbol_ref walk)
{
symbol_init_ref(walk) ; /* keeping things tidy */
@@ -817,7 +819,7 @@ symbol_ref_walk_start(symbol sym, symbol_ref walk)
} ;
/* Step walk and return the next reference (if any). */
-symbol_ref
+extern symbol_ref
symbol_ref_walk_step(symbol_ref walk)
{
symbol_ref next_ref ;
@@ -850,7 +852,7 @@ symbol_ref_walk_step(symbol_ref walk)
* NB: if the symbol is not defined and has no references or bookmarks it
* will now be freed.
*/
-void
+extern void
symbol_ref_walk_end(symbol_ref walk)
{
assert(symbol_ref_is_bookmark(walk)) ; /* must be a bookmark ! */
@@ -876,7 +878,7 @@ symbol_ref_walk_end(symbol_ref walk)
*
* Returns previous value -- which may require releasing.
*/
-void*
+extern void*
symbol_set_value(symbol sym, void* new_value)
{
void* old_value ;
@@ -912,7 +914,7 @@ symbol_set_value(symbol sym, void* new_value)
*/
/* Initialise symbol reference -- allocate if required. */
-symbol_ref
+extern symbol_ref
symbol_init_ref(symbol_ref ref)
{
if (ref == NULL)
@@ -936,7 +938,7 @@ symbol_init_ref(symbol_ref ref)
*
* if reference is not allocated, the parent and tag are unchanged.
*/
-symbol_ref
+extern symbol_ref
symbol_set_ref(symbol_ref ref, struct symbol* sym)
{
if (ref != NULL)
@@ -944,7 +946,7 @@ symbol_set_ref(symbol_ref ref, struct symbol* sym)
if (ref->sym == sym)
return ref ; /* Nothing more to do if already set to given value */
if (ref->sym != NULL)
- symbol_unset_ref_keep(ref) ;
+ symbol_unset_ref(ref, keep_it) ;
}
else
ref = symbol_init_ref(NULL) ;
@@ -967,8 +969,8 @@ symbol_set_ref(symbol_ref ref, struct symbol* sym)
*
* NB: copes if the reference is already unset, of course.
*/
-symbol_ref
-symbol_unset_ref(symbol_ref ref, int free_ref_structure)
+extern symbol_ref
+symbol_unset_ref(symbol_ref ref, free_keep_b free_ref_structure)
{
if (ref == NULL) return ref ;
@@ -979,6 +981,7 @@ symbol_unset_ref(symbol_ref ref, int free_ref_structure)
ref->sym = NULL ;
} ;
+ confirm(free_it == true) ;
if (free_ref_structure)
XFREE(MTYPE_SYMBOL_REF, ref) ; /* ref is set to NULL */
@@ -1008,7 +1011,7 @@ symbol_unset_ref(symbol_ref ref, int free_ref_structure)
* progress -- up to and including deleting it. Any other changes to
* the table must NOT be attempted.
*/
-void
+extern void
symbol_walk_start(symbol_table table, struct symbol_walker* walker)
{
walker->next = NULL ;
@@ -1016,7 +1019,7 @@ symbol_walk_start(symbol_table table, struct symbol_walker* walker)
walker->base_count = table->base_count ;
} ;
-symbol
+extern symbol
symbol_walk_next(struct symbol_walker* walker)
{
symbol this = walker->next ;
@@ -1049,9 +1052,9 @@ symbol_walk_next(struct symbol_walker* walker)
* caller's responsibility to avoid deleting any symbol whose pointer
* in the vector they expect to rely on !
*/
-vector
+extern vector
symbol_table_extract(symbol_table table,
- symbol_select_cmp* selector, const void* p_val, int most,
+ symbol_select_cmp* selector, const void* p_val, bool most,
symbol_sort_cmp* sort)
{
vector extract ;
@@ -1093,7 +1096,7 @@ symbol_table_extract(symbol_table table,
*
* This comparison treats substrings of digits as numbers, so "a10" is > "a1".
*/
-int
+extern int
symbol_mixed_name_cmp(const symbol* p_a,
const symbol* p_b)
{
diff --git a/lib/symtab.h b/lib/symtab.h
index a8a6e622..4e33390e 100644
--- a/lib/symtab.h
+++ b/lib/symtab.h
@@ -22,14 +22,8 @@
#ifndef _ZEBRA_SYMTAB_H
#define _ZEBRA_SYMTAB_H
+#include "misc.h"
#include "vector.h"
-#include <stddef.h>
-#include <stdint.h>
-
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
/* Maximum number of symbol table bases -- something has gone tragically wrong
* if we hit this. Assume can multiply this by 2 and get valid size_t result.
@@ -145,21 +139,18 @@ struct symbol_walker
/* Symbol Table Operations. */
-extern symbol_table
-symbol_table_init_new(symbol_table table,
- void* parent,
- unsigned bases,
- unsigned density,
- symbol_hash_function* hash_function,
- symbol_call_back_function* value_call_back) ;
-void symbol_table_set_parent(symbol_table table, void* parent) ;
-void* symbol_table_get_parent(symbol_table table) ;
-void* symbol_table_ream(symbol_table table, int free_structure) ;
-#define symbol_table_ream_free(table) symbol_table_ream(table, 1)
-#define symbol_table_ream_keep(table) symbol_table_ream(table, 0)
-symbol_table symbol_table_reset(symbol_table table, int free_structure) ;
-#define symbol_table_reset_free(table) symbol_table_reset(table, 1)
-#define symbol_table_reset_keep(table) symbol_table_reset(table, 0)
+extern symbol_table symbol_table_init_new(
+ symbol_table table,
+ void* parent,
+ uint bases,
+ uint density,
+ symbol_hash_function* hash_function,
+ symbol_call_back_function* value_call_back) ;
+extern void symbol_table_set_parent(symbol_table table, void* parent) ;
+extern void* symbol_table_get_parent(symbol_table table) ;
+extern void* symbol_table_ream(symbol_table table, free_keep_b free_structure) ;
+extern symbol_table symbol_table_reset(symbol_table table,
+ free_keep_b free_structure) ;
extern void symbol_hash_string(struct symbol_hash* p_hash, const char* string) ;
extern void symbol_hash_bytes(struct symbol_hash* p_hash, const void* bytes,
@@ -169,28 +160,31 @@ extern void symbol_table_set_value_call_back(symbol_table table,
extern void symbol_table_free(symbol_table) ;
-extern symbol symbol_lookup(symbol_table table, const void* name, int add) ;
-
-#define symbol_seek(table, name) symbol_lookup(table, name, 0)
-#define symbol_find(table, name) symbol_lookup(table, name, 1)
+extern symbol symbol_lookup(symbol_table table, const void* name, add_b add) ;
extern void* symbol_delete(symbol sym) ;
extern void* symbol_set_value(symbol sym, void* new_value) ;
-#define symbol_unset_value(sym) symbol_set_value(sym, NULL)
+Inline void*
+symbol_unset_value(symbol sym)
+{
+ symbol_set_value(sym, NULL) ;
+} ;
-void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
-symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
-void symbol_ref_walk_end(symbol_ref walk) ;
+extern void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
+extern symbol_ref symbol_ref_walk_step(symbol_ref walk) ;
+extern void symbol_ref_walk_end(symbol_ref walk) ;
-void symbol_walk_start(symbol_table table, struct symbol_walker* walker) ;
-symbol symbol_walk_next(struct symbol_walker* walker) ;
+extern void symbol_walk_start(symbol_table table, struct symbol_walker* walker);
+extern symbol symbol_walk_next(struct symbol_walker* walker) ;
typedef int symbol_select_cmp(const symbol, const void*) ;
typedef int symbol_sort_cmp(const symbol*, const symbol*) ;
-vector symbol_table_extract(symbol_table table,
- symbol_select_cmp* select, const void* p_value,
- int most, symbol_sort_cmp* sort) ;
+extern vector symbol_table_extract(symbol_table table,
+ symbol_select_cmp* select,
+ const void* p_value,
+ bool most,
+ symbol_sort_cmp* sort) ;
extern symbol_sort_cmp symbol_mixed_name_cmp ;
@@ -226,29 +220,22 @@ symbol_inc_ref(symbol sym)
return sym ;
} ;
-extern symbol symbol_zero_ref(symbol sym, int force) ;
+Private symbol symbol_zero_ref(symbol sym, bool force) ;
Inline symbol
symbol_dec_ref(symbol sym)
{
if (sym->ref_count <= 1)
- return symbol_zero_ref(sym, 0) ;
+ return symbol_zero_ref(sym, false) ;
--sym->ref_count ;
return sym ;
} ;
-extern symbol_ref
-symbol_init_ref(symbol_ref ref) ;
-
-extern symbol_ref
-symbol_set_ref(symbol_ref ref, symbol sym) ;
-
-extern symbol_ref
-symbol_unset_ref(symbol_ref ref, int free_ref_structure) ;
-
-#define symbol_unset_ref_free(ref) symbol_unset_ref(ref, 1) ;
-#define symbol_unset_ref_keep(ref) symbol_unset_ref(ref, 0) ;
+extern symbol_ref symbol_init_ref(symbol_ref ref) ;
+extern symbol_ref symbol_set_ref(symbol_ref ref, symbol sym) ;
+extern symbol_ref symbol_unset_ref(symbol_ref ref,
+ free_keep_b free_ref_structure) ;
/* Access functions -- argument is address of symbol_ref. */
/* These cope if address of symbol_ref is null, or reference is undefined. */
diff --git a/lib/temp.c b/lib/temp.c
new file mode 100644
index 00000000..759c2bb3
--- /dev/null
+++ b/lib/temp.c
@@ -0,0 +1,1349 @@
+/* Generic vector interface routine -- functions
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "memory.h"
+
+/* Vectors are implemented as a structure which points to an array of pointers
+ * to vector items. That array -- the body of the vector -- can change size,
+ * and therefore may move around in memory.
+ *
+ * The vector structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the vector operations
+ * require the address of the vector structure -- see typedef for vector.
+ *
+ * Vector items are accessed by index, fetching and storing pointers to
+ * those items. Can also push and pop items.
+ *
+ * A vector has a known (logical) end position. Everything beyond the end is
+ * defined to be NULL. When a vector is initialised it is set empty.
+ *
+ * At any given moment the vector body has a limit on the number of items it
+ * can accommodate (the physical end).
+ *
+ * A vector will grow to accommodate what is put into it. Adding items beyond
+ * the (logical) end moves it. Adding items beyond the (physical) limit causes
+ * the body to be extended to suit, and to leave some spare space for future
+ * expansion.
+ *
+ * While the vector is small (see VECTOR_LIMIT_DOUBLE_MAX) the body will grow by
+ * doubling in size. When it is larger, it grows to be multiples of
+ * VECTOR_LIMIT_DOUBLE_MAX.
+ *
+ * Deleting items reduces the (logical) end position, but does NOT release
+ * memory -- the (physical) limit is not changed.
+ *
+ * To release memory: vector_chop will release everything beyond the current
+ * end; vector_decant will create a new body of exactly the current size,
+ * releasing the old body; vector_discard will release everything beyond a
+ * given position.
+ *
+ * NB: you can set a vector item to be NULL. If you set a vector item beyond
+ * the current end, NULL items are inserted in the vector.
+ *
+ * NB: when setting a vector item it is the caller's responsibility to
+ * deallocate any pre-existing value of the item.
+ *
+ * NB: when deleting items it is also the caller's responsibility to deallocate
+ * any values that require it.
+ *
+ * Implementation Notes
+ *
+ * Everything beyond the (logical) end is implicitly NULL.
+ *
+ * Actual memory between (logical) end and (physical) limit is UNDEFINED. So
+ * when advancing the end some care has to be taken to ensure that any new
+ * items in the vector are either set to something or cleared to NULL.
+ *
+ * It would have been possible to ensure that everything between end and limit
+ * is cleared to NULL, but that is more work -- in particular it creates work
+ * when it is not always required.
+ */
+
+#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new vector, setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, otherwise body of exactly
+ * the required size is allocated.
+ *
+ * NB: discards any existing vector body -- so it is the caller's responsibility
+ * to release any existing body, and any items in that body.
+ */
+extern vector
+vector_init_new(vector v, vector_length_t limit)
+{
+ if (v == NULL)
+ v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
+ else
+ memset(v, 0, sizeof(struct vector)) ;
+
+ if (limit != 0)
+ {
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialize vector : allocate memory and return vector.
+ * allocates body with at least 1 entry.
+ *
+ * This is a "legacy" function.
+ */
+extern vector
+vector_init (vector_length_t limit)
+{
+ return vector_init_new(NULL, limit ? limit : 1) ; /* at least 1 entry */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Basic: free the vector body and the vector structure.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+void
+vector_free (vector v)
+{
+ XFREE (MTYPE_VECTOR_BODY, v->p_items);
+ XFREE (MTYPE_VECTOR, v);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise vector (or create new one), setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, but any existing body is
+ * retained. (vector_reset() will discard body.)
+ *
+ * Otherwise ensures existing body is at least the required size, or a body
+ * of exactly the required size is allocated.
+ *
+ * NB: when re-initialising an existing vector it is the caller's responsibility
+ * to release any vector item values *before* doing this.
+ * */
+extern vector
+vector_re_init(vector v, vector_length_t)
+{
+ if ((v == NULL) || (v->p_items == NULL))
+ return vector_init_new(v, limit) ;
+
+ v->end = 0 ;
+
+ if (v->limit < size)
+ {
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the vector body, and (if required) free the vector structure.
+ *
+ * Return NULL if releases vector, otherwise the address of the vector.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+vector
+vector_reset(vector v, free_keep_b free_structure)
+{
+ if (v == NULL)
+ return NULL ; /* allow for already freed vector */
+
+ if (v->p_items != NULL)
+ XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
+
+ if (free_structure)
+ {
+ confirm(free_it == true) ;
+ XFREE(MTYPE_VECTOR, v) ;
+ return NULL ;
+ }
+ else
+ return vector_init_new(v, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set vector length to be (at least) the given fixed length.
+ *
+ * There must be a vector.
+ *
+ * Does nothing if the vector is already as long or longer than the given
+ * length.
+ *
+ * If the body is not big enough for the new length, allocates or extends to
+ * exactly the new length. Otherwise, leaves body as it is.
+ *
+ * Appends NULLs as required to extend to the required length.
+ *
+ * Note that the existing contents of the vector are preserved in all cases.
+ */
+Private void
+vector_set_new_min_length(vector v, vector_length_t len)
+{
+ assert (v != NULL) ;
+
+ if (len > v->limit)
+ {
+ if (v->p_items == NULL)
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
+ else
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(len)) ;
+ v->limit = len ;
+ } ;
+
+ if (v->end < len)
+ vector_extend(v, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop item from vector, stepping past any NULLs.
+ * If vector is empty, free the body and, if required, the vector structure.
+ *
+ * Useful for emptying out and discarding a vector:
+ *
+ * while ((p_v = vector_ream_out(v, 1)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL if vector was empty and has now been freed as required.
+ */
+p_vector_item
+vector_ream(vector v, free_keep_b free_structure)
+{
+ p_vector_item p_v ;
+
+ if (v == NULL)
+ return NULL ;
+
+ while (v->end != 0)
+ {
+ p_v = v->p_items[--v->end] ;
+ if (p_v != NULL)
+ return p_v ; /* return non-NULL item */
+ } ;
+
+ /* vector is empty: free the body, and (if required) the vector structure. */
+ vector_reset(v, free_structure) ;
+
+ return NULL ; /* signals end */
+} ;
+
+/*==============================================================================
+ * Unset item, condensing and trimming vector.
+ *
+ * These are legacy operations.
+ */
+
+/*------------------------------------------------------------------------------
+ * Unset item at given index (ie set it NULL).
+ *
+ * Return the old value of the item.
+ *
+ * If the item at the current (logical) end of the vector is NULL, move the
+ * end backwards until finds a non-NULL item, or the vector becomes empty.
+ */
+extern p_vector_item
+vector_unset_item(vector v, vector_index_t i)
+{
+ p_vector_item was ;
+
+ if (i < v->end)
+ {
+ was = v->p_items[i] ;
+ v->p_items[i] = NULL ;
+ }
+ else if (v->end == 0)
+ return NULL ; /* avoid test for last entry NULL if is empty */
+ else
+ was = NULL ;
+
+ if (v->p_items[v->end - 1] == NULL)
+ vector_trim(v) ;
+
+ return was ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_trim(vector v)
+{
+ vector_length_t e = v->end ;
+ while ((e > 0) && (v->p_items[e - 1] == NULL))
+ --e ;
+ v->end = e ;
+ return e ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_condense(vector v)
+{
+ vector_length_t e = 0 ;
+ vector_index_t j ;
+
+ /* Find first NULL, if any */
+ while ((e < v->end) && (v->p_items[e] != NULL))
+ ++e ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (e == v->end)
+ return e ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = e + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[e++] = v->p_items[j] ;
+
+ v->end = e ;
+
+ return e ;
+} ;
+
+/*==============================================================================
+ * Inserting and deleting items.
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector, before item at given position
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item(vector v, vector_index_t i, void* p_v)
+{
+ if ((i == v->end) && (i < v->limit))
+ ++v->end ;
+ else
+ vector_insert(v, i, 1) ;
+
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector at given position with rider:
+ *
+ * rider < 0 -- insert before the item at the given position
+ * rider == 0 -- insert at the given position -- REPLACING any existing value
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: when an item is replaced, it is the caller's responsibility to release
+ * any memory used by the item, if required.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item_here(vector v, vector_index_t i, int rider,
+ p_vector_item p_v)
+{
+ if (rider == 0)
+ return vector_set_item(v, i, p_v) ;
+
+ if (rider > 0)
+ ++i ; /* insert before next item */
+ vector_insert_item(v, i, p_v) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete item from vector.
+ *
+ * Move items as required. Reduces number of items in the vector (unless
+ * the item in question is beyond the end of the vector !)
+ *
+ * Returns: the item that has just been deleted.
+ *
+ * NB: it is the caller's responsibility to release memory used by any
+ * current value of the item, if required.
+ *
+ * NB: does NOT change the size of the vector body.
+ */
+extern p_vector_item
+vector_delete_item(vector v, vector_index_t i)
+{
+ p_vector_item p_e ;
+ if (i < v->end)
+ {
+ p_e = v->p_items[i] ; /* pick up the current value */
+ if (i != (v->end - 1))
+ vector_delete(v, i, 1) ;
+ else
+ v->end = i ;
+ return p_e ;
+ }
+ else
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Moving items within vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Move item in vector from source position to destination position.
+ *
+ * Moves intervening items up or down as required.
+ *
+ * Extends vector to include the destination, if required.
+ *
+ * A source item beyond the end of the vector is implicitly NULL.
+ */
+extern void
+vector_move_item(vector v, vector_index_t i_dst, vector_index_t i_src)
+{
+ p_vector_item* pp_s ;
+ p_vector_item* pp_d ;
+ p_vector_item p_e ;
+
+ vector_length_t old_end = v->end ;
+
+ /* Worry about whether both source and destination exist. */
+ if (i_dst >= old_end)
+ {
+ vector_insert(v, i_dst, 1) ; /* ensure destination exists */
+ if (i_src >= old_end)
+ return ; /* both were beyond the end */
+ }
+ else if (i_src >= old_end)
+ {
+ i_src = old_end ; /* clamp to just beyond last */
+ vector_insert(v, i_src, 1) ; /* create empty entry */
+ } ;
+
+ if (i_dst == i_src) /* avoid work and edge case */
+ return ;
+
+ /* Both src and dst are within the vector and src != dst */
+ pp_s = &v->p_items[i_src] ; /* address of src entry */
+ pp_d = &v->p_items[i_dst] ; /* address of dst entry */
+ p_e = *pp_s ; /* pick up item to move */
+ if (i_src < i_dst)
+ memmove(pp_s, pp_s+1, P_ITEMS_SIZE(i_dst - i_src)) ;
+ else
+ memmove(pp_d+1, pp_d, P_ITEMS_SIZE(i_src - i_dst)) ;
+ *pp_d = p_e ; /* put down the item to move */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move item in vector to given position with rider:
+ *
+ * rider < 0 -- move to before the item at the given position
+ * rider == 0 -- move to replace item at the given position
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: it is the caller's responsibility to release the any existing value
+ * that will be replaced.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_move_item_here(vector v, vector_index_t i_dst, int rider,
+ vector_index_t i_src)
+{
+ if (rider != 0)
+ {
+ if (rider > 0)
+ ++i_dst ;
+ vector_move_item(v, i_dst, i_src) ;
+ }
+ else
+ {
+ /* to replace: copy and then delete. */
+ vector_set_item(v, i_dst, vector_get_item(v, i_src)) ;
+ vector_delete_item(v, i_src) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse vector: reverse the order of items in the vector.
+ */
+extern void
+vector_reverse(vector v)
+{
+ if (v != NULL)
+ vector_part_reverse(v, 0, v->end) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse portion of vector.
+ */
+extern void
+vector_part_reverse(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_index_t j ;
+
+ if (v == NULL)
+ return ;
+
+ if ((i + n) > v->limit)
+ vector_extend(v, i + n) ; /* ensure portion exists */
+
+ if (n <= 1)
+ return ;
+
+ j = i + n - 1 ; /* j > i, because n > 1 */
+ do
+ {
+ p_vector_item p_i = v->p_items[i] ;
+ v->p_items[i++] = v->p_items[j] ;
+ v->p_items[j--] = p_i ;
+ } while (j > i) ;
+} ;
+
+/*==============================================================================
+ * Copying, moving and appending entire vectors.
+ */
+
+static void vector_new_limit(vector v, vector_length_t new_end) ;
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ *
+ * Creates a new vector.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ */
+vector
+vector_copy (vector v)
+{
+ vector new = vector_init_new(NULL, v->limit) ;
+
+ new->end = v->end;
+
+ if (v->limit > 0)
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->end)) ;
+
+ return new;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ * Creates a new vector or re-initialises an existing one.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try copying a vector to itself !!
+ */
+vector
+vector_copy_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ dst = vector_re_init(dst, src->limit) ;
+
+ dst->end = src->end;
+
+ if (src->end > 0)
+ memcpy(dst->p_items, src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move -- moves body of vector.
+ *
+ * Creates a new vector or re-initialises an existing one.
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if (dst != NULL)
+ dst = vector_reset(dst, 0) ; /* Reset to deallocate any existing body */
+ else
+ dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
+
+ *dst = *src ; /* Copy the vector structure */
+
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy append -- copies pointers to item values, not the values.
+ *
+ * Appends copied pointers to the destination vector.
+ *
+ * Creates a new destination vector if required.
+ *
+ * NB: Can append to self.
+ */
+extern vector
+vector_copy_append(vector dst, vector src)
+{
+ vector_index_t new_end ;
+
+ assert(src != NULL) ;
+
+ if (dst != NULL)
+ {
+ new_end = dst->end + src->end ;
+ if (new_end > dst->limit)
+ vector_new_limit(dst, new_end) ;
+ }
+ else
+ {
+ new_end = src->end ;
+ vector_init_new(dst, new_end) ;
+ } ;
+
+ if (src->end)
+ memcpy(&dst->p_items[dst->end], src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ dst->end = new_end ; /* Done last, allows for append to self ! */
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move append -- moves pointers to item values.
+ *
+ * Appends moved pointers to the destination vector.
+ *
+ * Creates a new destination vector if required (dst == NULL).
+ *
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_append(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if ((dst == NULL) || (dst->end == 0))
+ return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
+
+ vector_copy_append(dst, src) ; /* Extend dst and copy src */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+} ;
+
+/*==============================================================================
+ * Portmanteau splice/extract/replace function.
+ *
+ * All take a portion of 'src' vector and:
+ *
+ * splice:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * copying the replaced portion of the 'dst' vector to the 'to' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to', 'dst' and 'src' must be distinct vectors.
+ *
+ * extract:
+ *
+ * a) copy a portion of the 'src' vector to the 'to' vector
+ * c) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- NULL
+ * i_dst -- ignored
+ * n_dst -- ignored
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to' and 'src' must be distinct vectors.
+ *
+ * replace:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- false
+ * to -- ignored
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: original 'to' argument
+ *
+ * NB: 'dst' and 'src' must be distinct vectors.
+ *
+ * All copies are shallow -- pointers to item values are copied, not the values.
+ *
+ * NB: any existing contents of the 'to' vector are discarded.
+ *
+ * NB: it is the caller's responsibility to release memory allocated to any
+ * items which are discarded in these operations.
+ *
+ * NB: for splice and replace, the resulting destination vector will be at
+ * least i_dst + n_src long. (Even if is copying actual or implied NULLs
+ * from the source.)
+ *
+ * NB: where new vectors are created, they will be of exactly the required size.
+ *
+ * NB: where an existing vector is reused, it is the caller's responsibility
+ * to ensure the vector structure is currently valid (by vector_init_new()
+ * or by ensuring it is zeroized).
+ */
+extern vector
+vector_sak(int to_copy, vector to,
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move)
+{
+ int dst_replace ; /* true => replace portion of 'dst' */
+
+ vector_index_t new_dst_end = 0 ; /* new end of dst */
+
+ vector_length_t n_dst_nulls ; /* number of implicit NULLs to add */
+ vector_length_t n_dst_move ; /* number of items to move up or down */
+ vector_length_t n_src_real ; /* number of items to really copy */
+ vector_length_t n_src_nulls ; /* number of implicit NULLs to "copy" */
+
+ assert((to == NULL) || (dst == NULL) || (to != dst)) ;
+ assert((src == NULL) || (dst == NULL) || (src != dst)) ;
+
+ /* Worry about how much we really have in the source vector. */
+
+ n_src_real = n_src ; /* assume all real */
+ n_src_nulls = 0 ; /* so no NULLs to "copy" */
+
+ if (n_src != 0)
+ {
+ if ((src == NULL) || (i_src >= src->end))
+ n_src_real = 0 ;
+ else if ((i_src + n_src) > src->end)
+ n_src_real = src->end - i_src ;
+ n_src_nulls = n_src - n_src_real ;
+ } ;
+
+ /* If no 'dst' vector, then this is an extract. */
+
+ n_dst_move = 0 ; /* assume nothing to move */
+ n_dst_nulls = 0 ; /* assume no NULLs to add */
+
+ if (dst == NULL)
+ /* For extract: set up dst, i_dst and n_dst so that can copy to the */
+ /* 'to' vector as if from 'dst'. */
+ {
+ dst_replace = 0 ; /* no replacement operation */
+ dst = src ; /* copy from here */
+ i_dst = i_src ;
+ n_dst = n_src_real ;
+ }
+ else
+ /* Reduce n_dst to the number of actual items to be replaced. */
+ /* */
+ /* Calculate the new end of 'dst'. */
+ {
+ dst_replace = 1 ; /* have replacement to do */
+ if (i_dst >= dst->end)
+ /* If i_dst is beyond the end of 'dst', then there is nothing */
+ /* to replace (so set n_dst == 0). Will be adding n_src items */
+ /* at i_dst -- so new end must be i_dst + n_src. */
+ {
+ n_dst_nulls = i_dst - dst->end ; /* fill from end to i_dst */
+ n_dst = 0 ; /* nothing to replace */
+ new_dst_end = i_dst + n_src ; /* all beyond current end */
+ }
+ else
+ /* If i_dst + n_dst is beyond the end of 'dst', reduce n_dst to */
+ /* number of items up to the end. */
+ /* Will remove n_dst items and insert n_src, so end will move */
+ /* by n_src - n_dst. */
+ {
+ if ((i_dst + n_dst) > dst->end)
+ n_dst = dst->end - i_dst ; /* what we actually replace */
+ else if (n_dst != n_src)
+ n_dst_move = dst->end - (i_dst + n_dst) ;
+ /* what we move up or down */
+
+ new_dst_end = dst->end + n_src - n_dst ;
+ /* end depends on amount added */
+ /* & amount actually replaced */
+ } ;
+ } ;
+
+ /* Copy portion of 'dst' (or of 'src') to 'to', if required. */
+ /* */
+ /* Have arranged: n_dst -- number of items to copy, all existent */
+ /* dst -- vector to copy from -- if n_dst > 0 */
+ /* i_dst -- first item to copy -- if n_dst > 0 */
+
+ if (to_copy)
+ {
+ to = vector_re_init(to, n_dst) ; /* reinitialise or create */
+ to->end = n_dst ;
+ if (n_dst > 0)
+ memcpy(to->p_items, &dst->p_items[i_dst], P_ITEMS_SIZE(n_dst)) ;
+ } ;
+
+ /* Replace portion of 'dst' by portion of 'src', if required. */
+ /* */
+ /* Have arranged: */
+ /* */
+ /* new_dst_end -- end of dst once dust settles */
+ /* n_dst_nulls -- number of NULLs to insert at dst->end to fill up */
+ /* to i_dst (when i_dst is beyond old end.) */
+ /* n_dst_move -- number of items in dst to move up or down to */
+ /* leave n_src item hole at i_dst to fill in. */
+ /* n_src_real -- number of real src items at i_src to copy to dst */
+ /* at i_dst. */
+ /* n_src_nulls -- number of nulls to add to fill to i_dst + n_src. */
+
+ if (dst_replace)
+ {
+ if (new_dst_end > dst->limit) /* extend body if required */
+ vector_new_limit(dst, new_dst_end) ;
+
+ 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],
+ P_ITEMS_SIZE(n_dst_move)) ;
+ if (n_src_real > 0)
+ memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
+ P_ITEMS_SIZE(n_src_real)) ;
+ if (n_src_nulls > 0)
+ memset(&dst->p_items[i_dst + n_src_real], 0,
+ P_ITEMS_SIZE(n_src_nulls)) ;
+ dst->end = new_dst_end ;
+ } ;
+
+ /* Delete portion of 'src', if required (and have 'src' !) */
+
+ if (src_move && (n_src_real != 0))
+ vector_delete(src, i_src, n_src_real) ;
+
+ /* Done -- return 'to' vector as promised. */
+
+ return to ;
+} ;
+
+/*==============================================================================
+ * Legacy Vector Operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Set value to the smallest empty slot.
+ *
+ * Returns: index of slot used.
+ */
+extern int
+vector_set (vector v, void *val)
+{
+ vector_index_t i;
+
+ i = 0 ;
+ while (1)
+ {
+ if (i == v->end)
+ {
+ i = vector_extend_by_1(v) ;
+ break ;
+ }
+
+ if (v->p_items[i] == NULL)
+ break ;
+
+ ++i ;
+ } ;
+
+ v->p_items[i] = val;
+
+ return i ;
+}
+
+/*------------------------------------------------------------------------------
+ * Set value to specified index slot.
+ *
+ * Returns: index of slot (as given)
+ */
+extern int
+vector_set_index (vector v, vector_index_t i, void *val)
+{
+ vector_ensure (v, i);
+ v->p_items[i] = val;
+ return i;
+}
+
+/*------------------------------------------------------------------------------
+ * Look up vector -- get the i'th item.
+ *
+ * Returns: the i'th item -- NULL if item is null, or i >= logical len of vector
+ */
+extern p_vector_item
+vector_lookup (vector v, vector_index_t i)
+{
+ if (i >= v->end)
+ return NULL;
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Lookup vector, ensure it -- get i'th item and ensure logical len > i.
+ *
+ * Returns: the i'th item -- NULL if item is null
+ */
+extern p_vector_item
+vector_lookup_ensure (vector v, vector_index_t i)
+{
+ vector_ensure (v, i);
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Count the number of not empty slots.
+ */
+extern vector_length_t
+vector_count (vector v)
+{
+ vector_index_t i;
+ vector_length_t count = 0;
+
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] != NULL)
+ count++;
+
+ return count;
+}
+
+/*==============================================================================
+ * Sorting and Searching vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Sort the given vector.
+ *
+ * NB: the comparison function receives a pointer to the pointer to the
+ * vector item's value.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern void
+vector_sort(vector v, vector_sort_cmp* cmp)
+{
+ if (v->end <= 1)
+ return ; /* Stop dead if 0 or 1 items */
+
+ typedef int qsort_cmp(const void*, const void*) ;
+
+ qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Perform binary search on the given vector.
+ *
+ * The vector MUST be sorted in the order implied by the comparison function
+ * given. The vector need not contain unique values, but this search makes
+ * no effort to select any particular instance of a sequence of equals.
+ *
+ * Returns:
+ *
+ * result == 0: found an equal value.
+ * index returned is of first entry found which is equal to
+ * the value sought. There may be other equal values, before
+ * and/or after this one in the vector.
+ *
+ * result == +1: value not found and vector not empty.
+ * index returned is of the largest entry whose value is less
+ * than the value sought.
+ * (The value sought belongs after this point.)
+ *
+ * result == -1: value is less than everything in the vector, or the
+ * vector is empty.
+ * index returned is 0
+ *
+ * NB: The comparison function takes arguments which are:
+ *
+ * const void** pointer to pointer to value being searched for.
+ * const void** pointer to pointer to vector item to compare with
+ *
+ * The value being searched for need not be in the same form as a vector
+ * item. However, if it is then the same comparison function can be used
+ * to sort and search.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern vector_index_t
+vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
+ int* result)
+{
+ vector_index_t il, iv, ih ;
+ int c ;
+
+ if (v->end <= 1)
+ {
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
+ } ;
+
+ /* We have at least two items. */
+ il = 0 ;
+ ih = v->end - 1 ;
+
+ /* Pick off the edge cases: >= last and <= first. */
+ if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
+ {
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
+ } ;
+ if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
+ {
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
+ }
+
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
+ while (1)
+ {
+ iv = (il + ih) / 2 ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
+ c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
+ if (c == 0)
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
+ if (c < 0)
+ ih = iv ; /* step down iv > il, so new ih > il */
+ else
+ il = iv ; /* step up iv < ih, so new il < ih */
+ } ;
+} ;
+
+/*==============================================================================
+ * Mechanics for adding/deleting items and managing the vector (logical) end
+ * and (physical) limit.
+ */
+
+/* Extract the LS bit of unsigned integer 'x'. */
+#define lsbit(x) ((x) & ((~(x)) + 1))
+/* Round 'x' up to a multiple of 'm' */
+#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
+
+/*------------------------------------------------------------------------------
+ * Set new limit to suit new end for given vector.
+ *
+ * The new limit will be at least: VECTOR_LIMIT_MIN.
+ *
+ * While the vector is relatively small, the limit is doubled until there
+ * is at least 1/8 of the new vector free.
+ *
+ * Beyond VECTOR_LIMIT_DOUBLE_MAX, however, the limit is set to the
+ * smallest multiple of VECTOR_LIMIT_DOUBLE_MAX which gives at least
+ * VECTOR_LIMIT_SLACK_MIN free entries beyond the new end.
+ *
+ * This is an attempt to balance the cost of repeated reallocations of
+ * memory against the cost of possible wasted space at the end of the
+ * vector.
+ *
+ * NB: the new_limit depends entirely on the new end position. (Current
+ * end position is ignored.)
+ *
+ * NB: the new limit may be less than the current limit, in which case the
+ * vector body is reduced in size.
+ *
+ * Except for any size set when the vector is initialised, the vector body
+ * size will be a power of 2 or a multiple of VECTOR_LIMIT_DOUBLE_MAX.
+ * (Vectors are regular in their habits, which may help the memory allocator).
+ *
+ * TODO: what to do if calculation of new_limit overflows, or calculation
+ * of P_ITEMS_SIZE will ?
+ */
+static void
+vector_new_limit(vector v, vector_index_t new_end)
+{
+ vector_length_t old_limit = v->limit ;
+ vector_length_t new_limit ;
+
+ if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
+ {
+ new_limit = multiple(new_end + VECTOR_LIMIT_SLACK_MIN,
+ VECTOR_LIMIT_DOUBLE_MAX) ;
+ }
+ else
+ {
+ /* Want the new_limit to be a power of 2. */
+ /* If the old_limit was a power of 2, start from there. */
+ /* Otherwise start from a power of 2 less than new_end: either the */
+ /* minimum value or a value mid way to VECTOR_LIMIT_DOUBLE_MAX. */
+ if ( (old_limit != 0) && (old_limit == lsbit(old_limit))
+ && (new_end >= old_limit) )
+ new_limit = old_limit ;
+ else
+ new_limit = (new_end < VECTOR_LIMIT_MID) ? VECTOR_LIMIT_MIN
+ : VECTOR_LIMIT_MID ;
+ while (new_end > ((new_limit * 7) / 8))
+ new_limit *= 2 ;
+ } ;
+
+ v->p_items =
+ XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+
+ v->limit = new_limit ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend vector and set new (logical) end.
+ *
+ * Extends body if required.
+ * Ensures everything between old and new end is set NULL.
+ *
+ * NB: expects new end > old end, but copes with new end <= old end.
+ */
+extern void
+vector_extend(vector v, vector_length_t new_end)
+{
+ vector_length_t old_end = v->end ;
+
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (new_end > old_end)
+ memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert entries into vector: insert 'n' NULL entries at location 'i'.
+ *
+ * Updates end (and limit) to be at least 'i' + 'n'.
+ * (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
+ */
+extern void
+vector_insert(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+ vector_length_t n_above ;
+
+ /* If i < old end, then we are inserting n NULLs, and need
+ * to shuffle at least one item up.
+ * else we are setting new end to i + n and need to NULL
+ * fill from old end to the new end.
+ */
+ old_end = v->end ;
+ if (i < old_end)
+ {
+ if (n == 0)
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ new_end = old_end + n ;
+ }
+ else
+ {
+ n_above = 0 ; /* nothing to shuffle up. */
+ new_end = i + n ;
+ i = old_end ; /* where to zeroize from */
+ n = new_end - old_end ; /* how much to zeroize */
+ } ;
+
+ /* Now we extend the body if we need to. */
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (n_above > 0)
+ memmove(&v->p_items[i + n], &v->p_items[i], P_ITEMS_SIZE(n_above)) ;
+
+ memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete items from vector: delete 'n' items at location 'i'.
+ *
+ * Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
+ *
+ * Deletes from 'i' to end if less than 'n' items to the end.
+ *
+ * NB: does NOT change the size of the body.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being deleted.
+*/
+extern void
+vector_delete(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+
+ old_end = v->end ;
+
+ if ((i >= old_end) || (n == 0))
+ return ;
+
+ /* If i + n < old_end, we have 1 or more items to keep and move down */
+ if ((i + n) < old_end)
+ {
+ memmove(&v->p_items[i], &v->p_items[i + n],
+ P_ITEMS_SIZE(old_end - (i + n))) ;
+ new_end = old_end - n ;
+ }
+ else
+ {
+ new_end = i ; /* We are keeping nothing above 'i' */
+ /* NB: new_end < old_end */
+ } ;
+
+ v->end = new_end ; /* account for stuff dropped */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Discard entries from vector: discard entries from location 'i' onwards.
+ *
+ * Releases memory from 'i' onwards.
+ * Releases the body altogether if this sets the vector empty ('i' == 0).
+ * Sets new end of vector iff 'i' < current end.
+ *
+ * Does nothing if 'i' is beyond current limit (physical end) of vector.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being discarded.
+*/
+extern void
+vector_discard(vector v, vector_index_t i)
+{
+ if (i >= v->limit)
+ return ;
+
+ if (i == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ v->limit = i ;
+ if (i < v->end)
+ v->end = i ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(i)) ;
+ } ;
+} ;
+
+/* Chop vector at the current (logical) end.
+ *
+ * Releases the body altogether if the vector is currently empty.
+ */
+extern void
+vector_chop(vector v)
+{
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else if (new_limit != v->limit)
+ {
+ v->limit = new_limit ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ } ;
+} ;
+
+/* Decant vector into a new body allocated to current logical size, and
+ * release the old body.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_decant(vector v)
+{
+ p_vector_item* p_old_body ;
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ p_old_body = v->p_items ;
+
+ vector_init_new(v, new_limit) ; /* initialise with new body */
+
+ memcpy(v->p_items, p_old_body, P_ITEMS_SIZE(new_limit)) ;
+ /* copy the old body across */
+ v->end = new_limit ;
+
+ XFREE(MTYPE_VECTOR_BODY, p_old_body) ;
+ } ;
+} ;
diff --git a/lib/tstring.h b/lib/tstring.h
new file mode 100644
index 00000000..4dd45edb
--- /dev/null
+++ b/lib/tstring.h
@@ -0,0 +1,139 @@
+/* Temporary string handling -- header
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_TSTRING_H
+#define _ZEBRA_TSTRING_H
+
+#include "misc.h"
+#include "zassert.h"
+#include "memory.h"
+
+/*==============================================================================
+ * tstrings are allocated on the stack, but if (unexpectedly) the standard
+ * size is not enough, then they can be allocated dynamically.
+ *
+ * To declare a "tstring":
+ *
+ * tstring(foo, 64) ; // creates a "tstring" variable called "foo"
+ * // with 64 char buffer.
+ *
+ * Can then:
+ *
+ * s = tstring_set_len(foo, n) ; // ensures have buffer for n+1 chars
+ * s = tstring_set(foo, "...") ; // copies "..." (with '\0') to buffer
+ * s = tstring_set_n(foo, q, n) ; // copies n characters from q to buffer
+ * and '\0' terminates
+ *
+ * If can fit stuff in the buffer, will do so. Otherwise will allocate an
+ * MTYPE_TMP buffer to work in.
+ *
+ * And before leaving the scope of "foo" must:
+ *
+ * tstring_free(foo) ; // releases any dynamically allocated memory.
+ */
+
+struct tstring
+{
+ usize size ;
+ char* str ;
+ char* alloc ;
+} ;
+
+typedef struct tstring tstring[1] ;
+
+/* tstring(foo, 93) ; -- declare the variable "foo". */
+#define tstring_t(name, sz) \
+ char _zlxq_##name##_b[ ((sz) + 7) & 0xFFFFFFF8] ; \
+ tstring name = { { .size = ((sz) + 7) & 0xFFFFFFF8, \
+ .str = _zlxq_##name##_b, \
+ .alloc = NULL } }
+
+/*------------------------------------------------------------------------------
+ * Ensure the tstring "foo" can accomodate at least "len" characters plus the
+ * terminating '\0'.
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+Inline char*
+tstring_set_len(struct tstring* ts, usize len)
+{
+ if (len >= ts->size)
+ {
+ ts->size = len + 1 ;
+ ts->str = ts->alloc = XREALLOC(MTYPE_TMP, ts->alloc, len + 1) ;
+ } ;
+
+ return ts->str ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy "len" characters from "src" to the tstring "foo", and append a
+ * terminating '\0'.
+ *
+ * The "src" address is ignored if "len" == 0 (sets "foo" to be empty string).
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+static inline char*
+tstring_set_n(struct tstring* ts, const char* str, usize len)
+{
+ char* tss = tstring_set_len(ts, len) ;
+
+ if (len > 0)
+ memcpy(tss, str, len) ;
+ *(tss + len) = '\0' ;
+
+ return tss ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy the string "str" to the tstring "foo", with terminating '\0'.
+ *
+ * If "str" is NULL, sets "foo" to be an empty string.
+ *
+ * Returns: address of buffer
+ *
+ * NB: address of buffer may not be the same as returned by a previous operation
+ * on foo. Also, previous contents of foo may be lost.
+ */
+static inline char*
+tstring_set(struct tstring* ts, const char* str)
+{
+ return tstring_set_n(ts, str, (str != NULL) ? strlen(str) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If have dynamically allocated buffer for tstring "foo", release it now.
+ */
+static inline void
+tstring_free(struct tstring* ts)
+{
+ if (ts->alloc != NULL)
+ XFREE(MTYPE_TMP, ts->alloc) ; /* sets ts->alloc NULL */
+} ;
+
+#endif /* _ZEBRA_TSTRING_H */
diff --git a/lib/uty.h b/lib/uty.h
index d1a8b584..cc759673 100644
--- a/lib/uty.h
+++ b/lib/uty.h
@@ -24,20 +24,17 @@
#ifndef _ZEBRA_UTY_H
#define _ZEBRA_UTY_H
-#include <stdbool.h>
+#include "misc.h"
+#include "vargs.h"
#include "qpthreads.h"
#include "qpnexus.h"
#include "thread.h"
#include "list_util.h"
#include "vty.h"
+#include "vty_io_basic.h"
#include "node_type.h"
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
-
/*==============================================================================
* This is stuff which is used by the close family of:
*
@@ -67,15 +64,6 @@ extern vty_io vio_list_base ;
extern vty_io vio_monitors_base ;
extern vty_io vio_death_watch ;
-union vty_watch_dog
-{
- qtimer qnexus ; /* when running qnexus */
- struct thread* thread; /* when running threads */
- void* anon ;
-};
-
-extern union vty_watch_dog vty_watch_dog ;
-
extern struct thread_master* vty_master ;
extern unsigned long vty_timeout_val ;
@@ -125,7 +113,7 @@ extern int vty_assert_fail ;
#endif
Inline void
-VTY_LOCK(void)
+VTY_LOCK(void) /* if is qpthreads_enabled, lock vty_mutex */
{
qpt_mutex_lock(&vty_mutex) ;
if (VTY_DEBUG)
@@ -133,13 +121,19 @@ VTY_LOCK(void)
} ;
Inline void
-VTY_UNLOCK(void)
+VTY_UNLOCK(void) /* if is qpthreads_enabled, unlock vty_mutex */
{
if (VTY_DEBUG)
--vty_lock_count ;
qpt_mutex_unlock(&vty_mutex) ;
} ;
+Inline bool /* true => is (effectively) cli thread */
+vty_is_cli_thread(void)
+{
+ return !qpthreads_enabled || qpt_thread_is_self(vty_cli_nexus->thread_id) ;
+} ;
+
/* For debug (and documentation) purposes, will VTY_ASSERT_LOCKED where that
* is required.
*
@@ -167,9 +161,8 @@ VTY_ASSERT_LOCKED(void)
Inline void
VTY_ASSERT_CLI_THREAD(void)
{
- if (qpthreads_enabled)
- if (!qpt_thread_is_self(vty_cli_nexus->thread_id))
- VTY_ASSERT_FAILED() ;
+ if (!vty_is_cli_thread())
+ VTY_ASSERT_FAILED() ;
} ;
#else
diff --git a/lib/vargs.h b/lib/vargs.h
new file mode 100644
index 00000000..5f0207cb
--- /dev/null
+++ b/lib/vargs.h
@@ -0,0 +1,34 @@
+/* Variable arguments definitions
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VARGS_H
+#define _ZEBRA_VARGS_H
+
+#include <stdarg.h>
+
+/* GCC has printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+#endif /* _ZEBRA_VARGS_H */
diff --git a/lib/vector.c b/lib/vector.c
index 646f19a5..6df8d409 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -84,11 +84,13 @@
*/
#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+
/*==============================================================================
* Initialisation, allocation, reset etc.
*/
-/* Initialise a brand new vector, setting it empty.
+/*------------------------------------------------------------------------------
+ * Initialise a brand new vector, setting it empty.
*
* Allocates vector structure if none given -- that is, if v == NULL.
*
@@ -98,41 +100,50 @@
* NB: discards any existing vector body -- so it is the caller's responsibility
* to release any existing body, and any items in that body.
*/
-vector
-vector_init_new(vector v, unsigned int size)
+extern vector
+vector_init_new(vector v, vector_length_t limit)
{
if (v == NULL)
v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
else
memset(v, 0, sizeof(struct vector)) ;
- if (size != 0)
+ if (limit != 0)
{
- v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(size)) ;
- v->limit = size ;
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
} ;
return v ;
} ;
-/* Initialize vector : allocate memory and return vector.
+/*------------------------------------------------------------------------------
+ * Initialize vector : allocate memory and return vector.
* allocates body with at least 1 entry.
+ *
+ * This is a "legacy" function.
*/
-vector
-vector_init (unsigned int size)
+extern vector
+vector_init (vector_length_t limit)
{
- return vector_init_new(NULL, size ? size : 1) ; /* at least 1 entry */
+ return vector_init_new(NULL, limit ? limit : 1) ; /* at least 1 entry */
} ;
-/* Basic: free the vector body and the vector structure. */
+/*------------------------------------------------------------------------------
+ * Basic: free the vector body and the vector structure.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
void
vector_free (vector v)
{
XFREE (MTYPE_VECTOR_BODY, v->p_items);
XFREE (MTYPE_VECTOR, v);
-}
+} ;
-/* Re-Initialize vector (or create new one), setting it empty.
+/*------------------------------------------------------------------------------
+ * Re-initialise vector (or create new one), setting it empty.
*
* Allocates vector structure if none given -- that is, if v == NULL.
*
@@ -142,27 +153,29 @@ vector_free (vector v)
* Otherwise ensures existing body is at least the required size, or a body
* of exactly the required size is allocated.
*
- * NB: when re-initialising an existing vector it is the caller's
- * responsibility to release any vector item values *before* doing this.
+ * NB: when re-initialising an existing vector it is the caller's responsibility
+ * to release any vector item values *before* doing this.
* */
-vector
-vector_re_init(vector v, unsigned int size)
+extern vector
+vector_re_init(vector v, vector_length_t limit)
{
if ((v == NULL) || (v->p_items == NULL))
- return vector_init_new(v, size) ;
+ return vector_init_new(v, limit) ;
v->end = 0 ;
- if (v->limit < size)
+ if (v->limit < limit)
{
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(size)) ;
- v->limit = size ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
} ;
return v ;
} ;
-/* Free the vector body, and free the vector structure or reset it.
+/*------------------------------------------------------------------------------
+ * Free the vector body, and (if required) free the vector structure.
*
* Return NULL if releases vector, otherwise the address of the vector.
*
@@ -170,16 +183,17 @@ vector_re_init(vector v, unsigned int size)
* *before* doing this.
*/
vector
-vector_reset(vector v, int free_structure)
+vector_reset(vector v, free_keep_b free_structure)
{
if (v == NULL)
- return NULL ; /* allow for already freed vector */
+ return NULL ; /* allow for already freed vector */
if (v->p_items != NULL)
XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
if (free_structure)
{
+ confirm(free_it == true) ;
XFREE(MTYPE_VECTOR, v) ;
return NULL ;
}
@@ -187,7 +201,8 @@ vector_reset(vector v, int free_structure)
return vector_init_new(v, 0) ;
} ;
-/* Set vector length to be (at least) the given fixed length.
+/*------------------------------------------------------------------------------
+ * Set vector length to be (at least) the given fixed length.
*
* There must be a vector.
*
@@ -201,8 +216,8 @@ vector_reset(vector v, int free_structure)
*
* Note that the existing contents of the vector are preserved in all cases.
*/
-extern void
-vector_set_new_min_length(vector v, unsigned int len)
+Private void
+vector_set_new_min_length(vector v, vector_length_t len)
{
assert (v != NULL) ;
@@ -211,7 +226,8 @@ vector_set_new_min_length(vector v, unsigned int len)
if (v->p_items == NULL)
v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
else
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(len));
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(len)) ;
v->limit = len ;
} ;
@@ -219,8 +235,8 @@ vector_set_new_min_length(vector v, unsigned int len)
vector_extend(v, len) ;
} ;
-/* Pop item from vector, stepping past any NULLs.
- *
+/*------------------------------------------------------------------------------
+ * Pop item from vector, stepping past any NULLs.
* If vector is empty, free the body and, if required, the vector structure.
*
* Useful for emptying out and discarding a vector:
@@ -231,7 +247,7 @@ vector_set_new_min_length(vector v, unsigned int len)
* Returns NULL if vector was empty and has now been freed as required.
*/
p_vector_item
-vector_ream(vector v, int free_structure)
+vector_ream(vector v, free_keep_b free_structure)
{
p_vector_item p_v ;
@@ -242,61 +258,23 @@ vector_ream(vector v, int free_structure)
{
p_v = v->p_items[--v->end] ;
if (p_v != NULL)
- return p_v ; /* return non-NULL item */
+ return p_v ; /* return non-NULL item */
} ;
/* vector is empty: free the body, and (if required) the vector structure. */
vector_reset(v, free_structure) ;
- return NULL ; /* signals end */
+ return NULL ; /* signals end */
} ;
/*==============================================================================
* Unset item, condensing and trimming vector.
- */
-
-/* Trim any NULL entries at the current (logical) end of the vector.
*
- * Returns the (new) end of the vector.
+ * These are legacy operations.
*/
-extern vector_index
-vector_trim(vector v)
-{
- vector_index i = v->end ;
- while ((i > 0) && (v->p_items[i - 1] == NULL))
- --i ;
- v->end = i ;
- return i ;
-} ;
-/* Removes any NULL entries from the given vector.
- *
- * Returns the (new) end of the vector.
- */
-extern vector_index vector_condense(vector v)
-{
- vector_index i = 0 ;
- vector_index j ;
-
- /* Find first NULL, if any */
- while ((i < v->end) && (v->p_items[i] != NULL))
- ++i ;
-
- /* Quit if no NULLs (or vector is empty) */
- if (i == v->end)
- return i ;
-
- /* Shuffle any remaining non-NULL down */
- for (j = i + 1 ; j < v->end ; ++j)
- if (v->p_items[j] != NULL)
- v->p_items[i++] = v->p_items[j] ;
-
- v->end = i ;
-
- return i ;
-} ;
-
-/* Unset item at given index (ie set it NULL).
+/*------------------------------------------------------------------------------
+ * Unset item at given index (ie set it NULL).
*
* Return the old value of the item.
*
@@ -304,7 +282,7 @@ extern vector_index vector_condense(vector v)
* end backwards until finds a non-NULL item, or the vector becomes empty.
*/
extern p_vector_item
-vector_unset_item(vector v, vector_index i)
+vector_unset_item(vector v, vector_index_t i)
{
p_vector_item was ;
@@ -324,15 +302,60 @@ vector_unset_item(vector v, vector_index i)
return was ;
} ;
+/*------------------------------------------------------------------------------
+ * Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_trim(vector v)
+{
+ vector_length_t e = v->end ;
+ while ((e > 0) && (v->p_items[e - 1] == NULL))
+ --e ;
+ v->end = e ;
+ return e ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_condense(vector v)
+{
+ vector_length_t e = 0 ;
+ vector_index_t j ;
+
+ /* Find first NULL, if any */
+ while ((e < v->end) && (v->p_items[e] != NULL))
+ ++e ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (e == v->end)
+ return e ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = e + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[e++] = v->p_items[j] ;
+
+ v->end = e ;
+
+ return e ;
+} ;
+
/*==============================================================================
* Inserting and deleting items.
*/
-/* Insert item in vector, before item at given position
+/*------------------------------------------------------------------------------
+ * Insert item in vector, before item at given position
* Move items and extend vector as required.
*/
-void
-vector_insert_item(vector v, vector_index i, void* p_v)
+extern void
+vector_insert_item(vector v, vector_index_t i, void* p_v)
{
if ((i == v->end) && (i < v->limit))
++v->end ;
@@ -342,7 +365,8 @@ vector_insert_item(vector v, vector_index i, void* p_v)
v->p_items[i] = (p_vector_item)p_v ;
} ;
-/* Insert item in vector at given position with rider:
+/*------------------------------------------------------------------------------
+ * Insert item in vector at given position with rider:
*
* rider < 0 -- insert before the item at the given position
* rider == 0 -- insert at the given position -- REPLACING any existing value
@@ -353,8 +377,8 @@ vector_insert_item(vector v, vector_index i, void* p_v)
*
* Move items and extend vector as required.
*/
-void
-vector_insert_item_here(vector v, vector_index i, int rider,
+extern void
+vector_insert_item_here(vector v, vector_index_t i, int rider,
p_vector_item p_v)
{
if (rider == 0)
@@ -365,18 +389,21 @@ vector_insert_item_here(vector v, vector_index i, int rider,
vector_insert_item(v, i, p_v) ;
} ;
-/* Delete item from vector.
+/*------------------------------------------------------------------------------
+ * Delete item from vector.
*
* Move items as required. Reduces number of items in the vector (unless
* the item in question is beyond the end of the vector !)
*
+ * Returns: the item that has just been deleted.
+ *
* NB: it is the caller's responsibility to release memory used by any
* current value of the item, if required.
*
* NB: does NOT change the size of the vector body.
*/
-p_vector_item
-vector_delete_item(vector v, vector_index i)
+extern p_vector_item
+vector_delete_item(vector v, vector_index_t i)
{
p_vector_item p_e ;
if (i < v->end)
@@ -396,19 +423,23 @@ vector_delete_item(vector v, vector_index i)
* Moving items within vector.
*/
-/* Move item in vector from source position to destination position.
+/*------------------------------------------------------------------------------
+ * Move item in vector from source position to destination position.
+ *
* Moves intervening items up or down as required.
+ *
* Extends vector to include the destination, if required.
+ *
* A source item beyond the end of the vector is implicitly NULL.
*/
-void
-vector_move_item(vector v, vector_index i_dst, vector_index i_src)
+extern void
+vector_move_item(vector v, vector_index_t i_dst, vector_index_t i_src)
{
p_vector_item* pp_s ;
p_vector_item* pp_d ;
p_vector_item p_e ;
- vector_index old_end = v->end ;
+ vector_length_t old_end = v->end ;
/* Worry about whether both source and destination exist. */
if (i_dst >= old_end)
@@ -437,7 +468,8 @@ vector_move_item(vector v, vector_index i_dst, vector_index i_src)
*pp_d = p_e ; /* put down the item to move */
} ;
-/* Move item in vector to given position with rider:
+/*------------------------------------------------------------------------------
+ * Move item in vector to given position with rider:
*
* rider < 0 -- move to before the item at the given position
* rider == 0 -- move to replace item at the given position
@@ -448,14 +480,14 @@ vector_move_item(vector v, vector_index i_dst, vector_index i_src)
*
* Move items and extend vector as required.
*/
-void
-vector_move_item_here(vector v, vector_index i_dst, int rider,
- vector_index i_src)
+extern void
+vector_move_item_here(vector v, vector_index_t i_dst, int rider,
+ vector_index_t i_src)
{
if (rider != 0)
{
if (rider > 0)
- ++i_dst ;
+ ++i_dst ;
vector_move_item(v, i_dst, i_src) ;
}
else
@@ -466,21 +498,23 @@ vector_move_item_here(vector v, vector_index i_dst, int rider,
} ;
} ;
-/* Reverse vector: reverse the order of items in the vector.
+/*------------------------------------------------------------------------------
+ * Reverse vector: reverse the order of items in the vector.
*/
-void
+extern void
vector_reverse(vector v)
{
if (v != NULL)
vector_part_reverse(v, 0, v->end) ;
} ;
-/* Reverse portion of vector.
+/*------------------------------------------------------------------------------
+ * Reverse portion of vector.
*/
-void
-vector_part_reverse(vector v, vector_index i, unsigned int n)
+extern void
+vector_part_reverse(vector v, vector_index_t i, vector_length_t n)
{
- vector_index j ;
+ vector_index_t j ;
if (v == NULL)
return ;
@@ -504,12 +538,16 @@ vector_part_reverse(vector v, vector_index i, unsigned int n)
* Copying, moving and appending entire vectors.
*/
-static void vector_new_limit(vector v, vector_index new_end) ;
+static void vector_new_limit(vector v, vector_length_t new_end) ;
-/* Shallow vector copy -- copies pointers to item values, not the values. */
-/* Creates a new vector. */
-/* NB: copies whole body, including stuff beyond (logical) end ! */
-/* TODO: is this behaviour required ? */
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ *
+ * Creates a new vector.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ */
vector
vector_copy (vector v)
{
@@ -518,12 +556,13 @@ vector_copy (vector v)
new->end = v->end;
if (v->limit > 0)
- memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->limit)) ;
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->end)) ;
return new;
}
-/* Shallow vector copy -- copies pointers to item values, not the values.
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
* Creates a new vector or re-initialises an existing one.
*
* NB: creates new vector with same limit as existing one, but copies only
@@ -552,7 +591,9 @@ vector_copy_here(vector dst, vector src)
return dst ;
} ;
-/* Vector move -- moves body of vector.
+/*------------------------------------------------------------------------------
+ * Vector move -- moves body of vector.
+ *
* Creates a new vector or re-initialises an existing one.
* Leaves the source vector empty -- does not release the structure.
*
@@ -564,7 +605,7 @@ vector_copy_here(vector dst, vector src)
*
* NB: do NOT try moving a vector to itself !!
*/
-vector
+extern vector
vector_move_here(vector dst, vector src)
{
assert((src != NULL) && (src != dst)) ;
@@ -574,23 +615,26 @@ vector_move_here(vector dst, vector src)
else
dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
- *dst = *src ; /* Copy the vector structure */
+ *dst = *src ; /* Copy the vector structure */
- vector_init_new(src, 0) ; /* Set empty, forgetting body */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
return dst ;
}
-/* Shallow vector copy append -- copies pointers to item values, not the values.
+/*------------------------------------------------------------------------------
+ * Shallow vector copy append -- copies pointers to item values, not the values.
+ *
* Appends copied pointers to the destination vector.
+ *
* Creates a new destination vector if required.
*
* NB: Can append to self.
*/
-vector
+extern vector
vector_copy_append(vector dst, vector src)
{
- vector_index new_end ;
+ vector_index_t new_end ;
assert(src != NULL) ;
@@ -613,14 +657,18 @@ vector_copy_append(vector dst, vector src)
return dst ;
} ;
-/* Vector move append -- moves pointers to item values.
+/*------------------------------------------------------------------------------
+ * Vector move append -- moves pointers to item values.
+ *
* Appends moved pointers to the destination vector.
+ *
* Creates a new destination vector if required (dst == NULL).
+ *
* Leaves the source vector empty -- does not release the structure.
*
* NB: do NOT try moving a vector to itself !!
*/
-vector
+extern vector
vector_move_append(vector dst, vector src)
{
assert((src != NULL) && (src != dst)) ;
@@ -629,7 +677,7 @@ vector_move_append(vector dst, vector src)
return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
vector_copy_append(dst, src) ; /* Extend dst and copy src */
- vector_init_new(src, 0) ; /* Set empty, forgetting body */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
return dst ;
} ;
@@ -717,19 +765,19 @@ vector_move_append(vector dst, vector src)
* to ensure the vector structure is currently valid (by vector_init_new()
* or by ensuring it is zeroized).
*/
-vector
+extern vector
vector_sak(int to_copy, vector to,
- vector dst, vector_index i_dst, unsigned int n_dst,
- vector src, vector_index i_src, unsigned int n_src, int src_move)
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move)
{
int dst_replace ; /* true => replace portion of 'dst' */
- vector_index new_dst_end = 0 ; /* new end of dst */
+ vector_index_t new_dst_end = 0 ; /* new end of dst */
- unsigned int n_dst_nulls ; /* number of implicit NULLs to add */
- unsigned int n_dst_move ; /* number of items to move up or down */
- unsigned int n_src_real ; /* number of items to really copy */
- unsigned int n_src_nulls ; /* number of implicit NULLs to "copy" */
+ vector_length_t n_dst_nulls ; /* number of implicit NULLs to add */
+ vector_length_t n_dst_move ; /* number of items to move up or down */
+ vector_length_t n_src_real ; /* number of items to really copy */
+ vector_length_t n_src_nulls ; /* number of implicit NULLs to "copy" */
assert((to == NULL) || (dst == NULL) || (to != dst)) ;
assert((src == NULL) || (dst == NULL) || (src != dst)) ;
@@ -855,11 +903,15 @@ vector_sak(int to_copy, vector to,
* Legacy Vector Operations
*/
-/* Set value to the smallest empty slot. */
-int
+/*------------------------------------------------------------------------------
+ * Set value to the smallest empty slot.
+ *
+ * Returns: index of slot used.
+ */
+extern int
vector_set (vector v, void *val)
{
- vector_index i;
+ vector_index_t i;
i = 0 ;
while (1)
@@ -878,41 +930,55 @@ vector_set (vector v, void *val)
v->p_items[i] = val;
- return i;
+ return i ;
}
-/* Set value to specified index slot. */
-int
-vector_set_index (vector v, vector_index i, void *val)
+/*------------------------------------------------------------------------------
+ * Set value to specified index slot.
+ *
+ * Returns: index of slot (as given)
+ */
+extern int
+vector_set_index (vector v, vector_index_t i, void *val)
{
vector_ensure (v, i);
v->p_items[i] = val;
return i;
}
-/* Look up vector. */
-p_vector_item
-vector_lookup (vector v, vector_index i)
+/*------------------------------------------------------------------------------
+ * Look up vector -- get the i'th item.
+ *
+ * Returns: the i'th item -- NULL if item is null, or i >= logical len of vector
+ */
+extern p_vector_item
+vector_lookup (vector v, vector_index_t i)
{
if (i >= v->end)
return NULL;
return v->p_items[i];
}
-/* Lookup vector, ensure it. */
-p_vector_item
-vector_lookup_ensure (vector v, vector_index i)
+/*------------------------------------------------------------------------------
+ * Lookup vector, ensure it -- get i'th item and ensure logical len > i.
+ *
+ * Returns: the i'th item -- NULL if item is null
+ */
+extern p_vector_item
+vector_lookup_ensure (vector v, vector_index_t i)
{
vector_ensure (v, i);
return v->p_items[i];
}
-/* Count the number of not empty slots. */
-vector_index
+/*------------------------------------------------------------------------------
+ * Count the number of not empty slots.
+ */
+extern vector_length_t
vector_count (vector v)
{
- vector_index i;
- unsigned count = 0;
+ vector_index_t i;
+ vector_length_t count = 0;
for (i = 0; i < v->end; i++)
if (v->p_items[i] != NULL)
@@ -925,7 +991,8 @@ vector_count (vector v)
* Sorting and Searching vector.
*/
-/* Sort the given vector.
+/*------------------------------------------------------------------------------
+ * Sort the given vector.
*
* NB: the comparison function receives a pointer to the pointer to the
* vector item's value.
@@ -933,18 +1000,19 @@ vector_count (vector v)
* NB: if there are NULL items in the vector, the comparison function MUST
* be ready for them.
*/
-void
+extern void
vector_sort(vector v, vector_sort_cmp* cmp)
{
if (v->end <= 1)
- return ; /* Stop dead if 0 or 1 items */
+ return ; /* Stop dead if 0 or 1 items */
typedef int qsort_cmp(const void*, const void*) ;
qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
} ;
-/* Perform binary search on the given vector.
+/*------------------------------------------------------------------------------
+ * Perform binary search on the given vector.
*
* The vector MUST be sorted in the order implied by the comparison function
* given. The vector need not contain unique values, but this search makes
@@ -978,17 +1046,17 @@ vector_sort(vector v, vector_sort_cmp* cmp)
* NB: if there are NULL items in the vector, the comparison function MUST
* be ready for them.
*/
-vector_index
+extern vector_index_t
vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
- int* result)
+ int* result)
{
- vector_index il, iv, ih ;
+ vector_index_t il, iv, ih ;
int c ;
if (v->end <= 1)
{
*result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
- return 0 ; /* Stop dead if 0 or 1 items */
+ return 0 ; /* Stop dead if 0 or 1 items */
} ;
/* We have at least two items. */
@@ -998,36 +1066,36 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
/* Pick off the edge cases: >= last and <= first. */
if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
{
- *result = c ; /* 0 => found. +1 => val > last */
- return ih ; /* return high index. */
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
} ;
if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
{
- *result = c ; /* 0 => found. -1 => val < first */
- return il ; /* return low index. */
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
}
- /* Now binary chop. We know that item[il] < val < item[ih] */
- /* We also know that il < ih */
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
while (1)
{
iv = (il + ih) / 2 ;
- if (iv == il) /* true if (ih == il+1) */
- {
- *result = +1 ;
- return il ; /* return il: item[il] < val < item[il+1] */
- } ;
- /* We now know that il < iv < ih */
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
if (c == 0)
- {
- *result = 0 ;
- return iv ; /* found !! */
- }
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
if (c < 0)
- ih = iv ; /* step down iv > il, so new ih > il */
+ ih = iv ; /* step down iv > il, so new ih > il */
else
- il = iv ; /* step up iv < ih, so new il < ih */
+ il = iv ; /* step up iv < ih, so new il < ih */
} ;
} ;
@@ -1041,7 +1109,8 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
/* Round 'x' up to a multiple of 'm' */
#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
-/* Set new limit to suit new end for given vector.
+/*------------------------------------------------------------------------------
+ * Set new limit to suit new end for given vector.
*
* The new limit will be at least: VECTOR_LIMIT_MIN.
*
@@ -1070,10 +1139,10 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
* of P_ITEMS_SIZE will ?
*/
static void
-vector_new_limit(vector v, vector_index new_end)
+vector_new_limit(vector v, vector_index_t new_end)
{
- vector_index old_limit = v->limit ;
- vector_index new_limit ;
+ vector_length_t old_limit = v->limit ;
+ vector_length_t new_limit ;
if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
{
@@ -1102,17 +1171,18 @@ vector_new_limit(vector v, vector_index new_end)
v->limit = new_limit ;
} ;
-/* Extend vector and set new (logical) end.
+/*------------------------------------------------------------------------------
+ * Extend vector and set new (logical) end.
*
* Extends body if required.
* Ensures everything between old and new end is set NULL.
*
* NB: expects new end > old end, but copes with new end <= old end.
*/
-void
-vector_extend(vector v, vector_index new_end)
+extern void
+vector_extend(vector v, vector_length_t new_end)
{
- vector_index old_end = v->end ;
+ vector_length_t old_end = v->end ;
if (new_end > v->limit)
vector_new_limit(v, new_end) ;
@@ -1122,16 +1192,17 @@ vector_extend(vector v, vector_index new_end)
memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
} ;
-/* Insert entries into vector: insert 'n' NULL entries at location 'i'.
+/*------------------------------------------------------------------------------
+ * Insert entries into vector: insert 'n' NULL entries at location 'i'.
*
* Updates end (and limit) to be at least 'i' + 'n'.
* (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
*/
-void
-vector_insert(vector v, vector_index i, unsigned int n)
+extern void
+vector_insert(vector v, vector_index_t i, vector_length_t n)
{
- vector_index old_end, new_end ;
- unsigned int n_above ;
+ vector_length_t old_end, new_end ;
+ vector_length_t n_above ;
/* If i < old end, then we are inserting n NULLs, and need
* to shuffle at least one item up.
@@ -1142,19 +1213,19 @@ vector_insert(vector v, vector_index i, unsigned int n)
if (i < old_end)
{
if (n == 0)
- return ; /* give up now if not inserting anything */
- n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
new_end = old_end + n ;
}
else
{
- n_above = 0 ; /* nothing to shuffle up. */
+ n_above = 0 ; /* nothing to shuffle up. */
new_end = i + n ;
i = old_end ; /* where to zeroize from */
n = new_end - old_end ; /* how much to zeroize */
} ;
- /* Now we extend the body if we need to. */
+ /* Now we extend the body if we need to. */
if (new_end > v->limit)
vector_new_limit(v, new_end) ;
v->end = new_end ;
@@ -1165,7 +1236,8 @@ vector_insert(vector v, vector_index i, unsigned int n)
memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
} ;
-/* Delete items from vector: delete 'n' items at location 'i'.
+/*------------------------------------------------------------------------------
+ * Delete items from vector: delete 'n' items at location 'i'.
*
* Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
*
@@ -1176,10 +1248,10 @@ vector_insert(vector v, vector_index i, unsigned int n)
* NB: it is the caller's responsibility to have released any memory allocated
* for the items that are being deleted.
*/
-void
-vector_delete(vector v, vector_index i, unsigned int n)
+extern void
+vector_delete(vector v, vector_index_t i, vector_length_t n)
{
- vector_index old_end, new_end ;
+ vector_length_t old_end, new_end ;
old_end = v->end ;
@@ -1190,7 +1262,7 @@ vector_delete(vector v, vector_index i, unsigned int n)
if ((i + n) < old_end)
{
memmove(&v->p_items[i], &v->p_items[i + n],
- P_ITEMS_SIZE(old_end - (i + n))) ;
+ P_ITEMS_SIZE(old_end - (i + n))) ;
new_end = old_end - n ;
}
else
@@ -1202,7 +1274,8 @@ vector_delete(vector v, vector_index i, unsigned int n)
v->end = new_end ; /* account for stuff dropped */
} ;
-/* Discard entries from vector: discard entries from location 'i' onwards.
+/*------------------------------------------------------------------------------
+ * Discard entries from vector: discard entries from location 'i' onwards.
*
* Releases memory from 'i' onwards.
* Releases the body altogether if this sets the vector empty ('i' == 0).
@@ -1213,8 +1286,8 @@ vector_delete(vector v, vector_index i, unsigned int n)
* NB: it is the caller's responsibility to have released any memory allocated
* for the items that are being discarded.
*/
-void
-vector_discard(vector v, vector_index i)
+extern void
+vector_discard(vector v, vector_index_t i)
{
if (i >= v->limit)
return ;
@@ -1230,34 +1303,37 @@ vector_discard(vector v, vector_index i)
} ;
} ;
-/* Chop vector at the current (logical) end.
+/*------------------------------------------------------------------------------
+ * Chop vector at the current (logical) end.
*
* Releases the body altogether if the vector is currently empty.
-*/
-void
+ */
+extern void
vector_chop(vector v)
{
- vector_index new_limit = v->end ;
+ vector_length_t new_limit = v->end ;
if (new_limit == 0)
vector_reset(v, 0) ; /* reset, without releasing the structure */
else if (new_limit != v->limit)
{
v->limit = new_limit ;
- v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(new_limit)) ;
} ;
} ;
-/* Decant vector into a new body allocated to current logical size, and
+/*------------------------------------------------------------------------------
+ * Decant vector into a new body allocated to current logical size, and
* release the old body.
*
* Releases the body altogether if the vector is currently empty.
*/
-void
+extern void
vector_decant(vector v)
{
p_vector_item* p_old_body ;
- vector_index new_limit = v->end ;
+ vector_length_t new_limit = v->end ;
if (new_limit == 0)
vector_reset(v, 0) ; /* reset, without releasing the structure */
diff --git a/lib/vector.h b/lib/vector.h
index f098d78a..58b9e415 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -25,30 +25,31 @@
#ifndef _ZEBRA_VECTOR_H
#define _ZEBRA_VECTOR_H
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
+#include "misc.h"
/*------------------------------------------------------------------------------
* types and struct for vector
*
* NB: an entirely zero structure represents an entirely empty vector.
- *
- * TODO: could force vector_index to be 32 bits ?
*/
typedef void* p_vector_item ;
-typedef unsigned int vector_index ;
+typedef unsigned int vector_index_t ;
+typedef unsigned int vector_length_t ;
+
+enum {
+ VECTOR_LENGTH_MAX = (vector_length_t)INT_MAX + 1
+} ;
-typedef struct vector* vector ; /* pointer to vector structure */
-typedef struct vector vector_t ; /* embedded vector structure */
struct vector
{
- p_vector_item* p_items ; /* pointer to array of vector item pointers */
- vector_index end ; /* number of "active" item entries */
- vector_index limit ; /* number of allocated item entries */
+ p_vector_item* p_items ; /* pointer to array of vector item pointers */
+ vector_length_t end ; /* number of "active" item entries */
+ vector_length_t limit ; /* number of allocated item entries */
};
+typedef struct vector vector_t[1] ; /* embedded vector structure */
+typedef struct vector* vector ; /* pointer to vector structure */
+
/* Under very controlled circumstances, may access the vector body */
typedef p_vector_item const* vector_body_t ;
@@ -83,7 +84,7 @@ typedef p_vector_item const* vector_body_t ;
/* To walk all items in a vector:
*
- * vector_index i ;
+ * vector_index_t i ;
* xxxxx* p_v ;
*
* for (VECTOR_ITEMS(v, p_v, i))
@@ -104,71 +105,65 @@ typedef p_vector_item const* vector_body_t ;
* Prototypes.
*/
-extern vector vector_init (unsigned int size);
-Inline void vector_ensure(vector v, vector_index i) ;
+extern vector vector_init (vector_length_t size);
+Inline void vector_ensure(vector v, vector_index_t i) ;
extern int vector_set (vector v, void *val);
-extern int vector_set_index (vector v, vector_index i, void *val);
+extern int vector_set_index (vector v, vector_index_t i, void *val);
#define vector_unset(v, i) (void)vector_unset_item(v, i)
-extern vector_index vector_count (vector v);
+extern vector_length_t vector_count (vector v);
extern void vector_free (vector v);
extern vector vector_copy (vector v);
-extern void *vector_lookup (vector, vector_index);
-extern void *vector_lookup_ensure (vector, vector_index);
+extern void *vector_lookup (vector, vector_index_t);
+extern void *vector_lookup_ensure (vector, vector_index_t);
-extern vector vector_init_new(vector v, unsigned int size) ;
-extern vector vector_re_init(vector v, unsigned int size) ;
-extern vector vector_reset(vector v, int free_structure) ;
-extern p_vector_item vector_ream(vector v, int free_structure) ;
+extern vector vector_init_new(vector v, vector_length_t size) ;
+extern vector vector_re_init(vector v, vector_length_t size) ;
+extern vector vector_reset(vector v, free_keep_b free_structure) ;
+extern p_vector_item vector_ream(vector v, free_keep_b free_structure) ;
-/* Reset vector and free the vector structure. */
-#define vector_reset_free(v) vector_reset(v, 1)
-/* Reset vector but free the heap structure. */
-#define vector_reset_keep(v) vector_reset(v, 0)
-/* Ream out vector and free the vector structure. */
-#define vector_ream_free(v) vector_ream(v, 1)
-/* Ream out vector but keep the vector structure. */
-#define vector_ream_keep(v) vector_ream(v, 0)
+Inline void vector_set_min_length(vector v, vector_length_t len) ;
+Private void vector_set_new_min_length(vector v, vector_length_t len) ;
-Inline void vector_set_min_length(vector v, unsigned int len) ;
-extern void vector_set_new_min_length(vector v, unsigned int len) ;
-
-Inline void vector_set_length(vector v, unsigned int len) ;
+Inline void vector_set_length(vector v, vector_length_t len) ;
#define vector_set_end(v, l) vector_set_length(v, l)
-Inline vector_index vector_length(vector v) ;
-#define vector_end(v) vector_length(v)
-Inline int vector_is_empty(vector v) ;
+Inline vector_length_t vector_length(vector v) ;
+#define vector_end(v) vector_length(v)
+Inline bool vector_is_empty(vector v) ;
Inline vector_body_t vector_body(vector v) ;
-Inline p_vector_item vector_get_item(vector v, vector_index i) ;
+Inline p_vector_item vector_get_item(vector v, vector_index_t i) ;
Inline p_vector_item vector_get_first_item(vector v) ;
Inline p_vector_item vector_get_last_item(vector v) ;
-Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
-Inline void vector_assign_item(vector v, vector_index dst, vector_index src) ;
-extern p_vector_item vector_unset_item(vector v, vector_index i) ;
-extern vector_index vector_trim(vector v) ;
-extern vector_index vector_condense(vector v) ;
-
-extern void vector_insert_item(vector v, vector_index i, p_vector_item p_v) ;
-extern void vector_insert_item_here(vector v, vector_index i, int rider,
+Inline void vector_set_item(vector v, vector_index_t i, p_vector_item p_v) ;
+Inline void vector_assign_item(vector v, vector_index_t dst,
+ vector_index_t src) ;
+extern p_vector_item vector_unset_item(vector v, vector_index_t i) ;
+extern vector_length_t vector_trim(vector v) ;
+extern vector_length_t vector_condense(vector v) ;
+
+extern void vector_insert_item(vector v, vector_index_t i, p_vector_item p_v) ;
+extern void vector_insert_item_here(vector v, vector_index_t i, int rider,
p_vector_item p_v) ;
-extern void vector_move_item(vector v, vector_index dst, vector_index src) ;
-extern void vector_move_item_here(vector v, vector_index dst, int rider,
- vector_index src) ;
-extern p_vector_item vector_delete_item(vector v, vector_index i) ;
+extern void vector_move_item(vector v, vector_index_t dst, vector_index_t src) ;
+extern void vector_move_item_here(vector v, vector_index_t dst, int rider,
+ vector_index_t src) ;
+extern p_vector_item vector_delete_item(vector v, vector_index_t i) ;
extern void vector_reverse(vector v) ;
-extern void vector_part_reverse(vector v, vector_index i, unsigned int n) ;
+extern void vector_part_reverse(vector v, vector_index_t i, vector_length_t n) ;
Inline void vector_push_item(vector v, p_vector_item p_v) ;
Inline p_vector_item vector_pop_item(vector v) ;
+Inline void vector_unshift_item(vector v, p_vector_item p_v) ;
+Inline p_vector_item vector_shift_item(vector v) ;
-extern void vector_insert(vector v, vector_index i, unsigned int n) ;
-extern void vector_delete(vector v, vector_index i, unsigned int n) ;
+extern void vector_insert(vector v, vector_index_t i, vector_length_t n) ;
+extern void vector_delete(vector v, vector_index_t i, vector_length_t n) ;
typedef int vector_bsearch_cmp(const void** pp_val, const void** item) ;
-vector_index vector_bsearch(vector v, vector_bsearch_cmp* cmp,
+vector_index_t vector_bsearch(vector v, vector_bsearch_cmp* cmp,
const void* p_val, int* result) ;
typedef int vector_sort_cmp(const void** a, const void** b) ;
void vector_sort(vector v, vector_sort_cmp* cmp) ;
@@ -192,15 +187,15 @@ extern vector vector_move_append(vector dst, vector src) ;
vector_sak(0, NULL, dst, i_dst, n_dst, src, i_src, n_src, 1)
extern vector vector_sak(int to_copy, vector to,
- vector dst, vector_index i_dst, unsigned int n_dst,
- vector src, vector_index i_src, unsigned int n_src, int src_move) ;
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move) ;
-extern void vector_discard(vector v, vector_index i) ;
+extern void vector_discard(vector v, vector_index_t i) ;
extern void vector_chop(vector v) ;
extern void vector_decant(vector v) ;
-Inline vector_index vector_extend_by_1(vector v) ;
-extern void vector_extend(vector v, vector_index new_end) ;
+Inline vector_index_t vector_extend_by_1(vector v) ;
+extern void vector_extend(vector v, vector_length_t new_end) ;
/*==============================================================================
* The inline functions:
@@ -209,10 +204,10 @@ extern void vector_extend(vector v, vector_index new_end) ;
/* Extend vector by one item at the end, which is about to be set. */
/* Returns index of new least item in the vector. */
/* NB: if left unset, the item may be UNDEFINED. */
-Inline vector_index
+Inline vector_index_t
vector_extend_by_1(vector v)
{
- vector_index i = v->end ;
+ vector_index_t i = v->end ;
if (i < v->limit)
return v->end++ ; /* simple if we have room */
@@ -225,7 +220,7 @@ vector_extend_by_1(vector v)
/* Adjusts logical and physical end of the vector as required, filling */
/* with NULLs upto any new logical end. */
Inline void
-vector_ensure(vector v, vector_index i)
+vector_ensure(vector v, vector_index_t i)
{
if (i < v->end) /* trivial if within vector */
return ;
@@ -241,7 +236,7 @@ vector_ensure(vector v, vector_index i)
/* with NULLs upto any new logical end -- does not allocate any more */
/* than is exactly necessary. */
Inline void
-vector_set_min_length(vector v, unsigned int len)
+vector_set_min_length(vector v, vector_length_t len)
{
if (len > v->end) /* will not reduce the length */
vector_set_new_min_length(v, len) ;
@@ -256,7 +251,7 @@ vector_set_min_length(vector v, unsigned int len)
/* with NULLs upto any new logical end -- does not allocate any more */
/* than is exactly necessary. */
Inline void
-vector_set_length(vector v, unsigned int len)
+vector_set_length(vector v, vector_length_t len)
{
if (len > v->end)
vector_set_new_min_length(v, len) ; /* Extend if new length greater */
@@ -265,14 +260,14 @@ vector_set_length(vector v, unsigned int len)
} ;
/* Return index of end of vector (index of last item + 1) */
-Inline vector_index
+Inline vector_length_t
vector_length(vector v)
{
return v->end ;
} ;
/* Returns whether vector is empty or not. */
-Inline int
+Inline bool
vector_is_empty(vector v)
{
return (v->end == 0) ;
@@ -295,7 +290,7 @@ vector_body(vector v)
/* Get pointer to item. Returns NULL if accessing beyond end. */
Inline p_vector_item
-vector_get_item(vector v, vector_index i)
+vector_get_item(vector v, vector_index_t i)
{
return (i < v->end) ? v->p_items[i] : NULL ;
} ;
@@ -318,7 +313,7 @@ vector_get_last_item(vector v)
/* NB: it is the caller's responsibility to release memory used by any */
/* current value of the item, if required. */
Inline void
-vector_set_item(vector v, vector_index i, void* p_v)
+vector_set_item(vector v, vector_index_t i, p_vector_item p_v)
{
vector_ensure(v, i) ;
v->p_items[i] = (p_vector_item)p_v ;
@@ -330,16 +325,16 @@ vector_set_item(vector v, vector_index i, void* p_v)
* used by the current dst item or the new (duplicated) src item.
*/
Inline void
-vector_assign_item(vector v, vector_index dst, vector_index src)
+vector_assign_item(vector v, vector_index_t dst, vector_index_t src)
{
vector_set_item(v, dst, vector_get_item(v, src)) ;
} ;
/* Push value onto vector, extending as required. */
Inline void
-vector_push_item(vector v, void* p_v)
+vector_push_item(vector v, p_vector_item p_v)
{
- vector_index i = vector_extend_by_1(v) ;
+ vector_index_t i = vector_extend_by_1(v) ;
v->p_items[i] = (p_vector_item)p_v ;
} ;
@@ -351,4 +346,22 @@ vector_pop_item(vector v)
return (v->end > 0) ? v->p_items[--v->end] : NULL ;
} ;
+/* Unshift value onto start of vector, extending as required. */
+Inline void
+vector_unshift_item(vector v, p_vector_item p_v)
+{
+ vector_insert(v, 0, 1) ;
+ v->p_items[0] = p_v ;
+} ;
+
+/* Pop value from vector. Returns NULL if vector is empty. */
+/* NB: does NOT change the size of the vector body. */
+Inline p_vector_item
+vector_shift_item(vector v)
+{
+ p_vector_item p_v = vector_get_first_item(v) ;
+ vector_delete(v, 0, 1) ;
+ return p_v ;
+} ;
+
#endif /* _ZEBRA_VECTOR_H */
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
index 685f33ec..10c4a343 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -19,7 +19,7 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stddef.h>
+#include "misc.h"
#include <string.h>
#include "vio_fifo.h"
@@ -215,15 +215,18 @@ vio_fifo_ptr_unset(vio_fifo vf)
* Clear out contents of FIFO -- will continue to use the FIFO.
*
* Keeps one FIFO lump. (Frees everything else, including any spare.)
+ *
+ * Does nothing if there is no FIFO !
*/
extern void
vio_fifo_clear(vio_fifo vf)
{
vio_fifo_lump tail ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vf == NULL)
+ return ;
- assert(vf != NULL) ;
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
tail = ddl_tail(vf->base) ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
index 52f3455e..08af4590 100644
--- a/lib/vio_fifo.h
+++ b/lib/vio_fifo.h
@@ -23,20 +23,11 @@
#define _ZEBRA_VIO_FIFO_H
#include "zebra.h"
-#include <stdint.h>
-#include <stdbool.h>
+#include "misc.h"
#include "list_util.h"
#include "zassert.h"
-#ifndef Inline /* in case of compiler issues */
-#define Inline static inline
-#endif
-
-#ifndef Private /* extern, but for "friends" only */
-#define Private extern
-#endif
-
/* GCC have printf type attribute check. */
#ifdef __GNUC__
#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
index c2e9c43c..2ac874d1 100644
--- a/lib/vio_lines.c
+++ b/lib/vio_lines.c
@@ -19,8 +19,7 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdint.h>
-
+#include "misc.h"
#include "memory.h"
#include "zassert.h"
@@ -148,6 +147,9 @@ vio_lc_reset(vio_line_control lc, bool free_structure)
extern void
vio_lc_clear(vio_line_control lc)
{
+ if (lc == NULL)
+ return ;
+
qiovec_clear(&lc->qiov) ;
lc->pause = 0 ;
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
index ffef94ec..7097fe9c 100644
--- a/lib/vio_lines.h
+++ b/lib/vio_lines.h
@@ -23,16 +23,9 @@
#define _ZEBRA_VIO_LINES_H
#include "zebra.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
+#include "misc.h"
#include "qiovec.h"
-#ifndef Inline
-#define Inline static inline
-#endif
-
/*==============================================================================
*
*/
diff --git a/lib/vty.c b/lib/vty.c
index 9b1e5d87..3c1e62b7 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -22,7 +22,7 @@
*/
#include "zebra.h"
-#include <stdbool.h>
+#include "misc.h"
#include "lib/version.h"
#include "vty_io.h"
@@ -94,9 +94,6 @@ bool no_password_check = 0;
const bool restricted_mode_default = 0 ;
bool restricted_mode = 0 ;
-/* Watch-dog timer. */
-union vty_watch_dog vty_watch_dog = { NULL } ;
-
/*------------------------------------------------------------------------------
* VTYSH stuff
*/
@@ -110,6 +107,9 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
static void uty_reset (bool final, const char* why) ;
static void uty_init_commands (void) ;
static void vty_save_cwd (void) ;
+static bool vty_terminal (struct vty *);
+static bool vty_shell_server (struct vty *);
+static bool vty_shell_client (struct vty *);
/*------------------------------------------------------------------------------
* Tracking the initialisation state.
@@ -159,7 +159,7 @@ vty_init (struct thread_master *master_thread)
vty_cli_nexus = NULL ; /* not running qnexus-wise */
vty_cmd_nexus = NULL ;
- vty_watch_dog.anon = NULL ; /* no watch dog */
+ uty_watch_dog_init() ; /* empty watch dog */
uty_init_commands() ; /* install nodes */
@@ -440,16 +440,13 @@ uty_reset (bool curtains, const char* why)
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (vio->type == VTY_TERM)
+ if (uty_is_terminal(vio->vty))
cq_revoke(vio->vty) ;
- if (why != NULL)
- vio->close_reason = why ;
-
if (curtains)
- uty_close(vio) ;
+ uty_close_final(vio, why) ;
else
- uty_half_close(vio, why) ;
+ uty_close(vio, why) ;
} ;
vty_timeout_val = VTY_TIMEOUT_DEFAULT;
@@ -512,31 +509,79 @@ vty_close (struct vty *vty)
/*==============================================================================
* General VTY output.
*
- * This is mostly used during command execution, to output the results of the
- * command.
+ * This is used during command execution, to output the results of commands.
*
* All these end up in uty_vout -- see vty_io.
*/
/*------------------------------------------------------------------------------
* VTY output -- cf fprintf !
+ *
+ * This is for command output, which may be suppressed
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
*/
extern int
vty_out (struct vty *vty, const char *format, ...)
{
- int result;
+ int ret ;
+ va_list args ;
VTY_LOCK() ;
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
+
+ if (vty->output_enabled)
+ {
+ va_start (args, format) ;
+ ret = uty_vprintf(vty, format, args) ;
+ va_end (args) ;
+ }
+ else
+ ret = 0 ;
+
VTY_UNLOCK() ;
- return result;
+ return ret ;
+}
+
+/*------------------------------------------------------------------------------
+ * VTY output error message -- cf fprintf !
+ *
+ * If command has not yet been reflected, do that first.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+vty_out_error (struct vty *vty, const char *format, ...)
+{
+ int ret ;
+ va_list args ;
+
+ VTY_LOCK() ;
+
+ if (!vty->reflected)
+ ret = uty_reflect(vty) ;
+ else
+ ret = 0 ;
+
+ if (ret == 0)
+ {
+ va_start (args, format) ;
+ ret = uty_vprintf(vty, format, args) ;
+ va_end (args) ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return ret ;
}
/*------------------------------------------------------------------------------
* VTY output -- output a given numnber of spaces
+ *
+ * This is for command output, which may be suppressed
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
*/
/* 1 2 3 4 */
@@ -544,14 +589,19 @@ vty_out (struct vty *vty, const char *format, ...)
const char vty_spaces_string[] = " " ;
CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-extern void
+extern int
vty_out_indent(struct vty *vty, int indent)
{
- while (indent > 0)
+ int ret ;
+
+ ret = 0 ;
+ while ((indent > 0) && (ret >= 0))
{
- vty_out(vty, VTY_SPACES(indent)) ;
+ ret = vty_out(vty, VTY_SPACES(indent)) ;
indent -= VTY_MAX_SPACES ;
}
+
+ return ret ;
} ;
/*------------------------------------------------------------------------------
@@ -565,9 +615,9 @@ vty_time_print (struct vty *vty, int cr)
quagga_timestamp(0, buf, sizeof(buf)) ;
if (cr)
- vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ vty_out(vty, "%s\n", buf);
else
- vty_out (vty, "%s ", buf);
+ vty_out(vty, "%s ", buf);
return;
}
@@ -581,7 +631,7 @@ vty_hello (struct vty *vty)
VTY_LOCK() ;
#ifdef QDEBUG
- uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE);
+ uty_out (vty, "%s\n", debug_banner);
#endif
if (host.motdfile)
{
@@ -598,15 +648,15 @@ vty_hello (struct vty *vty)
for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
s--);
*s = '\0';
- uty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ uty_output (vty, "%s\n", buf);
}
fclose (f);
}
else
- uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE);
+ uty_output (vty, "MOTD file %s not found\n", host.motdfile);
}
else if (host.motd)
- uty_out (vty, "%s", host.motd);
+ uty_output (vty, "%s", host.motd);
VTY_UNLOCK() ;
}
@@ -643,7 +693,7 @@ uty_command(struct vty *vty)
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- assert(vty->vio->type == VTY_TERM) ;
+ assert(uty_is_terminal(vty)) ;
/* Parse the command and add to history (if not empty) */
ret = cmd_parse_command(vty,
@@ -679,15 +729,15 @@ uty_command(struct vty *vty)
switch (ret)
{
case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ vty_out_error(vty, "%% Ambiguous command.\n");
break;
case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ;
+ vty_out_error(vty, "%% Unknown command.\n") ;
break;
case CMD_ERR_INCOMPLETE:
- uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ vty_out_error(vty, "%% Command incomplete.\n");
break;
default:
@@ -795,14 +845,14 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
if (vty->node == AUTH_NODE)
{
- ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ;
+ ret = uty_cmd_close(vty, "Bad passwords, too many failures!") ;
}
else
{
/* AUTH_ENABLE_NODE */
vio->fail = 0;
- uty_out (vty, "%% Bad enable passwords, too many failures!%s",
- VTY_NEWLINE);
+ vty_out_error(vty,
+ "%% Bad enable passwords, too many failures!\n") ;
vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
ret = CMD_WARNING ;
@@ -833,7 +883,7 @@ vty_cmd_exit(struct vty* vty)
case VIEW_NODE:
case ENABLE_NODE:
case RESTRICTED_NODE:
- if (vty_shell (vty))
+ if (uty_shell_client(vty))
exit (0);
else
ret = uty_cmd_close(vty, "Exit") ;
@@ -1349,6 +1399,33 @@ vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
VTY_UNLOCK() ;
} ;
+/*------------------------------------------------------------------------------
+ * Flush the contents of the command output FIFO to the given file.
+ *
+ * Takes no notice of any errors !
+ */
+extern void
+uty_out_fflush(vty_io vio, FILE* file)
+{
+ char* src ;
+ size_t have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ fflush(file) ;
+
+ while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ {
+ fwrite(src, 1, have, file) ;
+ vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ } ;
+
+ fflush(file) ;
+} ;
+
+
+
+
/*==============================================================================
* Configuration node/state handling
*
@@ -1369,13 +1446,13 @@ vty_config_lock (struct vty *vty, enum node_type node)
VTY_LOCK() ;
- if (vty_config == 0)
+ if (!vty_config)
{
- vty->vio->config = 1 ;
- vty_config = 1 ;
+ vty->config = true ;
+ vty_config = true ;
} ;
- result = vty->vio->config;
+ result = vty->config;
if (result)
vty->node = node ;
@@ -1407,10 +1484,10 @@ extern void
uty_config_unlock (struct vty *vty, enum node_type node)
{
VTY_ASSERT_LOCKED() ;
- if ((vty_config == 1) && (vty->vio->config == 1))
+ if (vty_config && vty->config)
{
- vty->vio->config = 0;
- vty_config = 0;
+ vty->config = false ;
+ vty_config = false ;
}
assert(node <= MAX_NON_CONFIG_NODE) ;
@@ -1434,9 +1511,8 @@ DEFUN_CALL (config_who,
vio = vio_list_base ;
while (vio != NULL) /* TODO: show only VTY_TERM ??? */
{
- uty_out (vty, "%svty[%d] connected from %s.%s",
- vio->config ? "*" : " ",
- i, uty_get_name(vio), VTY_NEWLINE);
+ uty_output (vty, "%svty[%d] connected from %s.\n",
+ vio->vty->config ? "*" : " ", i, uty_get_name(vio));
vio = sdl_next(vio, vio_list) ;
} ;
VTY_UNLOCK() ;
@@ -1476,7 +1552,7 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
vty_timeout_val = timeout;
- if (vty_term(vty) || vty_shell_serv(vty))
+ if (uty_is_terminal(vty) || uty_is_shell_server(vty))
uty_sock_set_timer(&vty->vio->sock, timeout) ;
VTY_UNLOCK() ;
@@ -1542,8 +1618,7 @@ DEFUN_CALL (no_vty_access_class,
VTY_LOCK() ;
if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
{
- uty_out (vty, "Access-class is not currently applied to vty%s",
- VTY_NEWLINE);
+ vty_out_error(vty, "Access-class is not currently applied to vty\n");
result = CMD_WARNING;
}
else
@@ -1591,8 +1666,7 @@ DEFUN_CALL (no_vty_ipv6_access_class,
if (! vty_ipv6_accesslist_name ||
(argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
{
- uty_out (vty, "IPv6 access-class is not currently applied to vty%s",
- VTY_NEWLINE);
+ vty_out_error(vty, "IPv6 access-class is not currently applied to vty\n") ;
result = CMD_WARNING;
}
else
@@ -1732,9 +1806,9 @@ DEFUN_CALL (show_history,
continue;
}
- line = vector_get_item(&vty->vio->hist, index) ;
+ line = vector_get_item(vty->vio->hist, index) ;
if (line != NULL)
- uty_out (vty, " %s%s", line->char_body, VTY_NEWLINE);
+ uty_output (vty, " %s\n", line->char_body);
index++;
}
@@ -1751,35 +1825,32 @@ DEFUN_CALL (show_history,
static int
vty_config_write (struct vty *vty)
{
- vty_out (vty, "line vty%s", VTY_NEWLINE);
+ vty_out (vty, "line vty\n");
if (vty_accesslist_name)
- vty_out (vty, " access-class %s%s",
- vty_accesslist_name, VTY_NEWLINE);
+ vty_out (vty, " access-class %s\n", vty_accesslist_name);
if (vty_ipv6_accesslist_name)
- vty_out (vty, " ipv6 access-class %s%s",
- vty_ipv6_accesslist_name, VTY_NEWLINE);
+ vty_out (vty, " ipv6 access-class %s\n", vty_ipv6_accesslist_name);
/* exec-timeout */
if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld%s",
- vty_timeout_val / 60,
- vty_timeout_val % 60, VTY_NEWLINE);
+ vty_out (vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
+ vty_timeout_val % 60);
/* login */
if (no_password_check)
- vty_out (vty, " no login%s", VTY_NEWLINE);
+ vty_out (vty, " no login\n");
if (restricted_mode != restricted_mode_default)
{
if (restricted_mode_default)
- vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
+ vty_out (vty, " no anonymous restricted\n");
else
- vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
+ vty_out (vty, " anonymous restricted\n");
}
- vty_out (vty, "!%s", VTY_NEWLINE);
+ vty_out (vty, "!\n");
return CMD_SUCCESS;
}
@@ -1824,32 +1895,32 @@ vty_get_cwd ()
* Access functions for VTY values, where locking is or might be required.
*/
-bool
-vty_shell (struct vty *vty)
+static bool
+vty_is_terminal(struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_SHELL) ;
+ result = uty_is_terminal(vty) ;
VTY_UNLOCK() ;
return result;
}
-bool
-vty_term(struct vty *vty)
+static bool
+vty_is_shell_server (struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_TERM);
+ result = uty_is_shell_server(vty) ;
VTY_UNLOCK() ;
return result;
}
-bool
-vty_shell_serv (struct vty *vty)
+static bool
+vty_is_shell_client (struct vty *vty)
{
bool result;
VTY_LOCK() ;
- result = (vty->vio->type == VTY_SHELL_SERV);
+ result = uty_is_shell_client(vty) ;
VTY_UNLOCK() ;
return result;
}
diff --git a/lib/vty.h b/lib/vty.h
index 126218ab..b8621588 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -24,7 +24,7 @@
#ifndef _ZEBRA_VTY_H
#define _ZEBRA_VTY_H
-#include <stdbool.h>
+#include "misc.h"
#include "thread.h"
#include "log.h"
@@ -37,11 +37,6 @@
#include "qstring.h"
#include "node_type.h"
-/* Macro in case there are particular compiler issues. */
-#ifndef Inline
- #define Inline static inline
-#endif
-
/*==============================================================================
* The VTYSH uses a unix socket to talk to the daemon.
*
@@ -50,50 +45,59 @@
* option is turned into a testable constant.
*/
#ifdef VTYSH
-# define VTYSH_DEFINED 1
+ enum { VTYSH_ENABLED = true } ;
#else
-# define VTYSH_DEFINED 0
+ enum { VTYSH_ENABLED = false } ;
#endif
-enum { VTYSH_ENABLED = VTYSH_DEFINED } ;
-
-#undef VTYSH_DEFINED
-
/*==============================================================================
* VTY Types
*/
-enum vty_type
+enum vty_type /* Command output */
{
- VTY_NONE = 0, /* no type at all */
+ VTY_TERMINAL, /* a telnet terminal server */
+ VTY_SHELL_SERVER, /* a vty_shell server */
- VTY_TERM, /* a telnet terminal -- input and output */
- VTY_SHELL_SERV, /* a vty_shell slave -- input and output */
+ VTY_SHELL_CLIENT, /* a vty_shell client */
- VTY_CONFIG_READ, /* reading config file -- output is to buffer
- -- no input */
+ VTY_CONFIG_READ, /* configuration file reader */
- VTY_CONFIG_WRITE, /* writing config file -- output is to file
- -- no input */
+ VTY_STDOUT, /* stdout */
+ VTY_STDERR, /* stderr */
+} ;
+typedef enum vty_type vty_type_t ;
- VTY_STDOUT, /* general output -- output is to stdout
- -- no input */
+/*==============================================================================
+ *
+ *
+ *
+ *
+ */
- VTY_STDERR, /* general output -- output is to stderr
- -- no input */
+typedef unsigned long vty_timer_time ; /* Time out time in seconds */
- VTY_SHELL, /* vty in vtysh -- output is to stdout */
+enum
+{
+ VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
+
+ VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
+
+ VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
} ;
/*==============================================================================
* VTY struct.
*/
-typedef struct vty_io* vty_io ; /* private to vty.c */
+typedef struct vty_io* vty_io ; /* private to vty.c */
struct cmd_parsed ; /* in case vty.h expanded before command.h */
+typedef struct vty* vty ;
struct vty
{
+ vty_type_t type ;
+
/*----------------------------------------------------------------------
* The following are the context in which commands are executed.
*/
@@ -120,8 +124,8 @@ struct vty
*/
void *index_sub ;
- /* String which is newline... read only -- no locking */
- const char* newline ;
+ /* In configure mode. */
+ bool config;
/*----------------------------------------------------------------------------
* The current command line.
@@ -135,13 +139,17 @@ struct vty
struct cmd_parsed* parsed ;
unsigned lineno ;
+ bool output_enabled ;
+ bool reflect_enabled ;
+ bool more_enabled ;
+
+ bool reflected ;
+
/*----------------------------------------------------------------------
* The following are used inside vty.c only.
*/
-
- /* Pointer to related vty_io structure */
- vty_io vio ;
-};
+ vty_io vio ; /* one vio object per vty */
+} ;
/*------------------------------------------------------------------------------
* Can now include this
@@ -158,18 +166,15 @@ struct vty
/* Integrated configuration file. */
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
-/* Small macro to determine newline is newline only or linefeed needed. */
-#define VTY_NEWLINE (((vty) != NULL) ? (vty)->newline : "\n")
+/* Conversion of "\n" to "\r\n" is done at output time. */
+#define VTY_NEWLINE "\n"
+#define VTY_NL "\n"
/* For indenting, mostly. */
extern const char vty_spaces_string[] ;
enum { VTY_MAX_SPACES = 40 } ;
#define VTY_SPACES(n) (vty_spaces_string + ((n) < VTY_MAX_SPACES \
? VTY_MAX_SPACES - (n) : 0))
-
-/* Default time out value */
-#define VTY_TIMEOUT_DEFAULT 600
-
/* Vty read buffer size. */
#define VTY_READ_BUFSIZ 512
@@ -217,7 +222,7 @@ do { \
VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX)
#define VTY_GET_IPV4_ADDRESS(NAME,V,STR) \
-do { \
+do { \
int retv; \
retv = inet_aton ((STR), &(V)); \
if (!retv) \
@@ -228,7 +233,7 @@ do {
} while (0)
#define VTY_GET_IPV4_PREFIX(NAME,V,STR) \
-do { \
+do { \
int retv; \
retv = str2prefix_ipv4 ((STR), &(V)); \
if (retv <= 0) \
@@ -253,7 +258,7 @@ extern void vty_start(const char *addr, unsigned short port, const char *path) ;
extern void vty_restart(const char *addr, unsigned short port,
const char *path) ;
extern struct vty* vty_open(enum vty_type type) ;
-extern void vty_close(struct vty *);
+extern void vty_close_final(struct vty *);
extern void vty_init_vtysh (void);
extern void vty_terminate (void);
@@ -261,7 +266,9 @@ extern void vty_reset (void);
extern void vty_reset_because(const char* why) ;
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
-extern void vty_out_indent(struct vty *vty, int indent) ;
+extern int vty_out_indent(struct vty *vty, int indent) ;
+extern int vty_out_error (struct vty *vty, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3);
extern void vty_out_clear(struct vty *vty) ;
extern void vty_read_config (char *config_file, char *config_default);
@@ -273,9 +280,6 @@ extern void vty_time_print (struct vty *, int);
extern char *vty_get_cwd (void);
-extern bool vty_shell (struct vty *);
-extern bool vty_term (struct vty *);
-extern bool vty_shell_serv (struct vty *);
extern void vty_hello (struct vty *);
extern enum node_type vty_get_node(struct vty *);
extern void vty_set_node(struct vty *, enum node_type);
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index b4791365..17351f58 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -31,6 +31,7 @@
#include "vio_lines.h"
#include "command.h"
+#include "command_parse.h"
#include "command_execute.h"
#include "command_queue.h"
@@ -262,7 +263,7 @@ uty_new_host_name(const char* name)
* The CLI
*/
-#define CONTROL(X) ((X) - '@')
+#define CONTROL(X) ((X) & 0x1F)
static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
static enum vty_readiness uty_cli_standard(vty_io vio) ;
@@ -309,7 +310,7 @@ static void uty_dont_lflow_ahead (vty_io vio) ;
extern void
uty_cli_init(vty_io vio)
{
- assert(vio->type == VTY_TERM) ;
+ assert(vio->vty->type == VTY_TERMINAL) ;
vio->cmd_in_progress = 1 ;
vio->cli_blocked = 1 ;
@@ -350,7 +351,7 @@ extern void
uty_cli_close(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
+ assert(vio->vty->type == VTY_TERMINAL) ;
cq_revoke(vio->vty) ;
@@ -373,7 +374,7 @@ extern enum vty_readiness
uty_cli(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
+ assert(vio->vty_type == VTY_TERM) ;
if (vio->half_closed)
return not_ready ; /* Nothing more if half closed */
@@ -411,7 +412,7 @@ static enum vty_readiness
uty_cli_standard(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
+ assert(vio->vty_type == VTY_TERM) ;
/* cli_blocked is set when is waiting for a command, or its output to
* complete -- unless either of those has happened, is still blocked.
@@ -598,7 +599,7 @@ vty_queued_result(struct vty *vty, enum cmd_return_code ret)
VTY_LOCK() ;
- vio = vty->vio ;
+ vio = vty->tos ;
if (!vio->closed)
{
@@ -622,9 +623,7 @@ vty_queued_result(struct vty *vty, enum cmd_return_code ret)
/* If the VTY is closed, the only reason it still exists is because
* there was cmd_in_progress.
*/
- vio->cmd_in_progress = 0 ;
-
- uty_close(vio) ; /* Final close */
+ vio->cmd_in_progress = 0 ; /* death watch will apply coup de grace */
} ;
VTY_UNLOCK() ;
@@ -660,7 +659,7 @@ uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
if (ret == CMD_CLOSE)
- uty_half_close(vio, NULL) ;
+ uty_close(vio, NULL) ;
vio->cmd_in_progress = 0 ; /* command complete */
vio->cmd_out_enabled = 1 ; /* enable the output */
@@ -1405,7 +1404,7 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Now process as much as possible of what there is */
ret = cli_do_nothing ;
- while (1)
+ while (ret == cli_do_nothing)
{
if (!vio->cli_drawn)
uty_cli_draw_this(vio, node) ;
@@ -1565,6 +1564,7 @@ uty_cli_process(vty_io vio, enum node_type node)
case ('D'):
uty_cli_backwards(vio, 1);
break;
+
default:
break ;
} ;
@@ -1580,17 +1580,13 @@ uty_cli_process(vty_io vio, enum node_type node)
zabort("unknown keystroke type") ;
} ;
- /* After each keystroke..... */
-
- if (ret != cli_do_nothing)
- {
- uty_cli_eol (vio) ; /* go to the end of the line */
- break ; /* stop processing */
- } ;
} ;
/* Tidy up and return where got to. */
+ if (ret != cli_do_nothing)
+ uty_cli_eol (vio) ; /* go to the end of the line */
+
qs_term(&vio->cl) ; /* add '\0' */
return ret ;
@@ -1997,14 +1993,14 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
/* make sure have a suitable history vector */
- vector_set_min_length(&vio->hist, VTY_MAXHIST) ;
+ vector_set_min_length(vio->hist, VTY_MAXHIST) ;
/* find the previous command line in the history */
prev_index = vio->hindex - 1 ;
if (prev_index < 0)
prev_index = VTY_MAXHIST - 1 ;
- prev_line = vector_get_item(&vio->hist, prev_index) ;
+ prev_line = vector_get_item(vio->hist, prev_index) ;
/* If the previous line is NULL, that means the history is empty.
*
@@ -2018,10 +2014,10 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
vio->hindex = prev_index ;
else
- prev_line = vector_get_item(&vio->hist, vio->hindex) ;
+ prev_line = vector_get_item(vio->hist, vio->hindex) ;
/* Now replace the hindex entry */
- vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
/* Advance to the near future and reset the history pointer */
vio->hindex++;
@@ -2063,8 +2059,8 @@ uty_cli_history_use(vty_io vio, int step)
/* before stepping back from the present, take a copy of the
* current command line -- so can get back to it.
*/
- hist = vector_get_item(&vio->hist, vio->hindex) ;
- vector_set_item(&vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ hist = vector_get_item(vio->hist, vio->hindex) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
} ;
/* Advance or retreat */
@@ -2074,7 +2070,7 @@ uty_cli_history_use(vty_io vio, int step)
else if (index >= VTY_MAXHIST)
index = 0 ;
- hist = vector_get_item(&vio->hist, index) ;
+ hist = vector_get_item(vio->hist, index) ;
/* If moving backwards in time, may not move back to the insertion
* point (that would be wrapping round to the present) and may not
@@ -2232,35 +2228,32 @@ uty_cli_complete_command (vty_io vio, enum node_type node)
* Command Description
*/
static void
-uty_cli_describe_command (vty_io vio, enum node_type node)
+uty_cli_describe_command (vty_io vio, node_type_t node)
{
- int ret;
- vector vline;
- vector describe;
+ cmd_return_code_t ret ;
+ vector describe ;
VTY_ASSERT_LOCKED() ;
/* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- describe = cmd_describe_command (vline, node, &ret);
- cmd_free_strvec (vline);
+ describe = cmd_describe_command (qs_term(&vio->cl), node, &ret);
uty_cli_out_newline(vio); /* clears cli_drawn */
/* Deal with result. */
switch (ret)
{
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
- case CMD_ERR_NO_MATCH:
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
- default:
- uty_cli_describe_show(vio, describe) ;
- break ;
+ default:
+ uty_cli_describe_show(vio, describe) ;
+ break ;
} ;
if (describe != NULL)
diff --git a/lib/vty_io.c b/lib/vty_io.c
index 2eadc2d1..c116e6b9 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -1,4 +1,4 @@
-/* VTY IO Functions
+/* VTY IO Functions -- top level of VTY IO hierarchy
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -21,13 +21,13 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
-
#include "vty.h"
#include "vty_io.h"
+#include "vty_io_term.h"
#include "vty_cli.h"
#include "qstring.h"
#include "keystroke.h"
+#include "list_util.h"
#include "memory.h"
@@ -44,33 +44,58 @@
#define VTYSH_DEBUG 0
/*==============================================================================
- * VTY Command Output -- base functions
+ * Basic output to VTY.
+ *
*
- * During command processing the output sent here is held until the command
- * completes.
*/
-static int uty_config_write(vty_io vio, bool all) ;
-
/*------------------------------------------------------------------------------
- * VTY output function -- cf fprintf
+ * UTY output function -- cf fprintf
+ *
+ * NB: this is NOT suppressed by ! vty->output_enabled
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
*/
extern int
-uty_out (struct vty *vty, const char *format, ...)
+uty_output(struct vty *vty, const char *format, ...)
{
- int result;
+ int ret ;
+ va_list args ;
+
VTY_ASSERT_LOCKED() ;
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- return result;
+
+ va_start (args, format) ;
+ ret = uty_vprintf(vty, format, args) ;
+ va_end (args) ;
+
+ return ret ;
}
/*------------------------------------------------------------------------------
+ * UTY reflect command line, if not already reflected
+ *
+ * NB: this is NOT suppressed by ! vty->output_enabled
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+uty_reflect(struct vty *vty)
+{
+ int ret ;
+
+ if (!vty->reflected)
+ ret = uty_output(vty, "%s\n", vty->buf) ;
+ else
+ ret = 0 ;
+
+ vty->reflected = true ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
* VTY output function -- cf vfprintf
*
* Returns: >= 0 => OK
@@ -95,47 +120,47 @@ uty_out (struct vty *vty, const char *format, ...)
* * output is discarded if the vty is no longer write_open
*/
extern int
-uty_vout(struct vty *vty, const char *format, va_list args)
+uty_vprintf(struct vty *vty, const char *format, va_list args)
{
- vty_io vio ;
+ vio_vf vf ;
int ret ;
VTY_ASSERT_LOCKED() ;
- vio = vty->vio ;
+ vf = vty->vio->vout ;
- switch (vio->type)
+ switch (vf->vout_type)
{
- case VTY_STDOUT:
- case VTY_SHELL:
- ret = vprintf (format, args) ;
+ case VOUT_NONE:
+ ret = 0 ;
break ;
- case VTY_STDERR:
- ret = vfprintf (stderr, format, args) ;
+ case VOUT_TERM:
+ ret = uty_term_vprintf(vf, format, args) ;
break ;
- case VTY_CONFIG_WRITE:
- ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
- if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
- ret = uty_config_write(vio, false) ;
+ case VOUT_SHELL:
+ ret = uty_shell_vprintf(vf, format, args) ;
break ;
- case VTY_TERM:
- case VTY_SHELL_SERV:
- assert(vio->cmd_in_progress) ;
+ case VOUT_FILE:
+ ret = uty_file_vprintf(vf, format, args) ;
+ break ;
- if (!vio->sock.write_open)
- return 0 ; /* discard output if not open ! */
+ case VOUT_PIPE:
+ ret = uty_pipe_vprintf(vf, format, args) ;
+ break ;
- /* fall through.... */
+ case VOUT_STDOUT:
+ ret = uty_stdout_vprintf(vf, format, args) ;
+ break ;
- case VTY_CONFIG_READ:
- ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ case VOUT_STDERR:
+ ret = uty_stderr_vprintf(vf, format, args) ;
break ;
default:
- zabort("impossible VTY type") ;
+ zabort("impossible VTY Output type") ;
} ;
return ret ;
@@ -152,34 +177,11 @@ uty_out_clear(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- vio_fifo_clear(&vio->cmd_obuf) ;
-
- if (vio->cmd_lc != NULL)
- vio_lc_clear(vio->cmd_lc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Flush the contents of the command output FIFO to the given file.
- *
- * Takes no notice of any errors !
- */
-extern void
-uty_out_fflush(vty_io vio, FILE* file)
-{
- char* src ;
- size_t have ;
-
- VTY_ASSERT_LOCKED() ;
-
- fflush(file) ;
-
- while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ if (vio->vout != NULL)
{
- fwrite(src, 1, have, file) ;
- vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ vio_fifo_clear(vio->vout->obuf) ;
+ vio_lc_clear(vio->vout->olc) ;
} ;
-
- fflush(file) ;
} ;
/*==============================================================================
@@ -193,97 +195,54 @@ uty_out_fflush(vty_io vio, FILE* file)
* * the death watch list
*/
-enum { vty_watch_dog_interval = 5 } ;
+/* Watch-dog timer. */
+static vio_timer_t vty_watch_dog ;
-static void vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when) ;
-static int vty_watch_dog_thread(struct thread *thread) ;
-
-static void uty_watch_dog_bark(void) ;
-static bool uty_death_watch_scan(void) ;
+static vty_timer_time uty_watch_dog_bark(vio_timer_t* timer, void* info) ;
+static bool uty_death_watch_scan(bool final) ;
/*------------------------------------------------------------------------------
- * Start watch dog -- the first time a VTY is created.
+ * Initialise watch dog -- at start-up time.
*/
extern void
-uty_watch_dog_start()
+uty_watch_dog_init(void)
{
- if (vty_cli_nexus)
- vty_watch_dog.qnexus = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, NULL) ;
+ vio_timer_init(&vty_watch_dog, NULL, NULL) ; /* empty */
+} ;
- uty_watch_dog_bark() ; /* start up by barking the first time */
-}
+/*------------------------------------------------------------------------------
+ * Start watch dog -- before a VTY is created.
+ */
+extern void
+uty_watch_dog_start(void)
+{
+ vio_timer_init(&vty_watch_dog, uty_watch_dog_bark, NULL) ;
+ vio_timer_set(&vty_watch_dog, VTY_WATCH_DOG_INTERVAL) ;
+} ;
/*------------------------------------------------------------------------------
* Stop watch dog timer -- at close down.
*
* Final run along the death-watch
- *
*/
extern void
uty_watch_dog_stop(void)
{
- if (vty_watch_dog.anon != NULL)
- {
- if (vty_cli_nexus)
- qtimer_free(vty_watch_dog.qnexus) ;
- else
- thread_cancel(vty_watch_dog.thread) ;
- } ;
-
- uty_death_watch_scan() ; /* scan the death-watch list */
+ vio_timer_reset(&vty_watch_dog) ;
+ uty_death_watch_scan(true) ; /* scan the death-watch list */
}
/*------------------------------------------------------------------------------
- * qnexus watch dog action
+ * Watch dog vio_timer action
*/
-static void
-vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
-{
- VTY_LOCK() ;
-
- uty_watch_dog_bark() ;
-
- VTY_UNLOCK() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * thread watch dog action
- */
-static int
-vty_watch_dog_thread(struct thread *thread)
-{
- VTY_LOCK() ;
-
- vty_watch_dog.thread = NULL ;
- uty_watch_dog_bark() ;
-
- VTY_UNLOCK() ;
- return 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Watch dog action
- */
-static void
-uty_watch_dog_bark(void)
+static vty_timer_time
+uty_watch_dog_bark(vio_timer_t* timer, void* info)
{
uty_check_host_name() ; /* check for host name change */
- uty_death_watch_scan() ; /* scan the death-watch list */
+ uty_death_watch_scan(false) ; /* scan the death-watch list */
- /* Set timer to go off again later */
- if (vty_cli_nexus)
- qtimer_set(vty_watch_dog.qnexus,
- qt_add_monotonic(QTIME(vty_watch_dog_interval)),
- vty_watch_dog_qnexus) ;
- else
- {
- if (vty_watch_dog.thread != NULL)
- thread_cancel (vty_watch_dog.thread);
- vty_watch_dog.thread = thread_add_timer (vty_master,
- vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
- } ;
+ return VTY_WATCH_DOG_INTERVAL ;
} ;
/*------------------------------------------------------------------------------
@@ -293,7 +252,7 @@ uty_watch_dog_bark(void)
* progress.
*/
static bool
-uty_death_watch_scan(void)
+uty_death_watch_scan(bool final)
{
vty_io vio ;
vty_io next ;
@@ -304,6 +263,12 @@ uty_death_watch_scan(void)
vio = next ;
next = sdl_next(vio, vio_list) ;
+ if (final && !vio->closed)
+
+
+ if (vio->closed)
+
+
if (vio->closed && !vio->cmd_in_progress)
{
uty_close(vio) ; /* closes again to ensure that all buffers
@@ -322,22 +287,9 @@ uty_death_watch_scan(void)
/*==============================================================================
* Prototypes.
*/
-static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
-static void uty_sock_half_close(vio_sock sock) ;
-static void uty_sock_close(vio_sock sock) ;
-
-static void vty_read_qnexus (qps_file qf, void* file_info) ;
-static void vty_write_qnexus (qps_file qf, void* file_info) ;
-static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
-static int vty_read_thread (struct thread *thread) ;
-static int vty_write_thread (struct thread *thread) ;
-static int vty_timer_thread (struct thread *thread) ;
-
-static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
-static int vtysh_read_thread (struct thread *thread) ;
-
-static enum vty_readiness uty_write(vty_io vio) ;
+static void uty_vf_half_close(vio_vf vf) ;
+static void uty_vf_close(vio_vf vf) ;
/*==============================================================================
* Creation and destruction of VTY objects
@@ -346,56 +298,53 @@ static enum vty_readiness uty_write(vty_io vio) ;
/*------------------------------------------------------------------------------
* Allocate new vty struct
*
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
+ * VTY_TERMINAL a telnet terminal server
+ *
+ * Must be in cli thread.
+ *
+ * Requires fd = socket for telnet terminal.
+ *
+ * VTY_SHELL_SERVER a vty_shell server
+ *
+ * Must be in cli thread.
+ *
+ * Requires fd = socket for talking to the VTY_SHELL_CLIENT
+ *
+ * VTY_SHELL_CLIENT a vty_shell client
+ *
+ * VTY_CONFIG_READ configuration file reader
+ *
+ * Requires fd = file descriptor for reading config file
+ *
+ * VTY_STDOUT stdout
+ * VTY_STDERR stderr
+ *
*
- * Note that where is not setting up a vty_sock, this *may* be called from
- * any thread.
*
- * NB: may not create a VTY_CONFIG_WRITE type vty directly
*
- * see: vty_open_config_write() and vty_close_config_write()
*
- * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
- * (So MUST be in the CLI thread to set those up !)
*
- * the sock_fd is ignored for everything else.
+ *
+ *
+ * Allocates and initialises basic vty and vty_io structures, setting the
+ * given type.
+ *
+ * Note that where is not setting up a vty_file, this *may* be called from
+ * any thread.
*
* Returns: new vty
*/
extern struct vty *
-uty_new(enum vty_type type, int sock_fd)
+uty_new(enum vty_type type, int fd)
{
- struct vty *vty ;
- struct vty_io* vio ;
+ vty vty ;
+ vty_io vio ;
VTY_ASSERT_LOCKED() ;
- /* If this is a VTY_TERM or a VTY_SHELL, place */
- switch (type)
- {
- case VTY_TERM: /* Require fd -- Telnet session */
- case VTY_SHELL_SERV: /* Require fd -- Unix socket */
- assert(sock_fd >= 0) ;
- break ;
-
- case VTY_CONFIG_WRITE:
- zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
- break ;
-
- case VTY_CONFIG_READ:
- case VTY_STDOUT:
- case VTY_STDERR:
- case VTY_SHELL:
- sock_fd = -1 ; /* No fd -- output to stdout/stderr */
- break ;
-
- default:
- zabort("unknown VTY type") ;
- } ;
-
/* Basic allocation */
- vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+
+ vty = XCALLOC (MTYPE_VTY, sizeof (struct vty)) ;
vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
vty->vio = vio ;
@@ -460,7 +409,11 @@ uty_new(enum vty_type type, int sock_fd)
confirm(cli_do_nothing == 0) ;
confirm(AUTH_NODE == 0) ; /* default node type */
- vio->type = type ;
+ vty->type = type ;
+
+
+
+
/* Zeroising the vty structure has set:
*
@@ -471,13 +424,40 @@ uty_new(enum vty_type type, int sock_fd)
* index = NULL -- nothing, yet
* index_sub = NULL -- nothing, yet
*/
- if (type == VTY_TERM)
- vty->newline = "\n" ; /* line control looks after "\r\n" */
- else
- vty->newline = "\n" ;
- /* Initialise the vio_sock, */
- uty_sock_init_new(&vio->sock, sock_fd, vio) ;
+ /* If this is a VTY_TERM or a VTY_SHELL, place */
+ switch (type)
+ {
+ case VTY_TERMINAL: /* Require fd -- Telnet session */
+ VTY_ASSERT_CLI_THREAD() ;
+ assert(fd >= 0) ;
+
+ uty_term_new(vio, fd) ;
+ break ;
+
+ case VTY_SHELL_SERVER: /* Require fd -- Unix socket */
+ VTY_ASSERT_CLI_THREAD() ;
+ assert(fd >= 0) ;
+
+
+ break ;
+
+ case VTY_CONFIG_READ: /* Require fd -- file to read */
+ assert(fd >= 0) ;
+ break ;
+
+ case VTY_STDOUT:
+ case VTY_STDERR:
+ case VTY_SHELL_CLIENT:
+ fd = -1 ; /* No fd -- output to stdout/stderr */
+ break ;
+
+ default:
+ zabort("unknown VTY type") ;
+ } ;
+
+
+
/* Make sure all buffers etc. are initialised clean and empty.
*
@@ -499,132 +479,77 @@ uty_new(enum vty_type type, int sock_fd)
} ;
/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERM -- ie attached to a telnet session.
+ * Add a new vf to the vio->vin stack, and set read stuff.
*
- * Returns: new vty
+ * Sets the vf->vin_type and set vf->read_open.
+ *
+ * Sets the read ready action and the read timer timeout action.
+ *
+ * NB: may add a VIN_NONE *only* as the first vin item.
+ *
+ * Can have a write only VTY_XXX object, which requires a vin entry so that
+ * do not have to everywhere deal with NULL vio_vf pointers.
+ *
+ * But for subsequent write only vio_vf objects, must not add to the vin
+ * stack.
*/
-static struct vty *
-uty_new_term(int sock_fd, union sockunion *su)
+extern void
+uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_fd_action* read_action, vio_timer_action* read_timer_action)
{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
+ vf->vin_type = type ;
+ vf->read_open = true ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_TERM, sock_fd) ;
- vio = vty->vio ;
-
- /* Allocate and initialise a keystroke stream TODO: CSI ?? */
- vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+ vio_fd_set_read_action(vf->vfd, read_action) ;
+ vio_fd_set_read_timeout_action(vf->vfd, read_timer_action) ;
- /* Set the socket action functions */
- if (vty_cli_nexus)
- {
- vio->sock.action.read.qnexus = vty_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = vty_timer_qnexus ;
- }
+ ssl_push(vio->vin, vf, vin_next) ;
+ if (vio->vin_base == NULL)
+ vio->vin_base = vf ;
else
- {
- vio->sock.action.read.thread = vty_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = vty_timer_thread ;
- } ;
-
- /* The text form of the address identifies the VTY */
- vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
-
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
-
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
-
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
+ assert(type != VIN_NONE) ;
+} ;
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
+/*------------------------------------------------------------------------------
+ * Add a new vf to the vio->vout stack, and set write stuff.
+ *
+ * Sets the vf->vout_type and set vf->write_open.
+ *
+ * Sets the write ready action and the write timer timeout action.
+ *
+ * NB: may add a VOUT_NONE *only* as the first vout item.
+ *
+ * Can have a read only VTY_XXX object, which requires a vout entry so that
+ * do not have to everywhere deal with NULL vio_vf pointers.
+ *
+ * But for subsequent read only vio_vf objects, must not add to the vout
+ * stack.
+ */
+extern void
+uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_fd_action* write_action, vio_timer_action* write_timer_action)
+{
+ vf->vout_type = type ;
+ vf->write_open = true ;
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+ vio_fd_set_write_action(vf->vfd, write_action) ;
+ vio_fd_set_write_timeout_action(vf->vfd, write_timer_action) ;
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_half_close (vio, "Vty password is not set.");
- vty = NULL;
- }
+ ssl_push(vio->vout, vf, vout_next) ;
+ if (vio->vout_base == NULL)
+ vio->vout_base = vf ;
else
- {
- /* Say hello to the world. */
- vty_hello (vty);
+ assert(type != VOUT_NONE) ;
+} ;
- if (! no_password_check)
- uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE);
- } ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
- return vty;
-} ;
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
- *
- * Returns: new vty
- */
-static struct vty *
-uty_new_shell_serv(int sock_fd)
-{
- struct vty *vty ;
- vty_io vio ;
- VTY_ASSERT_LOCKED() ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
- vio = vty->vio ;
- /* Set the action functions */
- if (vty_cli_nexus)
- {
- vio->sock.action.read.qnexus = vtysh_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = NULL ;
- }
- else
- {
- vio->sock.action.read.thread = vtysh_read_thread ;
- vio->sock.action.write.thread = vty_write_thread ;
- vio->sock.action.timer.thread = NULL ;
- } ;
- vty->node = VIEW_NODE;
- /* Kick start the CLI etc. */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
-
- return vty;
-} ;
/*------------------------------------------------------------------------------
* Set/Clear "monitor" state:
@@ -639,7 +564,7 @@ uty_set_monitor(vty_io vio, bool on)
if (on && !vio->monitor)
{
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
+ if ((vio->vty->type == VTY_TERMINAL) && !vio->half_closed)
{
vio->monitor = 1 ;
sdl_push(vio_monitors_base, vio, mon_list) ;
@@ -664,87 +589,141 @@ uty_get_name(vty_io vio)
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY for reading.
+ * Close all the readers.
+ *
+ * Half-close everything on the vin stack. Empties the vin stack down to the
+ * base entry. Discards any read-only vio_vf (except for last vin entry).
*
- * For VTY_TERM (must be in CLI thread):
+ * VIO is placed on death watch, and will stay there until:
+ *
+ * * outstanding commands complete.
+ *
+ * * outstanding output completes, or times out, or program terminates.
+ *
+ * For VTY_TERMINAL (must be in CLI thread):
*
* * shut the socket for reading
* * discard all buffered input, setting it to "EOF"
* * turns off any monitor status !
* * drop down to RESTRICTED_NODE
*
- * For VTY_SHELL_SERV (must be in CLI thread):
+ * For VTY_SHELL_SERVER (must be in CLI thread):
*
* * shut the socket for reading
* * discard all buffered input
* * drop down to RESTRICTED_NODE
*
- * In all cases:
- *
- * * place on death watch
- * * set the vty half_closed
- * * sets the reason for closing (if any given)
+ * If no reason for the close has already been set, sets the given reason.
*
- * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
- * the buffers, the VTY is closed.
+ * If already half-closed, does nothing else.
*
- * May already have set the vio->close_reason, or can set it now. (Passing a
- * NULL reason has no effect on any existing posted reason.)
+ * Returns true <=> first half close.
*/
-extern void
-uty_half_close (vty_io vio, const char* reason)
+static bool
+uty_do_half_close(vty_io vio, const char* reason)
{
+ vio_vf vf ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->half_closed)
- return ;
+ if ((vio->close_reason == NULL) && (reason != NULL))
+ vio->close_reason = XSTRDUP(MTYPE_TMP, reason) ;
- if (reason != NULL)
- vio->close_reason = reason ;
+ if (vio->half_closed)
+ return false ;
- /* Do the file side of things
+ /* Half close everything on the vin stack.
*
- * Note that half closing the file sets a new timeout, sets read off
- * and write on.
+ * Leave stack with just the base entry (closed for read).
*/
- uty_sock_half_close(&vio->sock) ;
- uty_set_monitor(vio, 0) ;
+ while (1)
+ {
+ vf = vio->vin ; /* Current first on list */
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
+ uty_vf_half_close(vf) ; /* fd level half close etc. */
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
+ switch(vf->vin_type) /* tidy up each type */
+ {
+ case VIN_NONE:
+ break ;
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
+ case VIN_TERM:
+ uty_term_half_close(vf) ;
+ uty_cli_close(vio) ; /* tell the CLI to stop */
+ break ;
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
+ case VIN_SHELL:
+ break ;
+
+ case VIN_FILE:
+ break ;
+
+ case VIN_PIPE:
+ break ;
+
+ case VIN_CONFIG:
+ break ;
+
+ default:
+ zabort("unknown VIN type") ;
+ } ;
+
+ /* Finished if half closed the last on the list */
+ if (vf == vio->vin_base)
+ break ;
+
+ /* Hack off head of list. If it is read-only, close & free. */
+ ssl_del_head(vio->vin, vin_next) ;
+
+ if (vf->vout_type == VOUT_NONE)
+ uty_vf_close(vf) ;
+ } ;
/* Make sure no longer holding the config symbol of power */
uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
- /* Log closing of VTY_TERM */
- if (vio->type == VTY_TERM)
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
-
/* Move to the death watch list */
sdl_del(vio_list_base, vio, vio_list) ;
sdl_push(vio_death_watch, vio, vio_list) ;
- vio->half_closed = 1 ;
+ vio->half_closed = true ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY.
+ *
+ *
+ *
+ * If no reason for the close has already been set, sets the given reason.
+ *
+ * If not already half-closed, close all readers as described above, and then
+ * kick everything on the vout stack and apply VTY_HALF_CLOSE_TIMEOUT.
+ *
+ *
+ */
+extern void
+uty_close (vty_io vio, const char* reason)
+{
+ if (uty_do_half_close(vio, reason))
+ {
+ vio_vf vf ;
+
+ /* Run down the vout stack, and write-ready enable everything with
+ * the half close timeout set.
+ *
+ * This will have the effect of kicking all output.
+ */
+ vf = vio->vout ;
+ while (vf != NULL)
+ {
+ vf->write_timeout = 0 ;
+ uty_vf_set_write(vf, on) ;
+
+ vf = ssl_next(vf, vout_next) ;
+ } ;
+ } ;
} ;
/*------------------------------------------------------------------------------
@@ -759,33 +738,70 @@ uty_half_close (vty_io vio, const char* reason)
*
* The vty structure is placed on death watch, which will finally free the
* structure once no longer cmd_in_progress.
+ *
+ *
+ * If no reason for the close has already been set, sets the given reason.
+ *
*/
extern void
-uty_close (vty_io vio)
+uty_close_final(vty_io vio, const char* reason)
{
VTY_ASSERT_LOCKED() ;
- /* Empty all the output buffers */
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
- vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
-
/* If not already closed, close. */
+ uty_do_half_close(vio, reason) ; /* set reason if required, and
+ make sure is half closed. */
if (!vio->closed)
{
- uty_half_close(vio, NULL) ; /* place on death watch -- if not
- already done */
- if (vio->type == VTY_TERM)
- uty_cli_close(vio) ; /* tell the CLI to stop */
+ vio_vf vf ;
+
+ /* Empty the vin stack */
+ assert(vio->vin != NULL) ;
+ assert(vio->vin == vio->vin_base) ;
+ if (vio->vin != vio->vout_base)
+ uty_vf_close(vio->vin) ;
+ vio->vin_base = vio->vin = NULL ;
+
+ /* Now kick everything in the vout stack, in case can get stuff
+ * written away. And then close.
+ */
+ while (ssl_pop(&vf, vio->vout, vout_next) != NULL)
+ {
+ uty_vf_set_write(vf, off) ; /* stop any write ready... */
+ vf->closing = true ; /* ...permanently. */
+
+ switch(vf->vout_type) /* tidy up each type */
+ {
+ case VOUT_NONE:
+ break ;
+
+ case VOUT_TERM:
+ break ;
+
+ case VOUT_SHELL:
+ break ;
+
+ case VOUT_FILE:
+ break ;
+
+ case VOUT_PIPE:
+ break ;
- vio->closed = 1 ; /* now closed (stop uty_write()
- from recursing) */
+ case VOUT_STDOUT:
+ break ;
- if (vio->sock.write_open)
- uty_write(vio) ; /* last gasp attempt */
+ case VOUT_STDERR:
+ break ;
- uty_sock_close(&vio->sock) ;
+ default:
+ zabort("unknown VOUT type") ;
+ } ;
+ uty_vf_close(vf) ; /* close and free */
+ } ;
+
+ vio->vout_base = NULL ; /* stack is empty */
+ vio->closed = true ; /* now closed */
} ;
/* Nothing more should happen, so can now release almost everything,
@@ -799,12 +815,12 @@ uty_close (vty_io vio)
vio->key_stream = keystroke_stream_free(vio->key_stream) ;
- qs_free_body(&vio->cli_prompt_for_node) ;
- qs_free_body(&vio->cl) ;
+ qs_reset(&vio->cli_prompt_for_node, keep_it) ;
+ qs_reset(&vio->cl, keep_it) ;
{
qstring line ;
- while ((line = vector_ream_keep(&vio->hist)) != NULL)
+ while ((line = vector_ream(vio->hist, keep_it)) != NULL)
qs_reset_free(line) ;
} ;
@@ -817,404 +833,98 @@ uty_close (vty_io vio)
*/
if (!vio->cmd_in_progress)
{
- qs_free_body(&vio->clx) ;
+ qs_reset(&vio->clx, keep_it) ;
vio->vty->buf = NULL ;
} ;
} ;
/*==============================================================================
- * For writing configuration file by command, temporarily redirect output to
- * an actual file.
- */
-
-/*------------------------------------------------------------------------------
- * Set the given fd as the VTY_FILE output.
+ * vio_vf level operations
*/
-extern void
-vty_open_config_write(struct vty* vty, int fd)
-{
- vty_io vio ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
-
- vio->real_type = vio->type ;
-
- vio->type = VTY_CONFIG_WRITE ;
- vio->file_fd = fd ;
- vio->file_error = 0 ;
-
- VTY_UNLOCK() ;
-} ;
/*------------------------------------------------------------------------------
- * Write away configuration file stuff -- all or just the full lump(s).
+ * Create and initialise a new vio_vf structure.
*
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see vio->file_error
- */
-static int
-uty_config_write(vty_io vio, bool all)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- if (vio->file_error == 0)
- {
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
-
- if (ret < 0)
- vio->file_error = errno ;
- }
- else
- ret = -1 ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write away any pending stuff, and return the VTY to normal.
- */
-extern int
-vty_close_config_write(struct vty* vty)
-{
- vty_io vio ;
- int err ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
-
- uty_config_write(vio, true) ; /* write all that is left */
-
- err = vio->file_error ;
-
- vio->type = vio->real_type ;
- vio->file_fd = -1 ;
- vio->file_error = 0 ;
-
- VTY_UNLOCK() ;
-
- return err ;
-} ;
-
-/*==============================================================================
- * vio_sock level operations
- */
-
-/*------------------------------------------------------------------------------
- * Initialise a new vio_sock structure.
+ * There are no errors, yet.
*
- * Requires that: the vio_sock structure is not currently in use.
+ * This leaves most things unset/NULL/false. Caller will want to set:
*
- * if fd >= 0 then: sock is open and ready read and write
- * otherwise: sock is not open
*
- * there are no errors, yet.
*
* Sets timeout to no timeout at all -- timeout is optional.
- *
- * NB: MUST be in the CLI thread if the fd is >= 0 !
*/
-static void
-uty_sock_init_new(vio_sock sock, int fd, void* info)
+extern vio_vf
+uty_vf_new(vty_io vio, int fd, vfd_type_t type, vfd_io_type_t io_type)
{
- VTY_ASSERT_LOCKED() ;
+ vio_vf vf ;
- if (fd >= 0)
- VTY_ASSERT_CLI_THREAD() ;
+ VTY_ASSERT_LOCKED() ;
- memset(sock, 0, sizeof(struct vio_sock)) ;
+ vf = XCALLOC (MTYPE_VTY, sizeof(struct vio_vf)) ;
/* Zeroising the structure has set:
*
- * action = all the actions set NULL
+ * vin_type = 0 -- VIN_NONE
+ * vin_next = NULL -- not on a vin list, yet
+ *
+ * vout_type = NULL -- VOUT_NONE
+ * vout_next = NULL -- not on a vout list, yet
*
- * error_seen = 0 -- no error, yet
+ * obuf = NULL -- none, yet
+ * olc = NULL -- none, yet
*
- * qf = NULL -- no qfile, yet
- * t_read = NULL ) no threads, yet
- * t_write = NULL )
+ * blocking = false
+ * closing = false
*
- * v_timeout = 0 -- no timeout set
- * timer_runing = 0 -- not running, yet
- * t_timer = NULL -- no timer thread, yet
- * qtr = NULL -- no qtimer, yet
+ * read_open = false
+ * write_open = false
+ * error_seen = 0 -- no error seen, yet
+ *
+ * read_timeout = 0 -- none
+ * write_timeout = 0 -- none
*/
- sock->fd = fd ;
- sock->info = info ;
+ confirm((VIN_NONE == 0) && (VOUT_NONE == 0)) ;
- sock->read_open = (fd >= 0) ;
- sock->write_open = (fd >= 0) ;
+ vf->vio = vio ;
+ vf->vfd = vio_fd_new(fd, type, io_type, vf) ;
- if ((fd >= 0) && vty_cli_nexus)
- {
- sock->qf = qps_file_init_new(NULL, NULL);
- qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
- } ;
+ return vf ;
} ;
/*------------------------------------------------------------------------------
- * Restart the timer.
- *
- * If a timeout time is set, then start or restart the timer with that value.
+ * Half close a vio_vf.
*
- * If no timeout time is set, and the timer is running, unset it.
+ * Half closes the vio_fd and shuts down all reading. If the vio_fd was
+ * read-only, the half-close will fully close it, and the vio_fd will have
+ * been freed.
*/
static void
-uty_sock_restart_timer(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (sock->v_timeout != 0)
- {
- assert(sock->action.timer.anon != NULL) ;
-
- if (vty_cli_nexus)
- {
- if (sock->qtr == NULL) /* allocate qtr if required */
- sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, sock->info) ;
- qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
- sock->action.timer.qnexus) ;
- }
- else
- {
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer);
- sock->t_timer = thread_add_timer (vty_master,
- sock->action.timer.thread, sock->info, sock->v_timeout) ;
- } ;
-
- sock->timer_running = 1 ;
- }
- else if (sock->timer_running)
- {
- if (vty_cli_nexus)
- {
- if (sock->qtr != NULL)
- qtimer_unset(sock->qtr) ;
- }
- else
- {
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer) ;
- } ;
-
- sock->timer_running = 0 ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set read on/off
- *
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_read(vio_sock sock, bool on)
+uty_vf_half_close(vio_vf vf)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (sock->fd < 0)
- return 0 ;
-
- if (on)
- {
- assert(sock->action.read.anon != NULL) ;
-
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
- else
- {
- if (sock->t_read != NULL)
- thread_cancel(sock->t_read) ;
+ /* the vfd level half close will close completely if is read only. */
+ vf->vfd = vio_fd_half_close(vf->vfd) ;
- sock->t_read = thread_add_read(vty_master,
- sock->action.read.thread, sock->info, sock->fd) ;
- } ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_read_mbit) ;
- else
- {
- if (sock->t_read != NULL)
- thread_cancel (sock->t_read) ;
- } ;
- } ;
+ vf->read_open = false ; /* no more read operations */
+ vf->read_on = off ; /* of course */
- return on ;
+ if (vf->vfd == NULL) /* check really was read-only */
+ assert(!vf->write_open && !vf->read_on) ;
} ;
/*------------------------------------------------------------------------------
- * Set write on/off
+ * Close given vio_vf and free the vio_vf structure and all its contents.
*
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_write(vio_sock sock, bool on)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (sock->fd < 0)
- return 0 ;
-
- if (on)
- {
- assert(sock->action.write.anon != NULL) ;
-
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
-
- sock->t_write = thread_add_write(vty_master,
- sock->action.write.thread, sock->info, sock->fd) ;
- } ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_write_mbit) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel (sock->t_write) ;
- } ;
- } ;
-
- return on ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set read/write readiness -- for VTY_TERM
- *
- * Note that for VTY_TERM, set only one of read or write, and sets write for
- * preference.
- */
-extern void
-uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- uty_sock_set_read(sock, (ready == read_ready)) ;
- uty_sock_set_write(sock, (ready >= write_ready)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_sock_set_timer(vio_sock sock, unsigned long timeout)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- sock->v_timeout = timeout ;
- if (sock->timer_running)
- uty_sock_restart_timer(sock) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close given vty sock for reading.
- *
- * Sets timer to timeout for clearing any pending output.
- *
- * NB: if there is a socket, MUST be in the CLI thread
- */
-static void
-uty_sock_half_close(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
-
- if (sock->fd < 0)
- return ; /* nothing more if no socket */
-
- VTY_ASSERT_CLI_THREAD() ;
-
- shutdown(sock->fd, SHUT_RD) ; /* actual half close */
-
- uty_sock_set_read(sock, off) ;
- uty_sock_set_write(sock, on) ;
- sock->v_timeout = 30 ; /* for output to clear */
- uty_sock_restart_timer(sock) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close given vio_sock, completely -- shut down any timer.
- *
- * Structure is cleared of everything except the last error !
- *
- * NB: if there is a socket, MUST be in the CLI thread
+ * Assumes has been removed from vio->vin and vio->vout !
*/
static void
-uty_sock_close(vio_sock sock)
+uty_vf_close(vio_vf vf)
{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
- sock->write_open = 0 ;
+ vf->vfd = vio_fd_close(vf->vfd) ;
- if (sock->fd < 0)
- {
- assert( (sock->qf == NULL)
- && (sock->qtr == NULL)
- && (sock->t_read == NULL)
- && (sock->t_write == NULL)
- && (sock->t_timer == NULL) ) ;
- return ; /* no more to be done here */
- } ;
-
- VTY_ASSERT_CLI_THREAD() ;
- close(sock->fd) ;
+ vf->obuf = vio_fifo_reset_free(vf->obuf) ;
+ vf->olc = vio_lc_reset_free(vf->olc) ;
- if (vty_cli_nexus)
- {
- assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
- qps_remove_file(sock->qf) ;
- qps_file_free(sock->qf) ;
- sock->qf = NULL ;
- } ;
-
- sock->fd = -1 ;
-
- if (sock->t_read != NULL)
- thread_cancel(sock->t_write) ;
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
-
- sock->t_read = NULL ;
- sock->t_write = NULL ;
-
- sock->info = NULL ;
- sock->action.read.anon = NULL ;
- sock->action.write.anon = NULL ;
- sock->action.timer.anon = NULL ;
-
- if (sock->qtr != NULL)
- qtimer_free(sock->qtr) ;
- if (sock->t_timer != NULL)
- thread_cancel(sock->t_timer) ;
-
- sock->v_timeout = 0 ;
- sock->qtr = NULL ;
- sock->t_timer = NULL ;
+ XFREE(MTYPE_VTY, vf) ;
} ;
/*------------------------------------------------------------------------------
@@ -1225,7 +935,7 @@ uty_sock_close(vio_sock sock)
* If is a "monitor", turn that off, *before* issuing log message.
*/
static int
-uty_sock_error(vty_io vio, const char* what)
+uty_vf_error(vty_io vio, const char* what)
{
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
@@ -1237,16 +947,16 @@ uty_sock_error(vty_io vio, const char* what)
if (vio->sock.error_seen == 0)
{
const char* type ;
- switch (vio->type)
+ switch (vio->vty_type)
{
case VTY_TERM:
type = "VTY Terminal" ;
break ;
- case VTY_SHELL_SERV:
+ case VTY_SHELL:
type = "VTY Shell Server" ;
break ;
default:
- zabort("unknown VTY type for uty_sock_error()") ;
+ zabort("unknown VTY type for uty_file_error()") ;
} ;
vio->sock.error_seen = errno ;
@@ -1257,1488 +967,134 @@ uty_sock_error(vty_io vio, const char* what)
return -1 ;
} ;
-/*==============================================================================
- * Readiness and the VTY_TERM type VTY.
- *
- * For VTY_TERM the driving force is write ready. This is used to prompt the
- * VTY_TERM when there is outstanding output (obviously), but also if there
- * is buffered input in the keystroke stream.
- *
- * The VTY_TERM uses read ready only when it doesn't set write ready. Does
- * not set both at once.
- *
- * So there is only one, common, uty_ready function, which:
- *
- * 1. attempts to clear any output it can.
- *
- * The state of the output affects the CLI, so must always do this before
- * before invoking the CLI.
- *
- * If this write enters the "--more--" state, then will have tried to
- * write away the prompt.
- *
- * 2. invokes the CLI
- *
- * Which will do either the standard CLI stuff or the special "--more--"
- * stuff.
- *
- * 3. attempts to write any output there now is.
- *
- * If the CLI generated new output, as much as possible is written away
- * now.
- *
- * If this write enters the "--more--" state, then it returns now_ready,
- * if the prompt was written away, which loops back to the CLI.
- *
- * Note that this is arranging:
- *
- * a. to write away the "--more--" prompt as soon as the tranche of output to
- * which it refers, completes
- *
- * b. to enter the cli_more_wait CLI for the first time immediately after the
- * "--more--" prompt is written away.
- *
- * The loop limits itself to one trache of command output each time.
- *
- * Resets the timer because something happened.
- */
-static void
-uty_ready(vty_io vio)
-{
- enum vty_readiness ready ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio->cmd_out_done = 0 ; /* not done any command output yet */
-
- uty_write(vio) ; /* try to clear outstanding stuff */
- do
- {
- ready = uty_cli(vio) ; /* do any CLI work... */
- ready |= uty_write(vio) ; /* ...and any output that generates */
- } while (ready >= now_ready) ;
-
- uty_sock_set_readiness(&vio->sock, ready) ;
- uty_sock_restart_timer(&vio->sock) ;
-} ;
-
-/*==============================================================================
- * Reading from VTY_TERM.
- *
- * The select/pselect call-back ends up in uty_read_ready().
- *
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
- */
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking CLI
- */
-static void
-vty_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking CLI
- */
-static int
-vty_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_read = NULL ; /* implicitly */
- uty_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the keystroke stream
- *
- * Steal keystroke if required -- see keystroke_input()
- *
- * Returns: 0 => nothing available
- * > 0 => read at least one byte
- * -1 => EOF (or not open, or failed)
- */
-extern int
-uty_read (vty_io vio, keystroke steal)
-{
- unsigned char buf[500] ;
- int get ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
-
- get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
- if (get >= 0)
- keystroke_input(vio->key_stream, buf, get, steal) ;
- else if (get < 0)
- {
- if (get == -1)
- uty_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
-
- get = -1 ;
- } ;
-
- return get ;
-} ;
-
-/*==============================================================================
- * The write sock action for VTY_TERM type VTY
- *
- * There are two sets of buffering:
- *
- * cli -- command line -- which reflects the status of the command line
- *
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
- *
- * The cli output takes precedence.
- *
- * Output of command stuff is subject to line_control, and may go through the
- * "--more--" mechanism.
- */
-
-static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
-static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to write -> try to empty buffers
- */
-static void
-vty_write_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info ;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: ready to write -> try to empty buffers
- */
-static int
-vty_write_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_write = NULL; /* implicitly */
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Write as much as possible of what there is.
- *
- * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
- * empty.
- *
- * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
- * and all output instantly successful.
- *
- * Sets write on if prevented from writing everything available for output
- * by write() threatening to block.
- *
- * Returns: write_ready if should now set write on
- * now_ready if should loop back and try again
- * not_ready otherwise
- */
-static enum vty_readiness
-uty_write(vty_io vio)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- ret = -1 ;
- while (vio->sock.write_open)
- {
- /* Any outstanding line control output takes precedence */
- if (vio->cmd_lc != NULL)
- {
- ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
- if (ret != 0)
- break ;
- }
-
- /* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret != 0)
- break ;
-
- /* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
- return not_ready ; /* done all can do */
-
- /* Last: if there is something in the command buffer, do that */
- if (!vio_fifo_empty(&vio->cmd_obuf))
- {
- if (vio->cmd_out_done)
- break ; /* ...but not if done once */
-
- vio->cmd_out_done = 1 ; /* done this once */
-
- assert(!vio->cli_more_wait) ;
-
- if (vio->cmd_lc != NULL)
- ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
- else
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->sock.fd, true) ;
-
- /* If moved into "--more--" state@
- *
- * * the "--more--" prompt is ready to be written, so do that now
- *
- * * if that completes, then want to run the CLI *now* to perform the
- * first stage of the "--more--" process.
- */
- if (vio->cli_more_wait)
- {
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret == 0)
- return now_ready ;
- } ;
-
- if (ret != 0)
- break ;
- }
-
- /* Exciting stuff: there is nothing left to output...
- *
- * ... watch out for half closed state.
- */
- if (vio->half_closed)
- {
- if (vio->close_reason != NULL)
- {
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
-
- vio->close_reason = NULL ; /* MUST discard now... */
- continue ; /* ... and write away */
- } ;
-
- if (!vio->closed) /* avoid recursion */
- uty_close(vio) ;
-
- return not_ready ; /* it's all over */
- } ;
-
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
- */
- if (vio->type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
-
- /* There really is nothing left to output */
- return not_ready ;
- } ;
-
- /* Arrives here if there is more to do, or failed (or was !write_open) */
-
- if (ret >= 0)
- return write_ready ;
-
- /* If is write_open, then report the error
- *
- * If still read_open, let the reader pick up and report the error, when it
- * has finished anything it has buffered.
- */
- if (vio->sock.write_open)
- {
- if (!vio->sock.read_open)
- uty_sock_error(vio, "write") ;
-
- vio->sock.write_open = 0 ; /* crash close write */
- } ;
-
- /* For whatever reason, is no longer write_open -- clear all buffers.
- */
- vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
- uty_out_clear(vio) ; /* throw away cmd stuff */
-
- vio->close_reason = NULL ; /* too late for this */
-
- return not_ready ; /* NB: NOT blocked by I/O */
-} ;
-
/*------------------------------------------------------------------------------
- * Write as much as possible -- for "monitor" output.
+ * Set required read ready state. Applies the current read timeout.
*
- * Outputs only:
+ * Forces off if: vf->closing
*
- * a. outstanding line control stuff.
- *
- * b. contents of CLI buffer
- *
- * And:
- *
- * a. does not report any errors.
- *
- * b. does not change anything except the state of the buffers.
- *
- * In particular, for the qpthreaded world, does not attempt to change
- * the state of the qfile or any other "thread private" structures.
- *
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed (or !write_open)
+ * Does nothing if: !vf->read_open
+ * or: vf->vfd == NULL
*/
-static int
-uty_write_monitor(vty_io vio)
+extern on_off_t
+uty_vf_set_read(vio_vf vf, on_off_t how)
{
- VTY_ASSERT_LOCKED() ;
-
- if (!vio->sock.write_open)
- return -1 ;
-
- if (vio->cmd_lc != NULL)
- {
- int ret ;
- ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
-
- if (ret != 0)
- return ret ;
- } ;
-
- return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (vf->closing)
+ how = off ;
+ return vf->read_on = vf->read_open
+ ? vio_fd_set_read(vf->vfd, how, vf->read_timeout)
+ : off ;
} ;
/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to possible line control.
- *
- * Note that even if no "--more--" is set, will have set some height, so
- * that does not attempt to empty the FIFO completely all in one go.
- *
- * If the line control becomes "paused", it is time to enter "--more--" state
- * -- unless the FIFO is empty (or "--more--" is not enabled).
+ * Set required read ready timeout -- if already read_on, restart it.
*
- * NB: expects that the sock is write_open
+ * Forces read ready off if: vf->closing
*
- * Returns: > 0 => blocked or completed one tranche
- * 0 => all gone
- * < 0 => failed
+ * Does nothing if: !vf->read_open
+ * or: vf->vfd == NULL
*/
-static int
-uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+extern on_off_t
+uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
{
- int ret ;
- char* src ;
- size_t have ;
-
- /* Collect another line_control height's worth of output.
- *
- * Expect the line control to be empty at this point, but it does not have
- * to be.
- */
- vio_lc_set_pause(lc) ; /* clears lc->paused */
-
- src = vio_fifo_get_rdr(vf, &have) ;
-
- while ((src != NULL) && (!lc->paused))
- {
- size_t take ;
- take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vf, &have, take) ;
- } ;
-
- vio->cli_dirty = (lc->col != 0) ;
-
- /* Write the contents of the line control */
- ret = uty_write_lc(vio, vf, lc) ;
-
- if (ret < 0)
- return ret ; /* give up now if failed. */
-
- if ((ret == 0) && vio_fifo_empty(vf))
- return 0 ; /* FIFO and line control empty */
-
- /* If should now do "--more--", now is the time to prepare for that.
- *
- * Entering more state issues a new prompt in the CLI buffer, which can
- * be written once line control write completes.
- *
- * The "--more--" cli will not do anything until the CLI buffer has
- * cleared.
- */
- if (lc->paused && vio->cli_more_enabled)
- uty_cli_enter_more_wait(vio) ;
-
- return 1 ; /* FIFO or line control, not empty */
+ vf->read_timeout = read_timeout ;
+ return vf->read_on ? uty_vf_set_read(vf, on)
+ : off ;
} ;
/*------------------------------------------------------------------------------
- * Write contents of line control (if any).
- *
- * NB: expects that the sock is write_open
+ * Set required write ready state. Applies the current write timeout.
*
- * NB: does nothing other than write() and buffer management.
+ * Forces off if: vf->closing
*
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed
+ * Does nothing if: !vf->write_open
+ * or: vf->vfd == NULL
*/
-static int
-uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+extern on_off_t
+uty_vf_set_write(vio_vf vf, on_off_t how)
{
- int ret ;
-
- ret = vio_lc_write_nb(vio->sock.fd, lc) ;
+ if (vf->closing)
+ how = off ;
- if (ret <= 0)
- vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
-
- return ret ;
+ return vf->write_on = vf->write_open
+ ? vio_fd_set_write(vf->vfd, how, vf->write_timeout)
+ : off ;
} ;
/*------------------------------------------------------------------------------
- * Start command output -- clears down the line control.
+ * Set required write ready timeout -- if already write_on, restart it.
*
- * Requires that that current line is empty -- restarts the line control
- * on the basis that is at column 0.
- */
-extern void
-uty_cmd_output_start(vty_io vio)
-{
- if (vio->cmd_lc != NULL)
- vio_lc_clear(vio->cmd_lc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
- *
- * If using line_control, may enable the "--more--" output handling.
- *
- * If not, want some limit on the amount of stuff output at a time.
+ * Forces write ready off if: vf->closing
*
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
+ * Does nothing if: !vf->write_open
+ * or: vf->vfd == NULL
*/
-extern void
-uty_set_height(vty_io vio)
+extern on_off_t
+uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout)
{
- bool on ;
-
- on = 0 ; /* default state */
-
- if ((vio->cmd_lc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
-
- if ((vio->width) != 0)
- {
- /* If window size is known, use lines or given height */
- if (vio->lines >= 0)
- height = vio->lines ;
- else
- {
- /* Window height, leaving one line from previous "page"
- * and one line for the "--more--" -- if at all possible
- */
- height = vio->height - 2 ;
- if (height < 1)
- height = 1 ;
- } ;
- }
- else
- {
- /* If window size not known, use lines if that has been set
- * explicitly for this terminal.
- */
- if (vio->lines_set)
- height = vio->lines ;
- } ;
-
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
-
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
-
- vio->cli_more_enabled = on ;
+ vf->write_timeout = write_timeout ;
+ return vf->write_on ? uty_vf_set_write(vf, on)
+ : off ;
} ;
/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
- */
-
-/*------------------------------------------------------------------------------
- * Timer has expired.
- *
- * If half_closed, then this is curtains -- have waited long enough !
- *
- * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
- */
-static void
-uty_timer_expired (vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
-
- uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
- } ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: deal with timer timeout.
- */
-static void
-vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
-{
- vty_io vio = timer_info ;
-
- VTY_LOCK() ;
-
- uty_timer_expired(vio);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: deal with timer timeout.
- */
-static int
-vty_timer_thread (struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- vio->sock.t_timer = NULL ; /* implicitly */
-
- uty_timer_expired(vio) ;
-
- VTY_UNLOCK() ;
- return 0;
-}
-
-/*==============================================================================
* VTY Listener(s)
*
- * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
+ * Have listeners for VTY_TERMINAL and VTY_SHELL_SERVER types of VTY.
*/
-typedef struct vty_listener* vty_listener ;
-
-struct vty_listener
-{
- vty_listener next ; /* ssl type list */
-
- enum vty_type type ;
-
- struct vio_sock sock ;
-};
-
/* List of listeners so can tidy up. */
-static vty_listener vty_listeners_list = NULL ;
-
-/* Prototypes for listener stuff */
-static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
-static int uty_serv_sock(const char* addr, unsigned short port) ;
-static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port) ;
-static int uty_serv_vtysh(const char *path) ;
-static int vty_accept_thread(struct thread *thread) ;
-static void vty_accept_qnexus(qps_file qf, void* listener) ;
-static int uty_accept(vty_listener listener, int listen_sock) ;
-static int uty_accept_term(vty_listener listener) ;
-static int uty_accept_shell_serv (vty_listener listener) ;
-
-static void uty_serv_start_listener(int fd, enum vty_type type) ;
-
-/*------------------------------------------------------------------------------
- * If possible, will use getaddrinfo() to find all the things to listen on.
- */
-
-#if defined(HAVE_IPV6) && !defined(NRL)
-# define VTY_USE_ADDRINFO 1
-#else
-# define VTY_USE_ADDRINFO 0
-#endif
+static vio_listener vty_listeners_list = NULL ;
/*------------------------------------------------------------------------------
- * Open VTY listener(s)
+ * Open VTY listener(s) for VTY_TERMINAL and VTY_SHELL_SERVER.
*
- * addr -- address ) to listen for VTY_TERM connections
+ * addr -- address ) to listen for VTY_TERMINAL connections
* port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ * path -- path for VTY_SHELL_SERVER connections -- if VTYSH_ENABLED
*/
extern void
uty_open_listeners(const char *addr, unsigned short port, const char *path)
{
VTY_ASSERT_LOCKED() ;
- /* If port is set to 0, do not listen on TCP/IP at all! */
+ /* If port is set to 0, do not listen for VTY_TERMINAL at all! */
if (port)
- {
- int n ;
-
- if (VTY_USE_ADDRINFO)
- n = uty_serv_sock_addrinfo(addr, port);
- else
- n = uty_serv_sock(addr, port);
-
- if (n == 0)
- uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
- }
+ uty_term_open_listeners(addr, port) ;
- /* If want to listen for vtysh, set up listener now */
+ /* If want to listen for vtysh, set up listener now */
if (VTYSH_ENABLED && (path != NULL))
uty_serv_vtysh(path) ;
} ;
/*------------------------------------------------------------------------------
- * Close VTY listener
+ * Create listener and set it ready to accept.
*
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ * Adds to list of listeners for close down.
*/
extern void
-uty_close_listeners(void)
+uty_add_listener(int fd, vio_fd_accept* accept_action)
{
- vty_listener listener ;
+ vio_listener vl ;
VTY_ASSERT_LOCKED() ;
- while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
- {
- uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
- XFREE(MTYPE_VTY, listener) ;
- } ;
-} ;
+ vl = vio_listener_new(fd, accept_action) ;
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- using getaddrinfo().
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
-{
-#if VTY_USE_ADDRINFO
-
-# ifndef HAVE_IPV6
-# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
-# endif
-
- int ret;
- int n ;
- struct addrinfo req;
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- char port_str[16];
-
- VTY_ASSERT_LOCKED() ;
-
- /* Want to listen, TCP-wise, on all available address families, on the
- * given port.
- */
- memset (&req, 0, sizeof (struct addrinfo));
- req.ai_flags = AI_PASSIVE;
- req.ai_family = AF_UNSPEC;
- req.ai_socktype = SOCK_STREAM;
- snprintf(port_str, sizeof(port_str), "%d", port);
-
- ret = getaddrinfo (hostname, port_str, &req, &ainfo);
-
- if (ret != 0)
- {
- fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
- exit (1);
- }
-
- /* Open up sockets on all AF_INET and AF_INET6 addresses */
- ainfo_save = ainfo;
-
- n = 0 ;
- do
- {
- if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
- continue;
-
- assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
-
- ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol, ainfo->ai_addr, port) ;
- if (ret >= 0)
- ++n ;
- }
- while ((ainfo = ainfo->ai_next) != NULL);
-
- freeaddrinfo (ainfo_save);
-
- return n ;
-
-#else
- zabort("uty_serv_sock_addrinfo not implemented") ;
-#endif /* VTY_USE_ADDRINFO */
-}
-
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock(const char* addr, unsigned short port)
-{
- int ret;
- int n ;
- union sockunion su_addr ;
- struct sockaddr* sa ;
-
- VTY_ASSERT_LOCKED() ;
-
- n = 0 ; /* nothing opened yet */
-
- /* If have an address, see what kind and whether valid */
- sa = NULL ;
-
- if (addr != NULL)
- {
- ret = str2sockunion (addr, &su_addr) ;
- if (ret == 0)
- sa = &su_addr.sa ;
- else
- uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
- } ;
-
- /* Try for AF_INET */
- ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-
-#if HAVE_IPV6
- /* Try for AF_INET6 */
- ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-#endif
-
- /* If not used the address... something wrong */
- if (sa != NULL)
- uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
-
- /* Done */
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Open a VTY_TERM listener socket.
- *
- * The sockaddr 'sa' may be NULL or of a different address family, in which
- * case "any" address is used.
- *
- * If the sockaddr 'sa' is used, only the address portion is used.
- *
- * Returns: < 0 => failed
- * == 0 => OK -- did not use the sockaddr 'sa'.
- * > 1 => OK -- and did use the sockaddr 'sa'
- */
-static int
-uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port)
-{
- union sockunion su ;
- int sock ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Is there an address and is it for this family ? */
- if ((sa != NULL) || (sa->sa_family == family))
- /* Set up sockunion containing required family and address */
- sockunion_new_sockaddr(&su, sa) ;
- else
- {
- /* no address or wrong family -- set up empty sockunion of
- * required family */
- sockunion_init_new(&su, family) ;
- sa = NULL ;
- } ;
-
- /* Open the socket and set its properties */
- sock = sockunion_socket(family, type, protocol) ;
- if (sock < 0)
- return -1 ;
-
- ret = sockopt_reuseaddr (sock);
-
- if (ret >= 0)
- ret = sockopt_reuseport (sock);
-
- if (ret >= 0)
- ret = set_nonblocking(sock);
-
-#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
- /* Want only IPV6 on ipv6 socket (not mapped addresses)
- *
- * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
- * attempt to bind to :: after binding to 0.0.0.0.
- */
- if ((ret >= 0) && (sa->sa_family == AF_INET6))
- {
- int on = 1;
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
- }
-#endif
-
- if (ret >= 0)
- ret = sockunion_bind (sock, &su, port, sa) ;
-
- if (ret >= 0)
- ret = sockunion_listen (sock, 3);
-
- if (ret < 0)
- {
- close (sock);
- return -1 ;
- }
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_TERM) ;
-
- /* Return OK and signal whether used address or not */
- return (sa != NULL) ? 1 : 0 ;
+ ssl_push(vty_listeners_list, vl, next) ;
} ;
/*------------------------------------------------------------------------------
- * Open a VTY_SHEL_SERV listener socket (UNIX domain).
- *
- * Returns: < 0 => failed
- * >= 0 => OK
- */
-static int
-uty_serv_vtysh(const char *path)
-{
- int ret;
- int sock, sa_len, path_len ;
- struct sockaddr_un sa_un ;
- mode_t old_mask;
- struct zprivs_ids_t ids;
-
- VTY_ASSERT_LOCKED() ;
-
- /* worry about the path length */
- path_len = strlen(path) + 1 ;
- if (path_len >= (int)sizeof(sa_un.sun_path))
- {
- uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
- return -1 ;
- } ;
-
- /* First of all, unlink existing socket */
- unlink (path);
-
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
- errtoa(errno, 0).str) ;
- return -1 ;
- }
-
- /* Bind to the required path */
- memset (&sa_un, 0, sizeof(sa_un));
- sa_un.sun_family = AF_UNIX;
- strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
-
- sa_len = SUN_LEN(&sa_un) ;
-
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- sa_un.sun_len = sa_len ;
-#endif
-
- old_mask = umask (0007);
-
- ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
-
- if (ret >= 0)
- ret = set_nonblocking(sock);
-
- if (ret >= 0)
- {
- ret = listen (sock, 5);
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
- errtoa(errno, 0).str) ;
- } ;
-
- zprivs_get_ids(&ids);
-
- if (ids.gid_vty > 0)
- {
- /* set group of socket */
- if ( chown (path, -1, ids.gid_vty) )
- uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
- errtoa(errno, 0).str) ;
- }
-
- umask (old_mask);
-
- /* Give up now if failed along the way */
- if (ret < 0)
- {
- close (sock) ;
- return -1 ;
- } ;
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
-
- return 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Socket is open -- set a VTY listener going
- *
- * Note that the vyt_listener structure is passed to the accept action function.
- */
-static void
-uty_serv_start_listener(int fd, enum vty_type type)
-{
- vty_listener listener ;
-
- listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
-
- ssl_push(vty_listeners_list, listener, next) ;
- uty_sock_init_new(&listener->sock, fd, listener) ;
-
- listener->type = type ;
-
- if (vty_cli_nexus)
- listener->sock.action.read.qnexus = vty_accept_qnexus ;
- else
- listener->sock.action.read.thread = vty_accept_thread ;
-
- uty_sock_set_read(&listener->sock, on) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the thread world -- create and dispatch VTY
- */
-static int
-vty_accept_thread(struct thread *thread)
-{
- vty_listener listener = THREAD_ARG(thread) ;
- int result ;
-
- VTY_LOCK() ;
-
- result = uty_accept(listener, THREAD_FD(thread));
-
- uty_sock_set_read(&listener->sock, on) ;
-
- VTY_UNLOCK() ;
- return result ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the qnexus world -- create and dispatch VTY
- */
-static void
-vty_accept_qnexus(qps_file qf, void* listener)
-{
- VTY_LOCK() ;
-
- uty_accept(listener, qf->fd);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
- */
-static int
-uty_accept(vty_listener listener, int listen_sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert(listener->sock.fd == listen_sock) ;
-
- switch (listener->type)
- {
- case VTY_TERM:
- return uty_accept_term(listener) ;
-
- case VTY_SHELL_SERV:
- return uty_accept_shell_serv(listener) ;
-
- default:
- zabort("unknown vty type") ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
- */
-static int
-uty_accept_term(vty_listener listener)
-{
- int sock_fd;
- union sockunion su;
- int ret;
- unsigned int on;
- struct prefix *p ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* We can handle IPv4 or IPv6 socket. */
- sockunion_init_new(&su, AF_UNSPEC) ;
-
- sock_fd = sockunion_accept (listener->sock.fd, &su);
-
- if (sock_fd < 0)
- {
- if (sock_fd == -1)
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* New socket is open... worry about access lists */
- p = sockunion2hostprefix (&su);
- ret = 0 ; /* so far, so good */
-
- if ((p->family == AF_INET) && vty_accesslist_name)
- {
- /* VTY's accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-
-#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
- {
- /* VTY's ipv6 accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-#endif /* HAVE_IPV6 */
-
- prefix_free (p);
-
- if (ret != 0)
- {
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
- close (sock_fd);
- return 0;
- } ;
-
- /* Final options (optional) */
- on = 1 ;
- ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
- (void*)&on, sizeof (on));
- if (ret < 0)
- uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
- sock_fd, errtoa(errno, 0).str) ;
-
- /* All set -- create the VTY_TERM */
- uty_new_term(sock_fd, &su);
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
- sock_fd) ;
-
- return 0;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_SHELL_SERV
- */
-static int
-uty_accept_shell_serv (vty_listener listener)
-{
- int sock_fd ;
- int ret ;
- int client_len ;
- struct sockaddr_un client ;
-
- VTY_ASSERT_LOCKED() ;
-
- client_len = sizeof(client);
- memset (&client, 0, client_len);
-
- sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
- (socklen_t *) &client_len) ;
-
- if (sock_fd < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* All set -- create the VTY_SHELL_SERV */
- if (VTYSH_DEBUG)
- printf ("VTY shell accept\n");
-
- uty_new_shell_serv(sock_fd) ;
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
- return 0;
-}
-
-/*==============================================================================
- * Reading from the VTY_SHELL_SERV type sock.
- *
- * The select/pselect call-back ends up in utysh_read_ready().
- */
-
-/*------------------------------------------------------------------------------
- * Ready to read -> kicking the "SHELL_SERV CLI"
- *
- * End up here when there is something ready to be read.
- *
- * Will also end up here if an error has occurred, the other end has closed,
- * this end has half closed, etc. This fact is used to kick the CLI even when
- * there is no data to be read.
- *
- * Note that nothing is actually read here -- reading is done in the CLI itself,
- * if required.
- *
- * The CLI decides whether to re-enable read, or enable write, or both.
- */
-static void
-utysh_read_ready(vty_io vio)
-{
- uty_sock_set_read(&vio->sock, off) ;
-
- /* TODO: need minimal "CLI" for VTY_SHELL_SERV
- * NB: when output from command is flushed out, must append the
- * following four bytes: '\0' '\0' '\0' <ret>
- * Where <ret> is the command return code.
- */
-} ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static void
-vtysh_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- utysh_read_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static int
-vtysh_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_read = NULL ; /* implicitly */
- utysh_read_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the command line buffer
- *
- * Lines coming in are terminated by '\0'.
- *
- * Assumes that the incoming command line is empty or otherwise incomplete.
- *
- * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
- * when get '\0' or empties the "buf".
- *
- * When empties "buf", reads a lump from the sock.
- *
- * Returns: 0 => command line is incomplete
- * 1 => have a complete command line
- * -1 => EOF (or not open, or failed)
- */
-extern int
-utysh_read (vty_io vio, qstring cl, qstring buf)
-{
- int get ;
- char* cp ;
- char* ep ;
- size_t have ;
-
- while (1)
- {
- /* process what there is in the buffer */
- if (buf->len > buf->cp)
- {
- cp = qs_cp_char(buf) ;
- ep = qs_ep_char(buf) ;
- have = ep - cp ;
-
- ep = memchr(cp, '\0', have) ;
- if (ep != NULL)
- have = ep - cp ; /* have upto, but excluding '\0' */
-
- if (have > 0) /* take what have */
- {
- qs_insert(cl, cp, have) ;
- cl->cp += have ;
- buf->cp += have ;
- } ;
-
- if (ep != NULL) /* if found '\0' */
- {
- qs_term(cl) ; /* '\0' terminate */
- ++buf->cp ; /* step past it */
- return 1 ; /* have a complete line <<<<<<<<<<<<< */
- }
- } ;
-
- /* buffer is empty -- try and get some more stuff */
- assert(buf->len == buf->cp) ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
-
- qs_need(buf, 500) ; /* need a reasonable lump */
- qs_clear(buf) ; /* set cp = len = 0 */
-
- get = read_nb(vio->sock.fd, buf->body, buf->size) ;
- if (get > 0)
- buf->len = get ;
- else if (get == 0)
- return 0 ; /* have an incomplete line <<<<<<<<<<<< */
- else
- {
- if (get == -1)
- uty_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
-
- return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
- } ;
- } ;
-} ;
-
-/*==============================================================================
- * Output to vty which are set to "monitor".
- *
- * This is VERY TRICKY.
- *
- * If not running qpthreaded, then the objective is to get the message away
- * immediately -- do not wish it to be delayed in any way by the thread
- * system.
- *
- * So proceed as follows:
- *
- * a. wipe command line -- which adds output to the CLI buffer
- *
- * b. write the CLI buffer to the sock and any outstanding line control.
- *
- * c. write the monitor output.
- *
- * If that does not complete, put the tail end to the CLI buffer.
- *
- * d. restore any command line -- which adds output to the CLI buffer
- *
- * e. write the CLI buffer to the sock
- *
- * If that all succeeds, nothing has changed as far as the VTY stuff is
- * concerned -- except that possibly some CLI output was sent before it got
- * round to it.
- *
- * Note that step (b) will deal with any output hanging around from an
- * earlier step (e). If cannot complete that, then does not add fuel to the
- * fire -- but the message will be discarded.
- *
- * If that fails, or does not complete, then can set write on, to signal that
- * there is some output in the CLI buffer that needs to be sent, or some
- * error to be dealt with.
- *
- * The output should be tidy.
- *
- * To cut down the clutter, step (d) is performed only if the command line
- * is not empty (or if in cli_more_wait). Once a the user has started to enter
- * a command, the prompt and the command will remain visible.
- *
- * When logging an I/O error for a vty that happens to be a monitor, the
- * monitor-ness has already been turned off. The monitor output code does not
- * attempt to log any errors, sets write on so that the error will be picked
- * up that way.
- *
- * However, in the event of an assertion failure, it is possible that an
- * assertion will fail inside the monitor output. The monitor_busy flag
- * prevents disaster. It is also left set if I/O fails in monitor output, so
- * will not try to use the monitor again.
- *
- * Note that an assertion which is false for all vty monitors will recurse
- * through all the monitors, setting each one busy, in turn !
- *
-
-
- * TODO: sort out write on in the qpthreads world ??
- *
- * The problem is that the qpselect structure is designed to be accessed ONLY
- * within the thread to which it belongs. This makes it impossible for the
- * monitor output to set/clear read/write on the vty sock... so some way
- * around this is required.
- */
-
-/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
+ * Close all VTY listeners
*/
extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
+uty_close_listeners(void)
{
- vty_io vio ;
+ vio_listener listener ;
VTY_ASSERT_LOCKED() ;
- vio = sdl_head(vio_monitors_base) ;
-
- if (vio == NULL)
- return ; /* go no further if no "monitor" vtys */
-
- /* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
-
- /* write to all known "monitor" vty
- *
- */
- while (vio != NULL)
- {
- if (!vio->monitor_busy)
- {
- int ret ;
-
- vio->monitor_busy = 1 ; /* close the door */
-
- uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
-
- ret = uty_write_monitor(vio) ;
- if (ret == 0)
- {
- ret = write_nb(vio->sock.fd, ll->line, ll->len) ;
-
- if (ret >= 0)
- {
- ret = uty_cli_post_monitor(vio, ll->line + ret,
- ll->len - ret) ;
- if (ret > 0)
- ret = uty_write_monitor(vio) ;
- } ;
- } ;
-
- if (ret != 0)
- /* need to prod */ ;
-
- if (ret >= 0)
- vio->monitor_busy = 0 ;
- } ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Async-signal-safe version of vty_log for fixed strings.
- *
- * This is last gasp operation.
- */
-void
-vty_log_fixed (const char *buf, size_t len)
-{
- vty_io vio ;
-
- /* Write to all known "monitor" vty
- *
- * Forget all the niceties -- about to die in any case.
- */
- vio = sdl_head(vio_monitors_base) ;
- while (vio != NULL)
+ while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
{
- write(vio->sock.fd, buf, len) ;
- write(vio->sock.fd, "\r\n", 2) ;
-
- vio = sdl_next(vio, mon_list) ;
+ vio_listener_close(listener) ; /* no ceremony, no flowers */
} ;
} ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
index 19689853..ecd7a451 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -25,9 +25,12 @@
#ifndef _ZEBRA_VTY_IO_H
#define _ZEBRA_VTY_IO_H
-#include <stdbool.h>
+#include "zebra.h"
+#include "misc.h"
+
#include <errno.h>
+#include "vty_io_basic.h"
#include "uty.h"
#include "vty.h"
#include "vio_fifo.h"
@@ -62,113 +65,158 @@
*
*/
-/*------------------------------------------------------------------------------
- * VTY sock structure
- *
- * Used for VTY_TERM and VTY_SHELL_SERV VTY types, which are attached to TCP
- * and UNIX sockets, respectively.
- *
- * Also used for the associated listeners.
+/*==============================================================================
+ * VTY CLI and OUT types
*/
+enum vio_in_type /* Command input */
+{
+ VIN_NONE = 0, /* no input at all */
-typedef int thread_action(struct thread *) ;
+ VIN_TERM, /* telnet terminal */
+ VIN_SHELL, /* vty_shell input */
-union sock_action
-{
- qps_action* qnexus ;
- thread_action* thread ;
- void* anon ;
-} ;
+ VIN_FILE, /* ordinary file input */
+ VIN_PIPE, /* pipe (from child process) */
-union timer_action
-{
- qtimer_action* qnexus ;
- thread_action* thread ;
- void* anon ;
+ VIN_CONFIG, /* config file ?? */
} ;
+typedef enum vio_in_type vio_in_type_t ;
-struct vio_sock_actions
+enum vio_out_type /* Command output */
{
- union sock_action read ;
- union sock_action write ;
- union timer_action timer ;
+ VOUT_NONE = 0, /* no output at all */
+
+ VOUT_TERM, /* a telnet terminal */
+ VOUT_SHELL, /* a vty_shell output pipe */
+
+ VOUT_FILE, /* ordinary file */
+ VOUT_PIPE, /* pipe (to child process) */
+
+ VOUT_STDOUT, /* stdout */
+ VOUT_STDERR, /* stderr */
};
+typedef enum vio_out_type vio_out_type_t ;
-typedef struct vio_sock* vio_sock ;
-struct vio_sock
+/*------------------------------------------------------------------------------
+ * VIO file structure
+ *
+ * All I/O is non-blocking for all sources and sinks of VIO stuff.
+ *
+ * Also used for the associated listeners.
+ */
+typedef struct vio_vf* vio_vf ;
+
+struct vio_vf
{
- int fd ;
+ vty_io vio ; /* parent */
- void* info ; /* for action routines */
+ vio_in_type_t vin_type ;
+ vio_vf vin_next ; /* list of inputs */
- struct vio_sock_actions action ;
+ vio_out_type_t vout_type ;
+ vio_vf vout_next ; /* list of outputs */
- bool read_open ; /* read returns 0 if not open */
- bool write_open ; /* write completes instantly if not open */
- int error_seen ; /* non-zero => failed */
+ vio_fifo obuf ; /* pointer to fifo */
+ vio_line_control olc ; /* pointer to lc */
- qps_file qf ; /* when running qnexus */
+ vio_fd vfd ;
- struct thread *t_read; /* when running threads */
- struct thread *t_write;
+ bool blocking ; /* using blocking reads */
+ bool closing ; /* suppress read/write ready */
- unsigned long v_timeout; /* time-out in seconds -- 0 => none */
- bool timer_running ; /* true when timer is running */
+ bool read_open ; /* reads returns 0 if not */
+ bool write_open ; /* writes complete instantly if not */
+ int error_seen ; /* non-zero => failed */
- qtimer qtr; /* when running qnexus */
- struct thread *t_timer; /* when running threads */
+ on_off_b read_on ;
+ on_off_b write_on ;
-} ;
-
-enum
-{
- on = true,
- off = false
+ vty_timer_time read_timeout ;
+ vty_timer_time write_timeout ;
} ;
enum vty_readiness /* bit significant */
{
- not_ready = 0,
- read_ready = 1,
- write_ready = 2, /* takes precedence */
- now_ready = 4
+ not_ready = 0,
+ read_ready = 1,
+ write_ready = 2, /* takes precedence */
+ now_ready = 4
} ;
/*------------------------------------------------------------------------------
* The vty_io structure
+ *
+ *
+ *
+ *
+ *
*/
-struct vty_io {
+struct vty_io
+{
struct vty* vty ; /* the related vty */
char *name ; /* for VTY_TERM is IP address) */
+ /* vin stack */
+ vio_vf vin ;
+ vio_vf vin_base ;
+
+ /* vout stack */
+ vio_vf vout ;
+ vio_vf vout_base ;
+
/* List of all vty_io objects */
struct dl_list_pair(vty_io) vio_list ;
/* List of all vty_io that are in monitor state */
struct dl_list_pair(vty_io) mon_list ;
- /* VTY type and sock stuff */
- enum vty_type type;
+ /* VTY state */
- struct vio_sock sock ; /* for VTY_TERM and VTY_SHELL_SERV */
-
- bool half_closed ; /* => on death watch list */
+ bool half_closed ; /* => on death watch list until closed */
bool closed ; /* => all I/O terminated
will also be half_closed */
- const char* close_reason ; /* message to be sent, once all other
+ char* close_reason ; /* message to be sent, once all other
output has completed, giving reason
for closing the VTY. */
+
+
+
+
/* When writing configuration file */
enum vty_type real_type ;
int file_fd ;
int file_error ;
- /*--------------------------------------------------------------------*/
- /* Command line and related state */
+ /* Failure count for login attempts */
+ int fail;
+
+ /* History of commands */
+ vector_t hist ;
+ int hp ; /* History lookup current point */
+ int hindex; /* History insert end point */
+
+ /* Window width/height as reported by Telnet. 0 => unknown */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+ bool lines_set ; /* true <=> explicitly set */
+
+ /* Terminal monitor. */
+ bool monitor ;
+ bool monitor_busy ;
+
+ /* Terminal timeout in seconds -- 0 => none */
+ vty_timer_time v_timeout ;
+
+ /*-------------------------------------------------------------------------
+ * CLI_TERM stuff.
+ */
keystroke_stream key_stream ;
@@ -242,69 +290,111 @@ struct vty_io {
/* CLI output buffering */
vio_fifo_t cli_obuf ;
- /* Command output buffering */
- vio_fifo_t cmd_obuf ;
+} ;
- vio_line_control cmd_lc ;
+/*==============================================================================
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+enum {
+#if defined(HAVE_IPV6) && !defined(NRL)
+ VTY_USE_ADDRINFO = 1,
+#else
+ VTY_USE_ADDRINFO = 0,
+#endif
+} ;
- /* Failure count for login attempts */
- int fail;
+/*==============================================================================
+ * Functions
+ */
- /* History of commands */
- vector_t hist ;
- int hp ; /* History lookup current point */
- int hindex; /* History insert end point */
+extern vty uty_new (vty_type_t type, int sock_fd) ;
+extern void uty_close (vty_io vio, const char* reason) ;
+extern void uty_close_final(vty_io vio, const char* reason) ;
- /* Window width/height as reported by Telnet. 0 => unknown */
- int width;
- int height;
- /* Configure lines. */
- int lines;
- bool lines_set ; /* true <=> explicitly set */
+extern void uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_fd_action* read_action, vio_timer_action* read_timer_action) ;
+extern void uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_fd_action* write_action, vio_timer_action* write_timer_action) ;
- /* Terminal monitor. */
- bool monitor ;
- bool monitor_busy ;
- /* In configure mode. */
- bool config;
-} ;
-/*==============================================================================
- * Functions
- */
+extern vio_vf uty_vf_new(vty_io vio, int fd, vfd_type_t type,
+ vfd_io_type_t io_type) ;
+Inline int uty_vf_fd(vio_vf vf) ;
+extern on_off_t uty_vf_set_read(vio_vf vf, on_off_t on) ;
+extern on_off_t uty_vf_set_read_timeout(vio_vf vf,
+ vty_timer_time read_timeout) ;
+extern on_off_t uty_vf_set_write(vio_vf vf, on_off_t on) ;
+extern on_off_t uty_vf_set_write_timeout(vio_vf vf,
+ vty_timer_time write_timeout) ;
+
-extern struct vty* uty_new (enum vty_type type, int sock_fd) ;
extern void uty_open_listeners(const char *addr, unsigned short port,
const char *path) ;
+extern void uty_add_listener(int fd, vio_fd_accept* accept) ;
extern void uty_close_listeners(void) ;
+extern void uty_watch_dog_init(void) ;
extern void uty_watch_dog_start(void) ;
extern void uty_watch_dog_stop(void) ;
-extern void uty_half_close (vty_io vio, const char* reason) ;
-extern void uty_close (vty_io vio) ;
-extern int uty_out (struct vty *vty, const char *format, ...)
+
+extern int uty_output (struct vty *vty, const char *format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
-extern int uty_vout(struct vty *vty, const char *format, va_list args) ;
+extern int uty_vprintf(struct vty *vty, const char *format, va_list args) ;
+extern int uty_reflect(struct vty *vty) ;
extern void uty_out_clear(vty_io vio) ;
extern void uty_out_fflush(vty_io vio, FILE* file) ;
extern void uty_set_height(vty_io vio) ;
extern void uty_cmd_output_start(vty_io vio) ;
-extern void uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready) ;
-extern void uty_sock_set_timer(vio_sock sock, unsigned long timeout) ;
+extern void uty_file_set_readiness(vio_vf vf, enum vty_readiness ready) ;
+extern void uty_file_set_timer(vio_vf vf, unsigned long timeout) ;
extern int uty_read (vty_io vio, keystroke steal) ;
extern int utysh_read (vty_io vio, qstring cl, qstring buf) ;
-
extern const char* uty_get_name(vty_io vio) ;
extern void uty_set_monitor(vty_io vio, bool on) ;
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Return the fd from a vio_fd structure
+ */
+Inline int
+uty_vf_fd(vio_vf vf)
+{
+ return vio_fd_fd(vf->vfd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return the fd from a vio_fd structure
+ */
+
+Inline bool
+uty_is_terminal(struct vty *vty)
+{
+ return vty->type == VTY_TERMINAL ;
+}
+
+Inline bool
+uty_is_shell_server(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_SERVER ;
+}
+
+Inline bool
+uty_is_shell_client(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_CLIENT ;
+}
+
#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/vty_io_basic.c b/lib/vty_io_basic.c
new file mode 100644
index 00000000..3bead51f
--- /dev/null
+++ b/lib/vty_io_basic.c
@@ -0,0 +1,1063 @@
+/* VTY IO Basic Functions -- bottom level of VTY IO hierarchy
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zebra.h"
+
+#include "vty_io_basic.h"
+
+/*==============================================================================
+ * Base level I/O and Timer handling....
+ *
+ * This is separated out so that the differences between running in a qpnexus
+ * and an old thread environment are encapsulated here.
+ */
+
+struct vio_io_set_args /* to CLI thread */
+{
+ bool active ; /* set when queued, cleared when dequeued */
+ bool die ; /* set when is queued and vio_fd is closed */
+ bool close ; /* close and free the vio_fd and mqb */
+
+ bool readable ; /* set when read state to be changed */
+ on_off_t read_on ; /* what to change read to */
+ vty_timer_time read_timeout ;
+ /* what to set the timeout to, if any */
+
+ bool writable ; /* set when write state to be changed */
+ on_off_t write_on ; /* what to change write to */
+ vty_timer_time write_timeout ;
+ /* what to set the timeout to, if any */
+} ;
+MQB_ARGS_SIZE_OK(vio_io_set_args) ;
+
+static void vio_fd_mqb_dispatch(vio_fd vfd) ;
+static void vio_fd_mqb_free(vio_fd vfd) ;
+static struct vio_io_set_args* vio_fd_mqb_args(vio_fd vfd) ;
+
+/*==============================================================================
+ * File Descriptor handling
+ *
+ * Provides read/write ready handling in consistent manner -- so don't care
+ * whether is qpnexus or old thread environment.
+ *
+ * NB: in all cases, when a read/write event goes off, the read/write readiness
+ * is cleared and any read/write timer is stopped.
+ *
+ * In the qpnexus world, there is a small complication... the qpselect stuff
+ * for all vty lives in the cli thread, so there is a mechanism here to allow
+ * for messages from other threads to implement the necessary qpselect things,
+ * see above.
+ */
+
+static void vio_fd_qps_read_action(qps_file qf, void* file_info) ;
+static void vio_fd_qps_write_action(qps_file qf, void* file_info) ;
+static int vio_fd_thread_read_action(struct thread *thread) ;
+static int vio_fd_thread_write_action(struct thread *thread) ;
+
+static void vio_timer_squelch(vio_timer_t* timer) ;
+
+Inline void
+vio_fd_do_read_action(vio_fd vfd)
+{
+ if (vfd->active)
+ vfd->read_action(vfd, vfd->action_info) ;
+}
+
+Inline void
+vio_fd_do_write_action(vio_fd vfd)
+{
+ if (vfd->active)
+ vfd->write_action(vfd, vfd->action_info) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new vfd structure.
+ */
+extern vio_fd
+vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
+{
+ vio_fd vfd ;
+
+ vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_fd)) ;
+
+ /* Has set:
+ *
+ * active -- false !
+ *
+ * read_action -- NULL
+ * write_action -- NULL
+ *
+ * f.qf -- NULL
+ * f.thread.read -- NULL
+ * f.thread.write -- NULL
+ *
+ * mqb -- NULL
+ */
+
+ vio_fd_set_fd(vfd, fd, type, io_type) ;
+
+ vio_timer_init(&vfd->read_timer, NULL, NULL) ;
+ vio_timer_init(&vfd->write_timer, NULL, NULL) ;
+
+ vio_fd_set_action_info(vfd, action_info) ;
+
+ return vfd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If vfd was not fully set up when created, set it up now.
+ *
+ * To close an active vfd, use vio_fd_close() !
+ *
+ * NB: for use when vfd has been created, but the fd was not known at that
+ * time -- ie the vfd is NOT active.
+ */
+extern void
+vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
+{
+ assert(!vfd->active) ;
+
+ vfd->fd = fd ;
+ vfd->active = (fd >= 0) ;
+ vfd->type = type ;
+ vfd->io_type = io_type ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the read action field for the given vio_fd.
+ */
+extern void
+vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action)
+{
+ vfd->read_action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the write action field for the given vio_fd.
+ */
+extern void
+vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action)
+{
+ vfd->write_action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the read action field for the given vio_fd.
+ */
+extern void
+vio_fd_set_read_timeout_action(vio_fd vfd, vio_timer_action* action)
+{
+ vio_timer_set_action(&vfd->read_timer, action) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the write action field for the given vio_fd.
+ */
+extern void
+vio_fd_set_write_timeout_action(vio_fd vfd, vio_timer_action* action)
+{
+ vio_timer_set_action(&vfd->write_timer, action) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the action_info field for the given vio_fd read/write action.
+ */
+extern void
+vio_fd_set_action_info(vio_fd vfd, void* action_info)
+{
+ vfd->action_info = action_info ;
+ vio_timer_set_info(&vfd->read_timer, action_info) ;
+ vio_timer_set_info(&vfd->write_timer, action_info) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is a read action set for the give vio_fd (if any), then kick it.
+ */
+extern void
+vio_fd_do_read_action(vio_fd vfd)
+{
+ if ((vfd != NULL) && (vfd->read_action != NULL))
+ vio_fd_do_read_action(vfd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is a write action set for the give vio_fd (if any), then kick it.
+ */
+extern void
+vio_fd_do_write_action(vio_fd vfd)
+{
+ if ((vfd != NULL) && (vfd->write_action != NULL))
+ vio_fd_do_read_action(vfd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Half close the given vfd (if any).
+ *
+ * If the vfd is a socket, then does a shutdown of the read side.
+ *
+ * If the vfd is not a socket and is read (only) closes the vfd.
+ *
+ * In any case, turns off any read ready and read ready timeout.
+ *
+ * Returns original vfd, or NULL if it has been closed.
+ */
+extern vio_fd
+vio_fd_half_close(vio_fd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd == NULL)
+ return NULL ;
+
+ if (vfd->fd >= 0)
+ {
+ assert(vfd->active) ;
+
+ if (vfd->io_type & vfd_io_read)
+ {
+ if (vfd->io_type & vfd_io_write)
+ {
+ /* read & write, so really half-close if can */
+ if (vfd->type == vfd_socket)
+ shutdown(vfd->fd, SHUT_RD) ;
+ vio_fd_set_read(vfd, off, 0) ;
+ vfd->io_type ^= vfd_io_read ; /* now write only ! */
+ }
+ else
+ {
+ /* read only, so fully close */
+ vfd = vio_fd_close(vfd) ;
+ } ;
+ } ;
+ }
+ else
+ assert(!vfd->active) ;
+
+ return vfd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is an fd, close it.
+ *
+ * Stops any read/write waiting and releases all memory.
+ *
+ * NB: this can done from any thread, but if not done from the CLI thread,
+ * and there is a qf, a message must be sent to the CLI thread to actually
+ * implement: which passes the vio_fd to the CLI thread for later
+ * close and destruction.
+ *
+ * The actual close has to be delayed, so that cannot open another fd
+ * and bang into a still active qps_file !
+ *
+ * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ */
+static void
+vio_fd_do_close(vio_fd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (vty_cli_nexus)
+ {
+ if (vfd->f.qf != NULL)
+ {
+ assert(vfd->fd == qps_file_fd(vfd->f.qf)) ;
+ vfd->f.qf = qps_file_free(vfd->f.qf) ;
+ } ;
+ vio_fd_mqb_free(vfd) ;
+ }
+ else
+ {
+ if (vfd->f.thread.read != NULL)
+ {
+ assert(vfd->fd == THREAD_FD(vfd->f.thread.read)) ;
+ thread_cancel(vfd->f.thread.read) ;
+ vfd->f.thread.read = NULL ;
+ } ;
+
+ if (vfd->f.thread.write != NULL)
+ {
+ assert(vfd->fd == THREAD_FD(vfd->f.thread.write)) ;
+ thread_cancel(vfd->f.thread.write) ;
+ vfd->f.thread.write = NULL ;
+ } ;
+
+ assert(vfd->mqb == NULL) ;
+ } ;
+
+ if (vfd->fd >= 0)
+ close(vfd->fd) ;
+
+ vio_timer_reset(&vfd->read_timer) ;
+ vio_timer_reset(&vfd->write_timer) ;
+
+ XFREE(MTYPE_VTY, vfd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the given vfd (if any).
+ *
+ * If there is an fd, close it. Stops any read/write waiting and releases all
+ * memory.
+ *
+ * NB: this can done from any thread, but if not done from the CLI thread,
+ * and there is a qf, a message must be sent to the CLI thread to actually
+ * implement: which passes the vio_fd to the CLI thread for later
+ * close and destruction.
+ *
+ * The actual close has to be delayed, so that cannot open another fd
+ * and bang into a still active qps_file !
+ *
+ * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ */
+extern vio_fd
+vio_fd_close(vio_fd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd == NULL)
+ return NULL ;
+
+ if (vfd->fd < 0)
+ {
+ /* closing an inactive vio_fd -- make sure all is quiet */
+ assert(!vfd->active) ;
+ if (vty_cli_nexus)
+ {
+ assert(vfd->f.qf == NULL) ;
+ }
+ else
+ {
+ assert(vfd->f.thread.read == NULL) ;
+ assert(vfd->f.thread.write == NULL) ;
+ } ;
+ assert(vfd->mqb == NULL) ;
+ }
+ else
+ {
+ /* closing an active vio_fd */
+ if (vty_is_cli_thread())
+ {
+ /* In cli thread, so close directly */
+ vio_fd_do_close(vfd) ;
+ }
+ else
+ {
+ /* Rats... have to send message to cli thread to close */
+ struct vio_io_set_args* args = vio_fd_mqb_args(vfd) ;
+
+ args->close = true ;
+ args->die = true ;
+
+ /* in case something goes ready before the close message
+ * is processed, squelch.
+ */
+ vfd->active = false ;
+ vfd->read_action = NULL ;
+ vfd->write_action = NULL ;
+ vfd->action_info = NULL ;
+
+ vio_timer_squelch(&vfd->read_timer) ;
+ vio_timer_squelch(&vfd->write_timer) ;
+
+ assert(vfd == mqb_get_arg0(vfd->mqb)) ;
+ vio_fd_mqb_dispatch(vfd) ;
+ } ;
+ } ;
+
+ return NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set or unset read ready state on given vio_fd (if any) if it is active.
+ *
+ * If setting read_on, starts any read timeout timer.
+ * If setting read off, stops any read timeout timer.
+ *
+ * NB: this can done from any thread, but if not done from the CLI thread,
+ * a message must be sent to the CLI thread to actually implement.
+ */
+extern on_off_t
+vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+{
+ struct vio_io_set_args* args ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if ((vfd == NULL) || (!vfd->active))
+ return off ;
+
+ if (vty_is_cli_thread())
+ {
+ /* In the cli thread (effectively) so do things directly. */
+
+ if (vfd->mqb != NULL)
+ {
+ /* discard/override any pending message setting */
+ args = mqb_get_args(vfd->mqb) ;
+ args->readable = false ;
+ } ;
+
+ if (on)
+ {
+ assert(vfd->read_action != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ if (vfd->f.qf == NULL)
+ {
+ vfd->f.qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, vfd->f.qf,
+ vfd->fd, vfd) ;
+ } ;
+ qps_enable_mode(vfd->f.qf, qps_read_mnum,
+ vio_fd_qps_read_action) ;
+ }
+ else
+ {
+ if (vfd->f.thread.read == NULL)
+ vfd->f.thread.read = thread_add_read(vty_master,
+ vio_fd_thread_read_action, vfd, vfd->fd) ;
+ } ;
+
+ vio_timer_set(&vfd->read_timer, timeout) ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ {
+ if (vfd->f.qf != NULL)
+ qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
+ }
+ else
+ {
+ if (vfd->f.thread.read != NULL)
+ thread_cancel (vfd->f.thread.read) ;
+ } ;
+
+ vio_timer_unset(&vfd->read_timer) ;
+ } ;
+ }
+ else
+ {
+ /* In other threads, must send message to cli thread */
+
+ args = vio_fd_mqb_args(vfd) ;
+ args->readable = true ;
+ args->read_on = on ;
+ args->read_timeout = timeout ;
+ vio_timer_squelch(&vfd->read_timer) ;
+ vio_fd_mqb_dispatch(vfd) ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set or unset write ready state on given vio_fd (if any) if it is active.
+ *
+ * If setting write_on, starts any write timeout timer.
+ * If setting write off, stops any write timeout timer.
+ *
+ * NB: this can done from any thread, but if not done from the CLI thread,
+ * a message must be sent to the CLI thread to actually implement.
+ */
+extern on_off_t
+vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+{
+ struct vio_io_set_args* args ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if ((vfd == NULL) || (!vfd->active))
+ return off ;
+
+ if (vty_is_cli_thread())
+ {
+ /* In the cli thread (effectively) so do things directly. */
+
+ if (vfd->mqb != NULL)
+ {
+ /* discard/override any pending message setting */
+ args = mqb_get_args(vfd->mqb) ;
+ args->writable = false ;
+ } ;
+
+ if (on)
+ {
+ assert(vfd->write_action != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ if (vfd->f.qf == NULL)
+ {
+ vfd->f.qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, vfd->f.qf,
+ vfd->fd, vfd) ;
+ } ;
+ qps_enable_mode(vfd->f.qf, qps_write_mnum,
+ vio_fd_qps_write_action) ;
+ }
+ else
+ {
+ if (vfd->f.thread.write == NULL)
+ vfd->f.thread.write = thread_add_write(vty_master,
+ vio_fd_thread_write_action, vfd, vfd->fd) ;
+ } ;
+
+ vio_timer_set(&vfd->write_timer, timeout) ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ {
+ if (vfd->f.qf != NULL)
+ qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
+ }
+ else
+ {
+ if (vfd->f.thread.write != NULL)
+ thread_cancel (vfd->f.thread.write) ;
+ } ;
+
+ vio_timer_unset(&vfd->write_timer) ;
+ } ;
+ }
+ else
+ {
+ /* In other threads, must send message to cli thread */
+
+ args = vio_fd_mqb_args(vfd) ;
+ args->writable = true ;
+ args->write_on = on ;
+ args->write_timeout = timeout ;
+ vio_timer_squelch(&vfd->write_timer) ;
+ vio_fd_mqb_dispatch(vfd) ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read
+ *
+ * Clears read ready state and unsets any read timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static void
+vio_fd_qps_read_action(qps_file qf, void* file_info)
+{
+ vio_fd vfd = file_info ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
+
+ qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
+ vio_timer_unset(&vfd->read_timer) ;
+
+ vio_fd_do_read_action(vfd) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to read
+ *
+ * Clears read ready state and unsets any read timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static int
+vio_fd_thread_read_action(struct thread *thread)
+{
+ vio_fd vfd = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert(vfd->fd == THREAD_FD(thread)) ;
+
+ vfd->f.thread.read = NULL ; /* implicitly */
+ vio_timer_unset(&vfd->read_timer) ;
+
+ vio_fd_do_read_action(vfd) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to write
+ *
+ * Clears write ready state and unsets any write timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static void
+vio_fd_qps_write_action(qps_file qf, void* file_info)
+{
+ vio_fd vfd = file_info ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
+
+ qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
+ vio_timer_unset(&vfd->write_timer) ;
+
+ vio_fd_do_write_action(vfd) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to write
+ *
+ * Clears write ready state and unsets any write timer.
+ *
+ * NB: if !vfd->active, then has been closed in another thread, but close
+ * message is yet to be procesed.
+ */
+static int
+vio_fd_thread_write_action(struct thread *thread)
+{
+ vio_fd vfd = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert(vfd->fd == THREAD_FD(thread)) ;
+ vio_timer_unset(&vfd->write_timer) ;
+
+ vfd->f.thread.write = NULL ; /* implicitly */
+
+ vio_fd_do_write_action(vfd) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Message handling, so that other threads can signal for output to be
+ * dispatched !
+ *
+ * There is one message block per vfd. It is only every touched under the
+ * vty_mutex.
+ *
+ * Once it is dispatched it is marked 'active'. Can still be changed, but no
+ * further dispatch is required. When it has been dequeued and processed,
+ * it is marked inactive.
+ *
+ * If the vfd is closed while the message is active, it is marked to die,
+ * which it will do when it is dequeued and actioned.
+ */
+
+static void vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag) ;
+
+/*------------------------------------------------------------------------------
+ * Get mqb for the given vfd -- make one if required.
+ */
+static struct vio_io_set_args*
+vio_fd_mqb_args(vio_fd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd->mqb == NULL)
+ vfd->mqb = mqb_init_new(NULL, vio_fd_set_action, vfd) ;
+
+ return mqb_get_args(vfd->mqb) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free mqb for the given vfd -- if any.
+ */
+static void
+vio_fd_mqb_free(vio_fd vfd)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vfd->mqb != NULL)
+ {
+ struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
+
+ if (args->active)
+ {
+ args->die = true ;
+ mqb_set_arg0(vfd->mqb, NULL) ;
+ }
+ else
+ {
+ mqb_free(vfd->mqb) ;
+ } ;
+
+ vfd->mqb = NULL ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch mqb, if not already active
+ */
+static void
+vio_fd_mqb_dispatch(vio_fd vfd)
+{
+ struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (!args->active)
+ {
+ args->active = true ;
+ mqueue_enqueue(vty_cli_nexus->queue, vfd->mqb, mqb_ordinary) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Action routine for the read/write on/off setting message.
+ *
+ * If the mqb is marked to die, then it and any qps_file it points to have been
+ * cut loose, and now is the time to close the fd and release the qps_file,
+ * along with releasing the mqb.
+ */
+static void
+vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct vio_io_set_args* args = mqb_get_args(mqb) ;
+ vio_fd vfd = mqb_get_arg0(mqb) ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ args->active = false ;
+
+ if ((flag != mqb_destroy) && (!args->die))
+ {
+ if (args->readable)
+ vio_fd_set_read(vfd, args->read_on, args->read_timeout) ;
+ if (args->writable)
+ vio_fd_set_write(vfd, args->write_on, args->write_timeout) ;
+ }
+ else
+ {
+ if (args->close)
+ vio_fd_do_close(vfd) ;
+ mqb_free(mqb) ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*==============================================================================
+ * Listener Handling
+ *
+ *
+ */
+
+static void vio_accept(vio_fd vfd, void* info) ;
+
+/*------------------------------------------------------------------------------
+ * Create a new listener object for the newly opened listener socket.
+ *
+ * Sets the accept action that will be called, and passed the fd of the listener
+ * socket, when the listen socket goes 'read ready'.
+ *
+ * Returns address of newly created listener structure.
+ */
+extern vio_listener
+vio_listener_new(int fd, vio_fd_accept* accept_action)
+{
+ vio_listener listener ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ listener = XCALLOC(MTYPE_VTY, sizeof(struct vio_listener)) ;
+ /* sets the next pointer to NULL */
+
+ listener->vfd = vio_fd_new(fd, vfd_listener, vfd_io_read, listener) ;
+
+ listener->accept_action = accept_action ;
+
+ vio_fd_set_read_action(listener->vfd, vio_accept) ;
+ vio_fd_set_read(listener->vfd, on, 0) ;
+
+ return listener ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close listener and free listener structure.
+ * Stops any read waiting and releases all memory.
+ *
+ * NB: assumes that the structure has been removed from any list.
+ */
+extern void
+vio_listener_close(vio_listener listener)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vio_fd_close(listener->vfd) ;
+ XFREE(MTYPE_VTY, listener) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- this is the read_action from the listener vfd.
+ *
+ * info points at the listener object.
+ */
+static void
+vio_accept(vio_fd vfd, void* info)
+{
+ vio_listener listener ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ listener = info ;
+ assert(vfd == listener->vfd) ;
+
+ listener->accept_action(vfd->fd) ;
+} ;
+
+/*==============================================================================
+ * Timer Handling
+ *
+ * Provides timer primitives that work either in qnexus environment or in
+ * a thread environment.
+ *
+ * The main difference is that thread environment timers are 'one-shot', set up
+ * for one timing run and then destroyed.
+ */
+
+static void vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when) ;
+static int vio_timer_thread_action(struct thread *thread) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise vio_timer structure. Assumes is all new.
+ *
+ * This assumes the vio_timer structure is embedded in another structure.
+ */
+extern void
+vio_timer_init(vio_timer_t* timer, vio_timer_action* action, void* action_info)
+{
+ memset(timer, 0, sizeof(vio_timer_t)) ;
+
+ /* active -- 0, false
+ * squelch -- 0, false
+ * t -- NULL, no qtr and no thread
+ */
+
+ timer->action = action ;
+ timer->action_info = action_info ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the action field for the given timer.
+ */
+extern void
+vio_timer_set_action(vio_timer_t* timer, vio_timer_action* action)
+{
+ timer->action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the info field for the given timer.
+ */
+extern void
+vio_timer_set_info(vio_timer_t* timer, void* action_info)
+{
+ timer->action_info = action_info ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill vio_timer -- used when closing .
+ */
+static void
+vio_timer_squelch(vio_timer_t* timer)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ timer->squelch = true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset vio_timer structure. Stops any timer and releases all memory.
+ *
+ * This assumes the vio_timer structure is embedded in another structure.
+ */
+extern void
+vio_timer_reset(vio_timer_t* timer)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (timer->t.anon != NULL)
+ {
+ if (vty_cli_nexus)
+ qtimer_free(timer->t.qtr) ;
+ else
+ thread_cancel(timer->t.thread) ;
+
+ timer->t.anon = NULL ;
+ } ;
+
+ timer->active = false ;
+ timer->squelch = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set vio_timer going, with the given time.
+ *
+ * If timer is running, set to new time.
+ *
+ * If the time == 0, stop any current timer, do not restart.
+ */
+extern void
+vio_timer_set(vio_timer_t* timer, vty_timer_time time)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (time == 0)
+ {
+ vio_timer_unset(timer) ;
+ return ;
+ } ;
+
+ assert(timer->action != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ if (timer->t.qtr == NULL) /* allocate qtr if required */
+ timer->t.qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ vio_timer_qtr_action, timer) ;
+ qtimer_set(timer->t.qtr, qt_add_monotonic(QTIME(time)), NULL) ;
+ }
+ else
+ {
+ if (timer->t.thread != NULL)
+ thread_cancel(timer->t.thread) ;
+ timer->t.thread = thread_add_timer(vty_master,
+ vio_timer_thread_action, timer, time) ;
+ } ;
+
+ timer->active = true ;
+ timer->squelch = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Stop vio_timer, if any.
+ */
+extern void
+vio_timer_unset(vio_timer_t* timer)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (timer->active)
+ {
+ if (vty_cli_nexus)
+ {
+ assert(timer->t.qtr != NULL) ;
+ qtimer_unset(timer->t.qtr) ;
+ }
+ else
+ {
+ assert(timer->t.thread != NULL) ;
+ thread_cancel(timer->t.thread) ;
+ timer->t.thread = NULL ;
+ } ;
+
+ timer->active = false ;
+ } ;
+
+ timer->squelch = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: deal with timer timeout.
+ */
+static void
+vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
+{
+ vio_timer_t* timer = timer_info ;
+ vty_timer_time time ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (!timer->squelch) /* do nothing if squelched */
+ {
+ time = timer->action(timer, timer->action_info) ;
+ if (time != 0)
+ vio_timer_set(timer, time) ;
+ else
+ timer->active = false ;
+ }
+ else
+ {
+ timer->squelch = false ;
+ timer->active = false ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: deal with timer timeout.
+ */
+static int
+vio_timer_thread_action(struct thread *thread)
+{
+ vio_timer_t* timer = THREAD_ARG (thread);
+ vty_timer_time time ;
+
+ VTY_LOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ timer->t.thread = NULL ; /* implicitly */
+
+ if (!timer->squelch) /* do nothing if squelched */
+ {
+ time = timer->action(timer, timer->action_info) ;
+ if (time != 0)
+ vio_timer_set(timer, time) ;
+ else
+ timer->active = false ;
+ }
+ else
+ {
+ timer->squelch = false ;
+ timer->active = false ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return 0;
+} ;
diff --git a/lib/vty_io_basic.h b/lib/vty_io_basic.h
new file mode 100644
index 00000000..a2b1cbb3
--- /dev/null
+++ b/lib/vty_io_basic.h
@@ -0,0 +1,186 @@
+/* VTY IO Structure and Functions -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VTY_IO_BASIC_H
+#define _ZEBRA_VTY_IO_BASIC_H
+
+#include "misc.h"
+
+#include "vty.h"
+
+#include "qpselect.h"
+#include "thread.h"
+#include "mqueue.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by all the
+ * VTY I/O hierarchy, providing the basic read/write ready functions and
+ * timer functions.
+ *
+ * This is separated out so that the differences between running in a qpnexus
+ * and an old thread environment are encapsulated here.
+ */
+
+enum vfd_type
+{
+ vfd_none = 0,
+ vfd_socket,
+ vfd_file,
+ vfd_pipe,
+ vfd_listener,
+} ;
+typedef enum vfd_type vfd_type_t ;
+
+enum vfd_io_type /* NB: *bit*significant* */
+{
+ vfd_io_none = 0,
+ vfd_io_read = 1,
+ vfd_io_write = 2,
+ vfd_io_read_write = 3,
+} ;
+typedef enum vfd_io_type vfd_io_type_t ;
+
+/*------------------------------------------------------------------------------
+ * Timers -- implemented as qtimer or thread timer, depending on environment.
+ */
+typedef struct vio_timer vio_timer_t ;
+typedef vty_timer_time vio_timer_action(vio_timer_t* timer, void* action_info) ;
+
+struct vio_timer
+{
+ vio_timer_action* action ; /* who do we call */
+ void* action_info ;
+
+ bool active ;
+ bool squelch ; /* used when message pending */
+
+ union {
+ qtimer qtr ; /* when running qnexus */
+ struct thread* thread ; /* when running threads */
+ void* anon ;
+ } t ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * File descriptors -- looks after ready to read and/or write.
+ *
+ * Implemented as qps_file or as read/write thread, depending on the
+ * environment.
+ */
+typedef struct vio_fd* vio_fd ;
+typedef void vio_fd_action(vio_fd vfd, void* action_info) ;
+
+struct vio_fd
+{
+ int fd ;
+ bool active ; /* used for close message */
+
+ vfd_type_t type ; /* used for half-close */
+ vfd_io_type_t io_type ; /* read, write, read/write */
+
+ vio_fd_action* read_action ;
+ vio_fd_action* write_action ;
+
+ vio_timer_t read_timer ;
+ vio_timer_t write_timer ;
+
+ void* action_info ; /* for all action and time-out */
+
+ union
+ {
+ qps_file qf ; /* when running qnexus */
+
+ struct /* when running threads */
+ {
+ struct thread* read ;
+ struct thread* write ;
+ } thread ;
+ } f ;
+
+ mqueue_block mqb ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Listeners
+ */
+
+typedef struct vio_listener* vio_listener ;
+
+typedef void vio_fd_accept(int fd) ;
+
+struct vio_listener
+{
+ vio_listener next ; /* ssl type list */
+ vio_fd vfd ;
+ vio_fd_accept* accept_action ;
+};
+
+/*==============================================================================
+ * Functions
+ */
+
+extern vio_fd vio_fd_new(int fd, vfd_type_t type,
+ vfd_io_type_t io_type, void* action_info) ;
+extern void vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type,
+ vfd_io_type_t io_type) ;
+extern void vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action) ;
+extern void vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action) ;
+extern void vio_fd_set_read_timeout_action(vio_fd vfd,
+ vio_timer_action* action) ;
+extern void vio_fd_set_write_timeout_action(vio_fd vfd,
+ vio_timer_action* action) ;
+extern void vio_fd_set_action_info(vio_fd vfd, void* action_info) ;
+extern vio_fd vio_fd_half_close(vio_fd vfd) ;
+extern vio_fd vio_fd_close(vio_fd vfd) ;
+extern on_off_t vio_fd_set_read(vio_fd vfd, on_off_t on,
+ vty_timer_time timeout) ;
+extern on_off_t vio_fd_set_write(vio_fd vfd, on_off_t on,
+ vty_timer_time timeout) ;
+Inline int vio_fd_fd(vio_fd vfd) ;
+
+extern vio_listener vio_listener_new(int fd, vio_fd_accept* accept) ;
+extern void vio_listener_close(vio_listener listener) ;
+
+extern void vio_timer_init(vio_timer_t* timer, vio_timer_action* action,
+ void* action_info) ;
+extern void vio_timer_set_action(vio_timer_t* timer, vio_timer_action* action) ;
+extern void vio_timer_set_info(vio_timer_t* timer, void* action_info) ;
+extern void vio_timer_reset(vio_timer_t* timer) ;
+extern void vio_timer_set(vio_timer_t* timer, vty_timer_time time) ;
+extern void vio_timer_unset(vio_timer_t* timer) ;
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Return the fd from a vio_fd structure
+ */
+Inline int
+vio_fd_fd(vio_fd vfd)
+{
+ return vfd->fd ;
+} ;
+
+#endif /* _ZEBRA_VTY_IO_BASIC_H */
diff --git a/lib/vty_io_file.c b/lib/vty_io_file.c
new file mode 100644
index 00000000..ed2c35e8
--- /dev/null
+++ b/lib/vty_io_file.c
@@ -0,0 +1,2608 @@
+/* VTY Log Functions
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zebra.h"
+
+#include "vty.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "qstring.h"
+#include "keystroke.h"
+
+#include "memory.h"
+
+#include "prefix.h"
+#include "filter.h"
+#include "privs.h"
+#include "sockunion.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <sys/un.h> /* for VTYSH */
+#include <sys/socket.h>
+
+#define VTYSH_DEBUG 0
+
+/*==============================================================================
+ * VTY Log Output -- provides a "vty" interface to logging.
+ *
+ * Once a VTY_LOG object is created, can be used as an (output only) vty.
+ *
+ * All output is sent to the logging system,
+ *
+ *
+ * During command processing the output sent here is held until the command
+ * completes.
+ */
+
+static int uty_config_write(vty_io vio, bool all) ;
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf fprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+uty_out (struct vty *vty, const char *format, ...)
+{
+ int result;
+ VTY_ASSERT_LOCKED() ;
+ va_list args;
+ va_start (args, format);
+ result = uty_vout(vty, format, args);
+ va_end (args);
+ return result;
+}
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf vfprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ *
+ * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
+ *
+ * * MAY NOT do any command output if !cmd_enabled
+ *
+ * * first, the life of a vty is not guaranteed unless cmd_in_progress,
+ * so should not attempt to use a vty anywhere other than command
+ * execution.
+ *
+ * * second, cmd_out_enabled is false most of the time, and is only
+ * set true when a command completes, and it is time to write away
+ * the results.
+ *
+ * * all output is placed in the vio->cmd_obuf. When the command completes,
+ * the contents of the cmd_obuf will be written away -- subject to line
+ * control.
+ *
+ * * output is discarded if the vty is no longer write_open
+ */
+extern int
+uty_vout(struct vty *vty, const char *format, va_list args)
+{
+ vty_io vio ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio = vty->vio ;
+
+ switch (vio->type)
+ {
+ case VTY_STDOUT:
+ case VTY_SHELL:
+ ret = vprintf (format, args) ;
+ break ;
+
+ case VTY_STDERR:
+ ret = vfprintf (stderr, format, args) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
+ ret = uty_config_write(vio, false) ;
+ break ;
+
+ case VTY_TERM:
+ case VTY_SHELL_SERV:
+ assert(vio->cmd_in_progress) ;
+
+ if (!vio->sock.write_open)
+ return 0 ; /* discard output if not open ! */
+
+ /* fall through.... */
+
+ case VTY_CONFIG_READ:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ break ;
+
+ default:
+ zabort("impossible VTY type") ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear the contents of the command output FIFO etc.
+ *
+ * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
+ * flags -- competent parties must deal with those
+ */
+extern void
+uty_out_clear(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vio_fifo_clear(&vio->cmd_obuf) ;
+
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush the contents of the command output FIFO to the given file.
+ *
+ * Takes no notice of any errors !
+ */
+extern void
+uty_out_fflush(vty_io vio, FILE* file)
+{
+ char* src ;
+ size_t have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ fflush(file) ;
+
+ while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ {
+ fwrite(src, 1, have, file) ;
+ vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ } ;
+
+ fflush(file) ;
+} ;
+
+/*==============================================================================
+ * Prototypes.
+ */
+static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
+static void uty_sock_half_close(vio_sock sock) ;
+static void uty_sock_close(vio_sock sock) ;
+
+static void vty_read_qnexus (qps_file qf, void* file_info) ;
+static void vty_write_qnexus (qps_file qf, void* file_info) ;
+static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
+
+static int vty_read_thread (struct thread *thread) ;
+static int vty_write_thread (struct thread *thread) ;
+static int vty_timer_thread (struct thread *thread) ;
+
+static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
+static int vtysh_read_thread (struct thread *thread) ;
+
+static enum vty_readiness uty_write(vty_io vio) ;
+
+/*==============================================================================
+ * Creation and destruction of VTY objects
+ */
+
+/*------------------------------------------------------------------------------
+ * Allocate new vty struct
+ *
+ * Allocates and initialises basic vty and vty_io structures, setting the
+ * given type.
+ *
+ * Note that where is not setting up a vty_sock, this *may* be called from
+ * any thread.
+ *
+ * NB: may not create a VTY_CONFIG_WRITE type vty directly
+ *
+ * see: vty_open_config_write() and vty_close_config_write()
+ *
+ * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
+ * (So MUST be in the CLI thread to set those up !)
+ *
+ * the sock_fd is ignored for everything else.
+ *
+ * Returns: new vty
+ */
+extern struct vty *
+uty_new(enum vty_type type, int sock_fd)
+{
+ struct vty *vty ;
+ struct vty_io* vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* If this is a VTY_TERM or a VTY_SHELL, place */
+ switch (type)
+ {
+ case VTY_TERM: /* Require fd -- Telnet session */
+ case VTY_SHELL_SERV: /* Require fd -- Unix socket */
+ assert(sock_fd >= 0) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
+ break ;
+
+ case VTY_CONFIG_READ:
+ case VTY_STDOUT:
+ case VTY_STDERR:
+ case VTY_SHELL:
+ sock_fd = -1 ; /* No fd -- output to stdout/stderr */
+ break ;
+
+ default:
+ zabort("unknown VTY type") ;
+ } ;
+
+ /* Basic allocation */
+ vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
+
+ vty->vio = vio ;
+ vio->vty = vty ;
+
+ /* Zeroising the vty_io structure has set:
+ *
+ * name = NULL -- no name, yet
+ *
+ * vio_list both pointers NULL
+ * mon_list both pointers NULL
+ *
+ * half_closed = 0 -- NOT half closed (important !)
+ * closed = 0 -- NOT closed (important !)
+ * close_reason = NULL -- no reason, yet
+ *
+ * real_type = 0 -- not material
+ * file_fd = 0 -- not material
+ * file_error = 0 -- not material
+ *
+ * key_stream = NULL -- no key stream (always empty, at EOF)
+ *
+ * cli_drawn = 0 -- not drawn
+ * cli_dirty = 0 -- not dirty
+ * cli_prompt_len = 0 )
+ * cli_extra_len = 0 ) not material
+ * cli_echo_suppress = 0 )
+ *
+ * cli_prompt_node = 0 -- not material
+ * cli_prompt_set = 0 -- so prompt needs to be constructed
+ *
+ * cli_blocked = 0 -- not blocked
+ * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_enabled = 0 -- command output is disabled
+ * cli_wait_more = 0 -- not waiting for response to "--more--"
+ *
+ * cli_more_enabled = 0 -- not enabled for "--more--"
+ *
+ * cmd_out_done = 0 -- not material
+ *
+ * cli_do = 0 == cli_do_nothing
+ *
+ * cmd_lc = NULL -- no line control
+ *
+ * fail = 0 -- no login failures yet
+ *
+ * hist = empty vector
+ * hp = 0 -- at the beginning
+ * hindex = 0 -- the beginning
+ *
+ * width = 0 -- unknown console width
+ * height = 0 -- unknown console height
+ *
+ * lines = 0 -- no limit
+ * lines_set = 0 -- no explicit setting
+ *
+ * monitor = 0 -- not a monitor
+ * monitor_busy = 0 -- not a busy monitor
+ *
+ * config = 0 -- not holder of "config" mode
+ */
+ confirm(cli_do_nothing == 0) ;
+ confirm(AUTH_NODE == 0) ; /* default node type */
+
+ vio->type = type ;
+
+ /* Zeroising the vty structure has set:
+ *
+ * node = 0 TODO: something better for node value ????
+ * buf = NULL -- no command line, yet
+ * parsed = NULL -- no parsed command, yet
+ * lineno = 0 -- nothing read, yet
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
+ */
+
+ /* Initialise the vio_sock, */
+ uty_sock_init_new(&vio->sock, sock_fd, vio) ;
+
+ /* Make sure all buffers etc. are initialised clean and empty.
+ *
+ * Note that no buffers are actually allocated at this stage.
+ */
+ qs_init_new(&vio->cli_prompt_for_node, 0) ;
+
+ qs_init_new(&vio->cl, 0) ;
+ qs_init_new(&vio->clx, 0) ;
+
+ vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
+
+ vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
+
+ /* Place on list of known vio/vty */
+ sdl_push(vio_list_base, vio, vio_list) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_TERM -- ie attached to a telnet session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_term(int sock_fd, union sockunion *su)
+{
+ struct vty *vty ;
+ vty_io vio ;
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (VTY_TERM, sock_fd) ;
+ vio = vty->vio ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+
+ /* Set the socket action functions */
+ if (vty_cli_nexus)
+ {
+ vio->sock.action.read.qnexus = vty_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = vty_timer_qnexus ;
+ }
+ else
+ {
+ vio->sock.action.read.thread = vty_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = vty_timer_thread ;
+ } ;
+
+ /* The text form of the address identifies the VTY */
+ vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
+
+ /* Set the initial node */
+ if (no_password_check)
+ {
+ if (restricted_mode)
+ vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+
+ /* Pick up current timeout setting */
+ vio->sock.v_timeout = vty_timeout_val;
+
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
+
+ /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
+ vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
+ uty_set_height(vio) ; /* set initial state */
+
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
+
+ /* Reject connection if password isn't set, and not "no password" */
+ if ((host.password == NULL) && (host.password_encrypt == NULL)
+ && ! no_password_check)
+ {
+ uty_half_close (vio, "Vty password is not set.");
+ vty = NULL;
+ }
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
+
+ if (! no_password_check)
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE);
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_shell_serv(int sock_fd)
+{
+ struct vty *vty ;
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
+ vio = vty->vio ;
+
+ /* Set the action functions */
+ if (vty_cli_nexus)
+ {
+ vio->sock.action.read.qnexus = vtysh_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = NULL ;
+ }
+ else
+ {
+ vio->sock.action.read.thread = vtysh_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = NULL ;
+ } ;
+
+ vty->node = VIEW_NODE;
+
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set/Clear "monitor" state:
+ *
+ * set: if VTY_TERM and not already "monitor" (and write_open !)
+ * clear: if is "monitor"
+ */
+extern void
+uty_set_monitor(vty_io vio, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (on && !vio->monitor)
+ {
+ if ((vio->type == VTY_TERM) && vio->sock.write_open)
+ {
+ vio->monitor = 1 ;
+ sdl_push(vio_monitors_base, vio, mon_list) ;
+ } ;
+ }
+ else if (!on && vio->monitor)
+ {
+ vio->monitor = 0 ;
+ sdl_del(vio_monitors_base, vio, mon_list) ;
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return "name" of VTY
+ *
+ * For VTY_TERM this is the IP address of the far end of the telnet connection.
+ */
+extern const char*
+uty_get_name(vty_io vio)
+{
+ return (vio->name != NULL) ? vio->name : "?" ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY for reading.
+ *
+ * For VTY_TERM (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input, setting it to "EOF"
+ * * turns off any monitor status !
+ * * drop down to RESTRICTED_NODE
+ *
+ * For VTY_SHELL_SERV (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input
+ * * drop down to RESTRICTED_NODE
+ *
+ * In all cases:
+ *
+ * * place on death watch
+ * * set the vty half_closed
+ * * sets the reason for closing (if any given)
+ *
+ * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
+ * the buffers, the VTY is closed.
+ *
+ * May already have set the vio->close_reason, or can set it now. (Passing a
+ * NULL reason has no effect on any existing posted reason.)
+ */
+extern void
+uty_half_close (vty_io vio, const char* reason)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return ;
+
+ if (reason != NULL)
+ vio->close_reason = reason ;
+
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_sock_half_close(&vio->sock) ;
+ uty_set_monitor(vio, 0) ;
+
+ /* Discard everything in the keystroke stream and force it to EOF */
+ if (vio->key_stream != NULL)
+ keystroke_stream_set_eof(vio->key_stream) ;
+
+ /* Turn off "--more--" so that all output clears without interruption.
+ *
+ * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
+ */
+ vio->cli_more_enabled = 0 ;
+
+ if (vio->cli_more_wait)
+ uty_cli_exit_more_wait(vio) ;
+
+ /* If a command is not in progress, enable output, which will clear
+ * the output buffer if there is anything there, plus any close reason,
+ * and then close.
+ *
+ * If command is in progress, then this process will start when it
+ * completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cmd_out_enabled = 1 ;
+
+ /* Make sure no longer holding the config symbol of power */
+ uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+
+ /* Log closing of VTY_TERM */
+ if (vio->type == VTY_TERM)
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
+
+ /* Move to the death watch list */
+ sdl_del(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
+
+ vio->half_closed = 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY.
+ *
+ * Shuts down everything and discards all buffers etc. etc.
+ *
+ * If cmd_in_progress, cannot complete the process -- but sets the closed
+ * flag.
+ *
+ * Can call vty_close() any number of times.
+ *
+ * The vty structure is placed on death watch, which will finally free the
+ * structure once no longer cmd_in_progress.
+ */
+extern void
+uty_close (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* Empty all the output buffers */
+ vio_fifo_reset_keep(&vio->cli_obuf) ;
+ vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
+
+ /* If not already closed, close. */
+ if (!vio->closed)
+ {
+ uty_half_close(vio, NULL) ; /* place on death watch -- if not
+ already done */
+ if (vio->type == VTY_TERM)
+ uty_cli_close(vio) ; /* tell the CLI to stop */
+
+ vio->closed = 1 ; /* now closed (stop uty_write()
+ from recursing) */
+
+ if (vio->sock.write_open)
+ uty_write(vio) ; /* last gasp attempt */
+
+ uty_sock_close(&vio->sock) ;
+
+ } ;
+
+ /* Nothing more should happen, so can now release almost everything,
+ * the exceptions being the things that are related to a cmd_in_progress.
+ *
+ * All writing to buffers is suppressed, and as the sock has been closed,
+ * there will be no more read_ready or write_ready events.
+ */
+ if (vio->name != NULL)
+ XFREE(MTYPE_VTY_NAME, vio->name) ;
+
+ vio->key_stream = keystroke_stream_free(vio->key_stream) ;
+
+ qs_reset(&vio->cli_prompt_for_node, keep_it) ;
+ qs_reset(&vio->cl, keep_it) ;
+
+ {
+ qstring line ;
+ while ((line = vector_ream(vio->hist, keep_it)) != NULL)
+ qs_reset_free(line) ;
+ } ;
+
+ /* The final stage cannot be completed if cmd_in_progress.
+ *
+ * The clx is pointed at by vty->buf -- containing the current command.
+ *
+ * Once everything is released, can take the vty off death watch, and
+ * release the vio and the vty.
+ */
+ if (!vio->cmd_in_progress)
+ {
+ qs_reset(&vio->clx, keep_it) ;
+ vio->vty->buf = NULL ;
+ } ;
+} ;
+
+/*==============================================================================
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set the given fd as the VTY_FILE output.
+ */
+extern void
+vty_open_config_write(struct vty* vty, int fd)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
+
+ vio->real_type = vio->type ;
+
+ vio->type = VTY_CONFIG_WRITE ;
+ vio->file_fd = fd ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away configuration file stuff -- all or just the full lump(s).
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see vio->file_error
+ */
+static int
+uty_config_write(vty_io vio, bool all)
+{
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file_error == 0)
+ {
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
+
+ if (ret < 0)
+ vio->file_error = errno ;
+ }
+ else
+ ret = -1 ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any pending stuff, and return the VTY to normal.
+ */
+extern int
+vty_close_config_write(struct vty* vty)
+{
+ vty_io vio ;
+ int err ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
+
+ uty_config_write(vio, true) ; /* write all that is left */
+
+ err = vio->file_error ;
+
+ vio->type = vio->real_type ;
+ vio->file_fd = -1 ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+
+ return err ;
+} ;
+
+/*==============================================================================
+ * vio_sock level operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new vio_sock structure.
+ *
+ * Requires that: the vio_sock structure is not currently in use.
+ *
+ * if fd >= 0 then: sock is open and ready read and write
+ * otherwise: sock is not open
+ *
+ * there are no errors, yet.
+ *
+ * Sets timeout to no timeout at all -- timeout is optional.
+ *
+ * NB: MUST be in the CLI thread if the fd is >= 0 !
+ */
+static void
+uty_sock_init_new(vio_sock sock, int fd, void* info)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (fd >= 0)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ memset(sock, 0, sizeof(struct vio_sock)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * action = all the actions set NULL
+ *
+ * error_seen = 0 -- no error, yet
+ *
+ * qf = NULL -- no qfile, yet
+ * t_read = NULL ) no threads, yet
+ * t_write = NULL )
+ *
+ * v_timeout = 0 -- no timeout set
+ * timer_runing = 0 -- not running, yet
+ * t_timer = NULL -- no timer thread, yet
+ * qtr = NULL -- no qtimer, yet
+ */
+ sock->fd = fd ;
+ sock->info = info ;
+
+ sock->read_open = (fd >= 0) ;
+ sock->write_open = (fd >= 0) ;
+
+ if ((fd >= 0) && vty_cli_nexus)
+ {
+ sock->qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restart the timer.
+ *
+ * If a timeout time is set, then start or restart the timer with that value.
+ *
+ * If no timeout time is set, and the timer is running, unset it.
+ */
+static void
+uty_sock_restart_timer(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->v_timeout != 0)
+ {
+ assert(sock->action.timer.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ if (sock->qtr == NULL) /* allocate qtr if required */
+ sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, sock->info) ;
+ qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
+ sock->action.timer.qnexus) ;
+ }
+ else
+ {
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer);
+ sock->t_timer = thread_add_timer (vty_master,
+ sock->action.timer.thread, sock->info, sock->v_timeout) ;
+ } ;
+
+ sock->timer_running = 1 ;
+ }
+ else if (sock->timer_running)
+ {
+ if (vty_cli_nexus)
+ {
+ if (sock->qtr != NULL)
+ qtimer_unset(sock->qtr) ;
+ }
+ else
+ {
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer) ;
+ } ;
+
+ sock->timer_running = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read on/off
+ *
+ * Returns: the on/off state set
+ */
+static bool
+uty_sock_set_read(vio_sock sock, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
+
+ if (on)
+ {
+ assert(sock->action.read.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
+ else
+ {
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_read) ;
+
+ sock->t_read = thread_add_read(vty_master,
+ sock->action.read.thread, sock->info, sock->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ qps_disable_modes(sock->qf, qps_read_mbit) ;
+ else
+ {
+ if (sock->t_read != NULL)
+ thread_cancel (sock->t_read) ;
+ } ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set write on/off
+ *
+ * Returns: the on/off state set
+ */
+static bool
+uty_sock_set_write(vio_sock sock, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
+
+ if (on)
+ {
+ assert(sock->action.write.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
+ else
+ {
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
+
+ sock->t_write = thread_add_write(vty_master,
+ sock->action.write.thread, sock->info, sock->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ qps_disable_modes(sock->qf, qps_write_mbit) ;
+ else
+ {
+ if (sock->t_write != NULL)
+ thread_cancel (sock->t_write) ;
+ } ;
+ } ;
+
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VTY_TERM
+ *
+ * Note that for VTY_TERM, set only one of read or write, and sets write for
+ * preference.
+ */
+extern void
+uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ uty_sock_set_read(sock, (ready == read_ready)) ;
+ uty_sock_set_write(sock, (ready >= write_ready)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a new timer value.
+ */
+extern void
+uty_sock_set_timer(vio_sock sock, unsigned long timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ sock->v_timeout = timeout ;
+ if (sock->timer_running)
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vty sock for reading.
+ *
+ * Sets timer to timeout for clearing any pending output.
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
+ */
+static void
+uty_sock_half_close(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ sock->read_open = 0 ; /* make sure */
+
+ if (sock->fd < 0)
+ return ; /* nothing more if no socket */
+
+ VTY_ASSERT_CLI_THREAD() ;
+
+ shutdown(sock->fd, SHUT_RD) ; /* actual half close */
+
+ uty_sock_set_read(sock, off) ;
+ uty_sock_set_write(sock, on) ;
+ sock->v_timeout = 30 ; /* for output to clear */
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vio_sock, completely -- shut down any timer.
+ *
+ * Structure is cleared of everything except the last error !
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
+ */
+static void
+uty_sock_close(vio_sock sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ sock->read_open = 0 ; /* make sure */
+ sock->write_open = 0 ;
+
+ if (sock->fd < 0)
+ {
+ assert( (sock->qf == NULL)
+ && (sock->qtr == NULL)
+ && (sock->t_read == NULL)
+ && (sock->t_write == NULL)
+ && (sock->t_timer == NULL) ) ;
+ return ; /* no more to be done here */
+ } ;
+
+ VTY_ASSERT_CLI_THREAD() ;
+ close(sock->fd) ;
+
+ if (vty_cli_nexus)
+ {
+ assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
+ qps_remove_file(sock->qf) ;
+ qps_file_free(sock->qf) ;
+ sock->qf = NULL ;
+ } ;
+
+ sock->fd = -1 ;
+
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
+
+ sock->t_read = NULL ;
+ sock->t_write = NULL ;
+
+ sock->info = NULL ;
+ sock->action.read.anon = NULL ;
+ sock->action.write.anon = NULL ;
+ sock->action.timer.anon = NULL ;
+
+ if (sock->qtr != NULL)
+ qtimer_free(sock->qtr) ;
+ if (sock->t_timer != NULL)
+ thread_cancel(sock->t_timer) ;
+
+ sock->v_timeout = 0 ;
+ sock->qtr = NULL ;
+ sock->t_timer = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dealing with an I/O error on VTY socket
+ *
+ * If this is the first error for this VTY, produce suitable log message.
+ *
+ * If is a "monitor", turn that off, *before* issuing log message.
+ */
+static int
+uty_sock_error(vty_io vio, const char* what)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* can no longer be a monitor ! *before* any logging ! */
+ uty_set_monitor(vio, 0) ;
+
+ /* if this is the first error, log it */
+ if (vio->sock.error_seen == 0)
+ {
+ const char* type ;
+ switch (vio->type)
+ {
+ case VTY_TERM:
+ type = "VTY Terminal" ;
+ break ;
+ case VTY_SHELL_SERV:
+ type = "VTY Shell Server" ;
+ break ;
+ default:
+ zabort("unknown VTY type for uty_sock_error()") ;
+ } ;
+
+ vio->sock.error_seen = errno ;
+ uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
+ type, what, vio->sock.fd, errtoa(vio->sock.error_seen, 0).str) ;
+ } ;
+
+ return -1 ;
+} ;
+
+/*==============================================================================
+ * Readiness and the VTY_TERM type VTY.
+ *
+ * For VTY_TERM the driving force is write ready. This is used to prompt the
+ * VTY_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
+ *
+ * The VTY_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
+ *
+ * So there is only one, common, uty_ready function, which:
+ *
+ * 1. attempts to clear any output it can.
+ *
+ * The state of the output affects the CLI, so must always do this before
+ * before invoking the CLI.
+ *
+ * If this write enters the "--more--" state, then will have tried to
+ * write away the prompt.
+ *
+ * 2. invokes the CLI
+ *
+ * Which will do either the standard CLI stuff or the special "--more--"
+ * stuff.
+ *
+ * 3. attempts to write any output there now is.
+ *
+ * If the CLI generated new output, as much as possible is written away
+ * now.
+ *
+ * If this write enters the "--more--" state, then it returns now_ready,
+ * if the prompt was written away, which loops back to the CLI.
+ *
+ * Note that this is arranging:
+ *
+ * a. to write away the "--more--" prompt as soon as the tranche of output to
+ * which it refers, completes
+ *
+ * b. to enter the cli_more_wait CLI for the first time immediately after the
+ * "--more--" prompt is written away.
+ *
+ * The loop limits itself to one trache of command output each time.
+ *
+ * Resets the timer because something happened.
+ */
+static void
+uty_ready(vty_io vio)
+{
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_out_done = 0 ; /* not done any command output yet */
+
+ uty_write(vio) ; /* try to clear outstanding stuff */
+ do
+ {
+ ready = uty_cli(vio) ; /* do any CLI work... */
+ ready |= uty_write(vio) ; /* ...and any output that generates */
+ } while (ready >= now_ready) ;
+
+ uty_sock_set_readiness(&vio->sock, ready) ;
+ uty_sock_restart_timer(&vio->sock) ;
+} ;
+
+/*==============================================================================
+ * Reading from VTY_TERM.
+ *
+ * The select/pselect call-back ends up in uty_read_ready().
+ *
+ * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
+ * current CLI.
+ */
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking CLI
+ */
+static void
+vty_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking CLI
+ */
+static int
+vty_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_read = NULL ; /* implicitly */
+ uty_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the keystroke stream
+ *
+ * Steal keystroke if required -- see keystroke_input()
+ *
+ * Returns: 0 => nothing available
+ * > 0 => read at least one byte
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+uty_read (vty_io vio, keystroke steal)
+{
+ unsigned char buf[500] ;
+ int get ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open */
+
+ get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ if (get >= 0)
+ keystroke_input(vio->key_stream, buf, get, steal) ;
+ else if (get < 0)
+ {
+ if (get == -1)
+ uty_sock_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+ keystroke_input(vio->key_stream, NULL, 0, steal) ;
+
+ get = -1 ;
+ } ;
+
+ return get ;
+} ;
+
+/*==============================================================================
+ * The write sock action for VTY_TERM type VTY
+ *
+ * There are two sets of buffering:
+ *
+ * cli -- command line -- which reflects the status of the command line
+ *
+ * cmd -- command output -- which is written to the file only while
+ * cmd_out_enabled.
+ *
+ * The cli output takes precedence.
+ *
+ * Output of command stuff is subject to line_control, and may go through the
+ * "--more--" mechanism.
+ */
+
+static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to write -> try to empty buffers
+ */
+static void
+vty_write_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info ;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to write -> try to empty buffers
+ */
+static int
+vty_write_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_write = NULL; /* implicitly */
+ uty_ready(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible of what there is.
+ *
+ * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
+ * empty.
+ *
+ * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
+ * and all output instantly successful.
+ *
+ * Sets write on if prevented from writing everything available for output
+ * by write() threatening to block.
+ *
+ * Returns: write_ready if should now set write on
+ * now_ready if should loop back and try again
+ * not_ready otherwise
+ */
+static enum vty_readiness
+uty_write(vty_io vio)
+{
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ ret = -1 ;
+ while (vio->sock.write_open)
+ {
+ /* Any outstanding line control output takes precedence */
+ if (vio->cmd_lc != NULL)
+ {
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ if (ret != 0)
+ break ;
+ }
+
+ /* Next: empty out the cli output */
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret != 0)
+ break ;
+
+ /* Finished now if not allowed to progress the command stuff */
+ if (!vio->cmd_out_enabled)
+ return not_ready ; /* done all can do */
+
+ /* Last: if there is something in the command buffer, do that */
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ {
+ if (vio->cmd_out_done)
+ break ; /* ...but not if done once */
+
+ vio->cmd_out_done = 1 ; /* done this once */
+
+ assert(!vio->cli_more_wait) ;
+
+ if (vio->cmd_lc != NULL)
+ ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ else
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->sock.fd, true) ;
+
+ /* If moved into "--more--" state@
+ *
+ * * the "--more--" prompt is ready to be written, so do that now
+ *
+ * * if that completes, then want to run the CLI *now* to perform the
+ * first stage of the "--more--" process.
+ */
+ if (vio->cli_more_wait)
+ {
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret == 0)
+ return now_ready ;
+ } ;
+
+ if (ret != 0)
+ break ;
+ }
+
+ /* Exciting stuff: there is nothing left to output...
+ *
+ * ... watch out for half closed state.
+ */
+ if (vio->half_closed)
+ {
+ if (vio->close_reason != NULL)
+ {
+ vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
+
+ struct vty* vty = vio->vty ;
+ if (vio->cli_drawn || vio->cli_dirty)
+ vty_out(vty, VTY_NEWLINE) ;
+ vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
+
+ vio->cmd_in_progress = 0 ;
+
+ vio->close_reason = NULL ; /* MUST discard now... */
+ continue ; /* ... and write away */
+ } ;
+
+ if (!vio->closed) /* avoid recursion */
+ uty_close(vio) ;
+
+ return not_ready ; /* it's all over */
+ } ;
+
+ /* For VTY_TERM: if the command line is not drawn, now is a good
+ * time to do that.
+ */
+ if (vio->type == VTY_TERM)
+ if (uty_cli_draw_if_required(vio))
+ continue ; /* do that now. */
+
+ /* There really is nothing left to output */
+ return not_ready ;
+ } ;
+
+ /* Arrives here if there is more to do, or failed (or was !write_open) */
+
+ if (ret >= 0)
+ return write_ready ;
+
+ /* If is write_open, then report the error
+ *
+ * If still read_open, let the reader pick up and report the error, when it
+ * has finished anything it has buffered.
+ */
+ if (vio->sock.write_open)
+ {
+ if (!vio->sock.read_open)
+ uty_sock_error(vio, "write") ;
+
+ vio->sock.write_open = 0 ; /* crash close write */
+ } ;
+
+ /* For whatever reason, is no longer write_open -- clear all buffers.
+ */
+ vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
+ uty_out_clear(vio) ; /* throw away cmd stuff */
+
+ vio->close_reason = NULL ; /* too late for this */
+
+ return not_ready ; /* NB: NOT blocked by I/O */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible -- for "monitor" output.
+ *
+ * Outputs only:
+ *
+ * a. outstanding line control stuff.
+ *
+ * b. contents of CLI buffer
+ *
+ * And:
+ *
+ * a. does not report any errors.
+ *
+ * b. does not change anything except the state of the buffers.
+ *
+ * In particular, for the qpthreaded world, does not attempt to change
+ * the state of the qfile or any other "thread private" structures.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed (or !write_open)
+ */
+static int
+uty_write_monitor(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vio->sock.write_open)
+ return -1 ;
+
+ if (vio->cmd_lc != NULL)
+ {
+ int ret ;
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+
+ if (ret != 0)
+ return ret ;
+ } ;
+
+ return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write the given FIFO to output -- subject to possible line control.
+ *
+ * Note that even if no "--more--" is set, will have set some height, so
+ * that does not attempt to empty the FIFO completely all in one go.
+ *
+ * If the line control becomes "paused", it is time to enter "--more--" state
+ * -- unless the FIFO is empty (or "--more--" is not enabled).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * Returns: > 0 => blocked or completed one tranche
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
+ char* src ;
+ size_t have ;
+
+ /* Collect another line_control height's worth of output.
+ *
+ * Expect the line control to be empty at this point, but it does not have
+ * to be.
+ */
+ vio_lc_set_pause(lc) ; /* clears lc->paused */
+
+ src = vio_fifo_get_rdr(vf, &have) ;
+
+ while ((src != NULL) && (!lc->paused))
+ {
+ size_t take ;
+ take = vio_lc_append(lc, src, have) ;
+ src = vio_fifo_step_rdr(vf, &have, take) ;
+ } ;
+
+ vio->cli_dirty = (lc->col != 0) ;
+
+ /* Write the contents of the line control */
+ ret = uty_write_lc(vio, vf, lc) ;
+
+ if (ret < 0)
+ return ret ; /* give up now if failed. */
+
+ if ((ret == 0) && vio_fifo_empty(vf))
+ return 0 ; /* FIFO and line control empty */
+
+ /* If should now do "--more--", now is the time to prepare for that.
+ *
+ * Entering more state issues a new prompt in the CLI buffer, which can
+ * be written once line control write completes.
+ *
+ * The "--more--" cli will not do anything until the CLI buffer has
+ * cleared.
+ */
+ if (lc->paused && vio->cli_more_enabled)
+ uty_cli_enter_more_wait(vio) ;
+
+ return 1 ; /* FIFO or line control, not empty */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of line control (if any).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * NB: does nothing other than write() and buffer management.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
+
+ ret = vio_lc_write_nb(vio->sock.fd, lc) ;
+
+ if (ret <= 0)
+ vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start command output -- clears down the line control.
+ *
+ * Requires that that current line is empty -- restarts the line control
+ * on the basis that is at column 0.
+ */
+extern void
+uty_cmd_output_start(vty_io vio)
+{
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the effective height for line control (if any)
+ *
+ * If using line_control, may enable the "--more--" output handling.
+ *
+ * If not, want some limit on the amount of stuff output at a time.
+ *
+ * Sets the line control window width and height.
+ * Sets cli_more_enabled if "--more--" is enabled.
+ */
+extern void
+uty_set_height(vty_io vio)
+{
+ bool on ;
+
+ on = 0 ; /* default state */
+
+ if ((vio->cmd_lc != NULL) && !vio->half_closed)
+ {
+ int height ;
+
+ height = 0 ; /* default state */
+
+ if ((vio->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (vio->lines >= 0)
+ height = vio->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = vio->height - 2 ;
+ if (height < 1)
+ height = 1 ;
+ } ;
+ }
+ else
+ {
+ /* If window size not known, use lines if that has been set
+ * explicitly for this terminal.
+ */
+ if (vio->lines_set)
+ height = vio->lines ;
+ } ;
+
+ if (height > 0)
+ on = 1 ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
+ } ;
+
+ vio->cli_more_enabled = on ;
+} ;
+
+/*==============================================================================
+ * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ */
+
+/*------------------------------------------------------------------------------
+ * Timer has expired.
+ *
+ * If half_closed, then this is curtains -- have waited long enough !
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static void
+uty_timer_expired (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return uty_close(vio) ; /* curtains */
+
+ uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
+ } ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: deal with timer timeout.
+ */
+static void
+vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
+{
+ vty_io vio = timer_info ;
+
+ VTY_LOCK() ;
+
+ uty_timer_expired(vio);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: deal with timer timeout.
+ */
+static int
+vty_timer_thread (struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ vio->sock.t_timer = NULL ; /* implicitly */
+
+ uty_timer_expired(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0;
+}
+
+/*==============================================================================
+ * VTY Listener(s)
+ *
+ * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
+ */
+
+typedef struct vty_listener* vty_listener ;
+
+struct vty_listener
+{
+ vty_listener next ; /* ssl type list */
+
+ enum vty_type type ;
+
+ struct vio_sock sock ;
+};
+
+/* List of listeners so can tidy up. */
+static vty_listener vty_listeners_list = NULL ;
+
+/* Prototypes for listener stuff */
+static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
+static int uty_serv_sock(const char* addr, unsigned short port) ;
+static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port) ;
+static int uty_serv_vtysh(const char *path) ;
+static int vty_accept_thread(struct thread *thread) ;
+static void vty_accept_qnexus(qps_file qf, void* listener) ;
+static int uty_accept(vty_listener listener, int listen_sock) ;
+static int uty_accept_term(vty_listener listener) ;
+static int uty_accept_shell_serv (vty_listener listener) ;
+
+static void uty_serv_start_listener(int fd, enum vty_type type) ;
+
+/*------------------------------------------------------------------------------
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
+
+/*------------------------------------------------------------------------------
+ * Open VTY listener(s)
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_open_listeners(const char *addr, unsigned short port, const char *path)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* If port is set to 0, do not listen on TCP/IP at all! */
+ if (port)
+ {
+ int n ;
+
+ if (VTY_USE_ADDRINFO)
+ n = uty_serv_sock_addrinfo(addr, port);
+ else
+ n = uty_serv_sock(addr, port);
+
+ if (n == 0)
+ uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
+ }
+
+ /* If want to listen for vtysh, set up listener now */
+ if (VTYSH_ENABLED && (path != NULL))
+ uty_serv_vtysh(path) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY listener
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_close_listeners(void)
+{
+ vty_listener listener ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
+ {
+ uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
+ XFREE(MTYPE_VTY, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- using getaddrinfo().
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+{
+#if VTY_USE_ADDRINFO
+
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
+
+ int ret;
+ int n ;
+ struct addrinfo req;
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ char port_str[16];
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Want to listen, TCP-wise, on all available address families, on the
+ * given port.
+ */
+ memset (&req, 0, sizeof (struct addrinfo));
+ req.ai_flags = AI_PASSIVE;
+ req.ai_family = AF_UNSPEC;
+ req.ai_socktype = SOCK_STREAM;
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ ret = getaddrinfo (hostname, port_str, &req, &ainfo);
+
+ if (ret != 0)
+ {
+ fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
+ exit (1);
+ }
+
+ /* Open up sockets on all AF_INET and AF_INET6 addresses */
+ ainfo_save = ainfo;
+
+ n = 0 ;
+ do
+ {
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
+ continue;
+
+ assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
+
+ ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
+ ainfo->ai_protocol, ainfo->ai_addr, port) ;
+ if (ret >= 0)
+ ++n ;
+ }
+ while ((ainfo = ainfo->ai_next) != NULL);
+
+ freeaddrinfo (ainfo_save);
+
+ return n ;
+
+#else
+ zabort("uty_serv_sock_addrinfo not implemented") ;
+#endif /* VTY_USE_ADDRINFO */
+}
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock(const char* addr, unsigned short port)
+{
+ int ret;
+ int n ;
+ union sockunion su_addr ;
+ struct sockaddr* sa ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ n = 0 ; /* nothing opened yet */
+
+ /* If have an address, see what kind and whether valid */
+ sa = NULL ;
+
+ if (addr != NULL)
+ {
+ ret = str2sockunion (addr, &su_addr) ;
+ if (ret == 0)
+ sa = &su_addr.sa ;
+ else
+ uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ } ;
+
+ /* Try for AF_INET */
+ ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+
+#if HAVE_IPV6
+ /* Try for AF_INET6 */
+ ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+#endif
+
+ /* If not used the address... something wrong */
+ if (sa != NULL)
+ uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+
+ /* Done */
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_TERM listener socket.
+ *
+ * The sockaddr 'sa' may be NULL or of a different address family, in which
+ * case "any" address is used.
+ *
+ * If the sockaddr 'sa' is used, only the address portion is used.
+ *
+ * Returns: < 0 => failed
+ * == 0 => OK -- did not use the sockaddr 'sa'.
+ * > 1 => OK -- and did use the sockaddr 'sa'
+ */
+static int
+uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port)
+{
+ union sockunion su ;
+ int sock ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Is there an address and is it for this family ? */
+ if ((sa != NULL) || (sa->sa_family == family))
+ /* Set up sockunion containing required family and address */
+ sockunion_new_sockaddr(&su, sa) ;
+ else
+ {
+ /* no address or wrong family -- set up empty sockunion of
+ * required family */
+ sockunion_init_new(&su, family) ;
+ sa = NULL ;
+ } ;
+
+ /* Open the socket and set its properties */
+ sock = sockunion_socket(family, type, protocol) ;
+ if (sock < 0)
+ return -1 ;
+
+ ret = sockopt_reuseaddr (sock);
+
+ if (ret >= 0)
+ ret = sockopt_reuseport (sock);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
+ /* Want only IPV6 on ipv6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ */
+ if ((ret >= 0) && (sa->sa_family == AF_INET6))
+ {
+ int on = 1;
+ ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
+ }
+#endif
+
+ if (ret >= 0)
+ ret = sockunion_bind (sock, &su, port, sa) ;
+
+ if (ret >= 0)
+ ret = sockunion_listen (sock, 3);
+
+ if (ret < 0)
+ {
+ close (sock);
+ return -1 ;
+ }
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_TERM) ;
+
+ /* Return OK and signal whether used address or not */
+ return (sa != NULL) ? 1 : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_SHEL_SERV listener socket (UNIX domain).
+ *
+ * Returns: < 0 => failed
+ * >= 0 => OK
+ */
+static int
+uty_serv_vtysh(const char *path)
+{
+ int ret;
+ int sock, sa_len, path_len ;
+ struct sockaddr_un sa_un ;
+ mode_t old_mask;
+ struct zprivs_ids_t ids;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* worry about the path length */
+ path_len = strlen(path) + 1 ;
+ if (path_len >= (int)sizeof(sa_un.sun_path))
+ {
+ uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
+ return -1 ;
+ } ;
+
+ /* First of all, unlink existing socket */
+ unlink (path);
+
+ /* Make UNIX domain socket. */
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ errtoa(errno, 0).str) ;
+ return -1 ;
+ }
+
+ /* Bind to the required path */
+ memset (&sa_un, 0, sizeof(sa_un));
+ sa_un.sun_family = AF_UNIX;
+ strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ errtoa(errno, 0).str) ;
+ }
+
+ umask (old_mask);
+
+ /* Give up now if failed along the way */
+ if (ret < 0)
+ {
+ close (sock) ;
+ return -1 ;
+ } ;
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Socket is open -- set a VTY listener going
+ *
+ * Note that the vyt_listener structure is passed to the accept action function.
+ */
+static void
+uty_serv_start_listener(int fd, enum vty_type type)
+{
+ vty_listener listener ;
+
+ listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+
+ ssl_push(vty_listeners_list, listener, next) ;
+ uty_sock_init_new(&listener->sock, fd, listener) ;
+
+ listener->type = type ;
+
+ if (vty_cli_nexus)
+ listener->sock.action.read.qnexus = vty_accept_qnexus ;
+ else
+ listener->sock.action.read.thread = vty_accept_thread ;
+
+ uty_sock_set_read(&listener->sock, on) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the thread world -- create and dispatch VTY
+ */
+static int
+vty_accept_thread(struct thread *thread)
+{
+ vty_listener listener = THREAD_ARG(thread) ;
+ int result ;
+
+ VTY_LOCK() ;
+
+ result = uty_accept(listener, THREAD_FD(thread));
+
+ uty_sock_set_read(&listener->sock, on) ;
+
+ VTY_UNLOCK() ;
+ return result ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the qnexus world -- create and dispatch VTY
+ */
+static void
+vty_accept_qnexus(qps_file qf, void* listener)
+{
+ VTY_LOCK() ;
+
+ uty_accept(listener, qf->fd);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
+ */
+static int
+uty_accept(vty_listener listener, int listen_sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(listener->sock.fd == listen_sock) ;
+
+ switch (listener->type)
+ {
+ case VTY_TERM:
+ return uty_accept_term(listener) ;
+
+ case VTY_SHELL_SERV:
+ return uty_accept_shell_serv(listener) ;
+
+ default:
+ zabort("unknown vty type") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM
+ */
+static int
+uty_accept_term(vty_listener listener)
+{
+ int sock_fd;
+ union sockunion su;
+ int ret;
+ unsigned int on;
+ struct prefix *p ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* We can handle IPv4 or IPv6 socket. */
+ sockunion_init_new(&su, AF_UNSPEC) ;
+
+ sock_fd = sockunion_accept (listener->sock.fd, &su);
+
+ if (sock_fd < 0)
+ {
+ if (sock_fd == -1)
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ errtoa(errno, 0).str) ;
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return -1 ;
+ } ;
+
+ /* New socket is open... worry about access lists */
+ p = sockunion2hostprefix (&su);
+ ret = 0 ; /* so far, so good */
+
+ if ((p->family == AF_INET) && vty_accesslist_name)
+ {
+ /* VTY's accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+
+#ifdef HAVE_IPV6
+ if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ {
+ /* VTY's ipv6 accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+#endif /* HAVE_IPV6 */
+
+ prefix_free (p);
+
+ if (ret != 0)
+ {
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
+ close (sock_fd);
+ return 0;
+ } ;
+
+ /* Final options (optional) */
+ on = 1 ;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof (on));
+ if (ret < 0)
+ uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
+ sock_fd, errtoa(errno, 0).str) ;
+
+ /* All set -- create the VTY_TERM */
+ uty_new_term(sock_fd, &su);
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
+ sock_fd) ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_SHELL_SERV
+ */
+static int
+uty_accept_shell_serv (vty_listener listener)
+{
+ int sock_fd ;
+ int ret ;
+ int client_len ;
+ struct sockaddr_un client ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ client_len = sizeof(client);
+ memset (&client, 0, client_len);
+
+ sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
+ (socklen_t *) &client_len) ;
+
+ if (sock_fd < 0)
+ {
+ uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
+ errtoa(errno, 0).str) ;
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return -1 ;
+ } ;
+
+ /* All set -- create the VTY_SHELL_SERV */
+ if (VTYSH_DEBUG)
+ printf ("VTY shell accept\n");
+
+ uty_new_shell_serv(sock_fd) ;
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
+ return 0;
+}
+
+/*==============================================================================
+ * Reading from the VTY_SHELL_SERV type sock.
+ *
+ * The select/pselect call-back ends up in utysh_read_ready().
+ */
+
+/*------------------------------------------------------------------------------
+ * Ready to read -> kicking the "SHELL_SERV CLI"
+ *
+ * End up here when there is something ready to be read.
+ *
+ * Will also end up here if an error has occurred, the other end has closed,
+ * this end has half closed, etc. This fact is used to kick the CLI even when
+ * there is no data to be read.
+ *
+ * Note that nothing is actually read here -- reading is done in the CLI itself,
+ * if required.
+ *
+ * The CLI decides whether to re-enable read, or enable write, or both.
+ */
+static void
+utysh_read_ready(vty_io vio)
+{
+ uty_sock_set_read(&vio->sock, off) ;
+
+ /* TODO: need minimal "CLI" for VTY_SHELL_SERV
+ * NB: when output from command is flushed out, must append the
+ * following four bytes: '\0' '\0' '\0' <ret>
+ * Where <ret> is the command return code.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static void
+vtysh_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ utysh_read_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static int
+vtysh_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_read = NULL ; /* implicitly */
+ utysh_read_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the command line buffer
+ *
+ * Lines coming in are terminated by '\0'.
+ *
+ * Assumes that the incoming command line is empty or otherwise incomplete.
+ *
+ * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
+ * when get '\0' or empties the "buf".
+ *
+ * When empties "buf", reads a lump from the sock.
+ *
+ * Returns: 0 => command line is incomplete
+ * 1 => have a complete command line
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+utysh_read (vty_io vio, qstring cl, qstring buf)
+{
+ int get ;
+ char* cp ;
+ char* ep ;
+ size_t have ;
+
+ while (1)
+ {
+ /* process what there is in the buffer */
+ if (buf->len > buf->cp)
+ {
+ cp = qs_cp_char(buf) ;
+ ep = qs_ep_char(buf) ;
+ have = ep - cp ;
+
+ ep = memchr(cp, '\0', have) ;
+ if (ep != NULL)
+ have = ep - cp ; /* have upto, but excluding '\0' */
+
+ if (have > 0) /* take what have */
+ {
+ qs_insert(cl, cp, have) ;
+ cl->cp += have ;
+ buf->cp += have ;
+ } ;
+
+ if (ep != NULL) /* if found '\0' */
+ {
+ qs_term(cl) ; /* '\0' terminate */
+ ++buf->cp ; /* step past it */
+ return 1 ; /* have a complete line <<<<<<<<<<<<< */
+ }
+ } ;
+
+ /* buffer is empty -- try and get some more stuff */
+ assert(buf->len == buf->cp) ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
+
+ qs_need(buf, 500) ; /* need a reasonable lump */
+ qs_clear(buf) ; /* set cp = len = 0 */
+
+ get = read_nb(vio->sock.fd, buf->body, buf->size) ;
+ if (get > 0)
+ buf->len = get ;
+ else if (get == 0)
+ return 0 ; /* have an incomplete line <<<<<<<<<<<< */
+ else
+ {
+ if (get == -1)
+ uty_sock_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+
+ return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Output to vty which are set to "monitor".
+ *
+ * This is VERY TRICKY.
+ *
+ * If not running qpthreaded, then the objective is to get the message away
+ * immediately -- do not wish it to be delayed in any way by the thread
+ * system.
+ *
+ * So proceed as follows:
+ *
+ * a. wipe command line -- which adds output to the CLI buffer
+ *
+ * b. write the CLI buffer to the sock and any outstanding line control.
+ *
+ * c. write the monitor output.
+ *
+ * If that does not complete, put the tail end to the CLI buffer.
+ *
+ * d. restore any command line -- which adds output to the CLI buffer
+ *
+ * e. write the CLI buffer to the sock
+ *
+ * If that all succeeds, nothing has changed as far as the VTY stuff is
+ * concerned -- except that possibly some CLI output was sent before it got
+ * round to it.
+ *
+ * Note that step (b) will deal with any output hanging around from an
+ * earlier step (e). If cannot complete that, then does not add fuel to the
+ * fire -- but the message will be discarded.
+ *
+ * If that fails, or does not complete, then can set write on, to signal that
+ * there is some output in the CLI buffer that needs to be sent, or some
+ * error to be dealt with.
+ *
+ * The output should be tidy.
+ *
+ * To cut down the clutter, step (d) is performed only if the command line
+ * is not empty (or if in cli_more_wait). Once a the user has started to enter
+ * a command, the prompt and the command will remain visible.
+ *
+ * When logging an I/O error for a vty that happens to be a monitor, the
+ * monitor-ness has already been turned off. The monitor output code does not
+ * attempt to log any errors, sets write on so that the error will be picked
+ * up that way.
+ *
+ * However, in the event of an assertion failure, it is possible that an
+ * assertion will fail inside the monitor output. The monitor_busy flag
+ * prevents disaster. It is also left set if I/O fails in monitor output, so
+ * will not try to use the monitor again.
+ *
+ * Note that an assertion which is false for all vty monitors will recurse
+ * through all the monitors, setting each one busy, in turn !
+ *
+
+
+ * TODO: sort out write on in the qpthreads world ??
+ *
+ * The problem is that the qpselect structure is designed to be accessed ONLY
+ * within the thread to which it belongs. This makes it impossible for the
+ * monitor output to set/clear read/write on the vty sock... so some way
+ * around this is required.
+ */
+
+/*------------------------------------------------------------------------------
+ * Output logging information to all vty which are set to "monitor".
+ */
+extern void
+uty_log(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio = sdl_head(vio_monitors_base) ;
+
+ if (vio == NULL)
+ return ; /* go no further if no "monitor" vtys */
+
+ /* Prepare line for output. */
+ uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
+
+ /* write to all known "monitor" vty
+ *
+ */
+ while (vio != NULL)
+ {
+ if (!vio->monitor_busy)
+ {
+ int ret ;
+
+ vio->monitor_busy = 1 ; /* close the door */
+
+ uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+
+ ret = uty_write_monitor(vio) ;
+ if (ret == 0)
+ {
+ ret = write_nb(vio->sock.fd, ll->line, ll->len) ;
+
+ if (ret >= 0)
+ {
+ ret = uty_cli_post_monitor(vio, ll->line + ret,
+ ll->len - ret) ;
+ if (ret > 0)
+ ret = uty_write_monitor(vio) ;
+ } ;
+ } ;
+
+ if (ret != 0)
+ /* need to prod */ ;
+
+ if (ret >= 0)
+ vio->monitor_busy = 0 ;
+ } ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Async-signal-safe version of vty_log for fixed strings.
+ *
+ * This is last gasp operation.
+ */
+void
+vty_log_fixed (const char *buf, size_t len)
+{
+ vty_io vio ;
+
+ /* Write to all known "monitor" vty
+ *
+ * Forget all the niceties -- about to die in any case.
+ */
+ vio = sdl_head(vio_monitors_base) ;
+ while (vio != NULL)
+ {
+ write(vio->sock.fd, buf, len) ;
+ write(vio->sock.fd, "\r\n", 2) ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
diff --git a/lib/vty_io_file.h b/lib/vty_io_file.h
new file mode 100644
index 00000000..b4a79f52
--- /dev/null
+++ b/lib/vty_io_file.h
@@ -0,0 +1,56 @@
+/* VTY IO FILE -- File I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VTY_IO_FILE_H
+#define _ZEBRA_VTY_IO_FILE_H
+
+#include "misc.h"
+#include <errno.h>
+
+#include "uty.h"
+#include "vty.h"
+#include "vty_io.h"
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "keystroke.h"
+#include "thread.h"
+#include "command.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O to files
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern int uty_vprintf_file(vty_io vio, const char *format, va_list args) ;
+
+
+#endif /* _ZEBRA_VTY_IO_FILE_H */
diff --git a/lib/vty_io_shell.c b/lib/vty_io_shell.c
new file mode 100644
index 00000000..c56cc2d2
--- /dev/null
+++ b/lib/vty_io_shell.c
@@ -0,0 +1,366 @@
+/* VTY IO SHELL -- VTY Shell I/O
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zebra.h"
+
+#include "vty.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "qstring.h"
+#include "keystroke.h"
+
+#include "memory.h"
+
+#include "prefix.h"
+#include "filter.h"
+#include "privs.h"
+#include "sockunion.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <sys/un.h> /* for VTYSH */
+#include <sys/socket.h>
+
+#define VTYSH_DEBUG 0
+
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_shell_serv(int sock_fd)
+{
+ struct vty *vty ;
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
+ vio = vty->vio ;
+
+ /* Set the action functions */
+ if (vty_cli_nexus)
+ {
+ vio->sock.action.read.qnexus = vtysh_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = NULL ;
+ }
+ else
+ {
+ vio->sock.action.read.thread = vtysh_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = NULL ;
+ } ;
+
+ vty->node = VIEW_NODE;
+
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+
+ return vty;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_SHEL_SERV listener socket (UNIX domain).
+ *
+ * Returns: < 0 => failed
+ * >= 0 => OK
+ */
+static int
+uty_serv_vtysh(const char *path)
+{
+ int ret;
+ int sock, sa_len, path_len ;
+ struct sockaddr_un sa_un ;
+ mode_t old_mask;
+ struct zprivs_ids_t ids;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* worry about the path length */
+ path_len = strlen(path) + 1 ;
+ if (path_len >= (int)sizeof(sa_un.sun_path))
+ {
+ uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
+ return -1 ;
+ } ;
+
+ /* First of all, unlink existing socket */
+ unlink (path);
+
+ /* Make UNIX domain socket. */
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ errtoa(errno, 0).str) ;
+ return -1 ;
+ }
+
+ /* Bind to the required path */
+ memset (&sa_un, 0, sizeof(sa_un));
+ sa_un.sun_family = AF_UNIX;
+ strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ errtoa(errno, 0).str) ;
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ errtoa(errno, 0).str) ;
+ }
+
+ umask (old_mask);
+
+ /* Give up now if failed along the way */
+ if (ret < 0)
+ {
+ close (sock) ;
+ return -1 ;
+ } ;
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_SHELL_SERV
+ */
+static int
+uty_accept_shell_serv (vty_listener listener)
+{
+ int sock_fd ;
+ int ret ;
+ int client_len ;
+ struct sockaddr_un client ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ client_len = sizeof(client);
+ memset (&client, 0, client_len);
+
+ sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
+ (socklen_t *) &client_len) ;
+
+ if (sock_fd < 0)
+ {
+ uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
+ errtoa(errno, 0).str) ;
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return -1 ;
+ } ;
+
+ /* All set -- create the VTY_SHELL_SERV */
+ if (VTYSH_DEBUG)
+ printf ("VTY shell accept\n");
+
+ uty_new_shell_serv(sock_fd) ;
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
+ return 0;
+}
+
+/*==============================================================================
+ * Reading from the VTY_SHELL_SERV type sock.
+ *
+ * The select/pselect call-back ends up in utysh_read_ready().
+ */
+
+/*------------------------------------------------------------------------------
+ * Ready to read -> kicking the "SHELL_SERV CLI"
+ *
+ * End up here when there is something ready to be read.
+ *
+ * Will also end up here if an error has occurred, the other end has closed,
+ * this end has half closed, etc. This fact is used to kick the CLI even when
+ * there is no data to be read.
+ *
+ * Note that nothing is actually read here -- reading is done in the CLI itself,
+ * if required.
+ *
+ * The CLI decides whether to re-enable read, or enable write, or both.
+ */
+static void
+utysh_read_ready(vty_io vio)
+{
+ uty_sock_set_read(&vio->sock, off) ;
+
+ /* TODO: need minimal "CLI" for VTY_SHELL_SERV
+ * NB: when output from command is flushed out, must append the
+ * following four bytes: '\0' '\0' '\0' <ret>
+ * Where <ret> is the command return code.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static void
+vtysh_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
+
+ utysh_read_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static int
+vtysh_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
+
+ vio->sock.t_read = NULL ; /* implicitly */
+ utysh_read_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the command line buffer
+ *
+ * Lines coming in are terminated by '\0'.
+ *
+ * Assumes that the incoming command line is empty or otherwise incomplete.
+ *
+ * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
+ * when get '\0' or empties the "buf".
+ *
+ * When empties "buf", reads a lump from the sock.
+ *
+ * Returns: 0 => command line is incomplete
+ * 1 => have a complete command line
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+utysh_read (vty_io vio, qstring cl, qstring buf)
+{
+ int get ;
+ char* cp ;
+ char* ep ;
+ size_t have ;
+
+ while (1)
+ {
+ /* process what there is in the buffer */
+ if (buf->len > buf->cp)
+ {
+ cp = qs_cp_char(buf) ;
+ ep = qs_ep_char(buf) ;
+ have = ep - cp ;
+
+ ep = memchr(cp, '\0', have) ;
+ if (ep != NULL)
+ have = ep - cp ; /* have upto, but excluding '\0' */
+
+ if (have > 0) /* take what have */
+ {
+ qs_insert(cl, cp, have) ;
+ cl->cp += have ;
+ buf->cp += have ;
+ } ;
+
+ if (ep != NULL) /* if found '\0' */
+ {
+ qs_term(cl) ; /* '\0' terminate */
+ ++buf->cp ; /* step past it */
+ return 1 ; /* have a complete line <<<<<<<<<<<<< */
+ }
+ } ;
+
+ /* buffer is empty -- try and get some more stuff */
+ assert(buf->len == buf->cp) ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
+
+ qs_need(buf, 500) ; /* need a reasonable lump */
+ qs_clear(buf) ; /* set cp = len = 0 */
+
+ get = read_nb(vio->sock.fd, buf->body, buf->size) ;
+ if (get > 0)
+ buf->len = get ;
+ else if (get == 0)
+ return 0 ; /* have an incomplete line <<<<<<<<<<<< */
+ else
+ {
+ if (get == -1)
+ uty_sock_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+
+ return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
diff --git a/lib/vty_io_shell.h b/lib/vty_io_shell.h
new file mode 100644
index 00000000..87cd92d0
--- /dev/null
+++ b/lib/vty_io_shell.h
@@ -0,0 +1,55 @@
+/* VTY IO SHELL -- VTY Shell I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VTY_IO_SHELL_H
+#define _ZEBRA_VTY_IO_SHELL_H
+
+#include "misc.h"
+#include <errno.h>
+
+#include "uty.h"
+#include "vty.h"
+#include "vty_io.h"
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "keystroke.h"
+#include "thread.h"
+#include "command.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O for VTY Shell Server.
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern int uty_vprintf_shell(vty_io vio, const char *format, va_list args) ;
+
+#endif /* _ZEBRA_VTY_IO_SHELL_H */
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
new file mode 100644
index 00000000..2053dcc5
--- /dev/null
+++ b/lib/vty_io_term.c
@@ -0,0 +1,1288 @@
+/* VTY IO TERM -- Telnet Terminal I/O
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "zebra.h"
+
+#include "vty_io_term.h"
+#include "vty_cli.h"
+
+#include "qstring.h"
+#include "keystroke.h"
+
+#include "memory.h"
+
+#include "prefix.h"
+#include "filter.h"
+#include "privs.h"
+#include "sockunion.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <sys/un.h> /* for VTYSH */
+#include <sys/socket.h>
+
+#define VTYSH_DEBUG 0
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
+ *
+ * This is called by the accept action for the VTY_TERMINAL listener.
+ */
+static void
+uty_term_accept_new(int sock_fd, union sockunion *su)
+{
+ struct vty *vty ;
+ vty_io vio ;
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new(VTY_TERMINAL, sock_fd) ;
+ vio = vty->vio ;
+
+ /* The text form of the address identifies the VTY */
+ vty->vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
+
+ /* Set the initial node */
+ if (no_password_check)
+ {
+ if (restricted_mode)
+ vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+
+ /* Pick up current timeout setting */
+ vio->sock.v_timeout = vty_timeout_val;
+
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
+
+ /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
+ vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
+ uty_set_height(vio) ; /* set initial state */
+
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
+
+ /* Reject connection if password isn't set, and not "no password" */
+ if ((host.password == NULL) && (host.password_encrypt == NULL)
+ && ! no_password_check)
+ {
+ uty_close(vio, "Vty password is not set.");
+ vty = NULL;
+ }
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
+
+ if (! no_password_check)
+ uty_output (vty, "\nUser Access Verification\n\n");
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct vio_vf structure for new VTY_TERMINAL type VTY, and add same.
+ *
+ *
+ */
+
+static void
+uty_term_new(vty_io vio, int sock_fd)
+{
+ vio_vf vf ;
+
+ enum vty_readiness ready ;
+
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ /* May only be the first vio_vf ! */
+ assert((vio->vin_base == NULL) && (vio->vout_base == NULL)) ;
+
+ /* Construct and add to the vio */
+ vf = uty_vf_new(vio, sock_fd, vfd_socket, vfd_io_read_write) ;
+
+ uty_vin_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_read_timeout) ;
+ uty_vout_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_write_timeout) ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+
+ /* Pick up current timeout setting */
+ vf->read_timeout = vty_timeout_val;
+
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
+
+ /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
+ vf->olc = vio_lc_init_new(NULL, 0, 0) ;
+ uty_set_height(vio) ; /* set initial state */
+
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
+
+ /* Reject connection if password isn't set, and not "no password" */
+ if ((host.password == NULL) && (host.password_encrypt == NULL)
+ && ! no_password_check)
+ {
+ uty_close(vio, "Vty password is not set.");
+ vty = NULL;
+ }
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
+
+ if (! no_password_check)
+ uty_output (vty, "\nUser Access Verification\n\n");
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
+} ;
+
+
+
+/*------------------------------------------------------------------------------
+ *
+ */
+extern void
+uty_term_half_close(vio_vf vf)
+{
+ vty_io vio ;
+
+ /* Get the vio and ensure that we are all straight */
+ vio = vf->vio ;
+ assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
+
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+
+
+
+ uty_set_monitor(vio, 0) ;
+
+ /* Discard everything in the keystroke stream and force it to EOF */
+ if (vio->key_stream != NULL)
+ keystroke_stream_set_eof(vio->key_stream) ;
+
+ /* Turn off "--more--" so that all output clears without interruption.
+ *
+ * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
+ */
+ vio->cli_more_enabled = 0 ;
+
+ if (vio->cli_more_wait)
+ uty_cli_exit_more_wait(vio) ;
+
+ /* If a command is not in progress, enable output, which will clear
+ * the output buffer if there is anything there, plus any close reason,
+ * and then close.
+ *
+ * If command is in progress, then this process will start when it
+ * completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cmd_out_enabled = 1 ;
+
+ /* Log closing of VTY_TERM */
+ assert(vio->vty->type == VTY_TERMINAL) ;
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", uty_vf_fd(vf)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * vprintf to VTY_TERM
+ *
+ * All output goes to output fifo until command completes.
+ *
+ * NB: MUST be cmd_in_progress
+ *
+ * Discards output if the socket is not open for whatever reason.
+ */
+extern int
+uty_term_vprintf(vio_vf vf, const char *format, va_list args)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vf->write_open)
+ return 0 ; /* discard output if not open ! */
+
+ assert(vf->vio->cmd_in_progress) ;
+
+ return vio_fifo_vprintf(vf->obuf, format, args) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set/Clear "monitor" state:
+ *
+ * set: if VTY_TERM and not already "monitor" (and write_open !)
+ * clear: if is "monitor"
+ */
+extern void
+uty_set_monitor(vty_io vio, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (on && !vio->monitor)
+ {
+ if ((vio->type == VTY_TERM) && vio->sock.write_open)
+ {
+ vio->monitor = 1 ;
+ sdl_push(vio_monitors_base, vio, mon_list) ;
+ } ;
+ }
+ else if (!on && vio->monitor)
+ {
+ vio->monitor = 0 ;
+ sdl_del(vio_monitors_base, vio, mon_list) ;
+ }
+} ;
+
+/*==============================================================================
+ * Action routines for VIN_TERM/VOUT_TERM type vin/vout objects
+ */
+
+static enum vty_readiness uty_term_write(vio_vf vf) ;
+
+/*==============================================================================
+ * Readiness and the VIN_TERM type vin.
+ *
+ * For TERM stuff the driving force is write ready. This is used to prompt the
+ * VOUT_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
+ *
+ * The VIN_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
+ *
+ * So there is only one, common, uty_term_ready function, which:
+ *
+ * 1. attempts to clear any output it can.
+ *
+ * The state of the output affects the CLI, so must always do this before
+ * before invoking the CLI.
+ *
+ * If this write enters the "--more--" state, then will have tried to
+ * write away the prompt.
+ *
+ * 2. invokes the CLI
+ *
+ * Which will do either the standard CLI stuff or the special "--more--"
+ * stuff.
+ *
+ * 3. attempts to write any output there now is.
+ *
+ * If the CLI generated new output, as much as possible is written away
+ * now.
+ *
+ * If this write enters the "--more--" state, then it returns now_ready,
+ * if the prompt was written away, which loops back to the CLI.
+ *
+ * Note that this is arranging:
+ *
+ * a. to write away the "--more--" prompt as soon as the tranche of output to
+ * which it refers, completes
+ *
+ * b. to enter the cli_more_wait CLI for the first time immediately after the
+ * "--more--" prompt is written away.
+ *
+ * The loop limits itself to one trache of command output each time.
+ *
+ * Resets the timer because something happened.
+ */
+static void
+uty_term_ready(vio_fd vfd, void* action_info)
+{
+ enum vty_readiness ready ;
+
+ vio_vf vf = action_info ;
+ vty_io vio = vf->vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_out_done = 0 ; /* not done any command output yet */
+
+ uty_term_write(vf) ; /* try to clear outstanding stuff */
+ do
+ {
+ ready = uty_cli(vio) ; /* do any CLI work... */
+ ready |= uty_term_write(vf) ; /* ...and any output that generates */
+ } while (ready >= now_ready) ;
+
+ uty_file_set_readiness(vf, ready) ;
+} ;
+
+/*==============================================================================
+ * Reading from VTY_TERM.
+ *
+ * The select/pselect call-back ends up in uty_read_ready().
+ *
+ * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
+ * current CLI.
+ */
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the keystroke stream
+ *
+ * Steal keystroke if required -- see keystroke_input()
+ *
+ * Returns: 0 => nothing available
+ * > 0 => read at least one byte
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+uty_read (vty_io vio, keystroke steal)
+{
+ unsigned char buf[500] ;
+ int get ;
+
+ if (!vio->sock.read_open)
+ return -1 ; /* at EOF if not open */
+
+ get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ if (get >= 0)
+ keystroke_input(vio->key_stream, buf, get, steal) ;
+ else if (get < 0)
+ {
+ if (get == -1)
+ uty_file_error(vio, "read") ;
+
+ vio->sock.read_open = 0 ;
+ keystroke_input(vio->key_stream, NULL, 0, steal) ;
+
+ get = -1 ;
+ } ;
+
+ return get ;
+} ;
+
+/*==============================================================================
+ * Writing to VOUT_TERM -- driven by ready state.
+ *
+ * There are two sets of buffering:
+ *
+ * cli -- command line -- which reflects the status of the command line
+ *
+ * cmd -- command output -- which is written to the file only while
+ * cmd_out_enabled.
+ *
+ * The cli output takes precedence.
+ *
+ * Output of command stuff is subject to line_control, and may go through the
+ * "--more--" mechanism.
+ */
+
+static int uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
+static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible of what there is.
+ *
+ * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
+ * empty.
+ *
+ * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
+ * and all output instantly successful.
+ *
+ * Sets write on if prevented from writing everything available for output
+ * by write() threatening to block.
+ *
+ * Returns: write_ready if should now set write on
+ * now_ready if should loop back and try again
+ * not_ready otherwise
+ */
+static enum vty_readiness
+uty_term_write(vio_vf vf)
+{
+ vty_io vio = vf->vio ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ ret = -1 ;
+ while (vf->write_open)
+ {
+ /* Any outstanding line control output takes precedence */
+ if (vf->olc != NULL)
+ {
+ ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
+ if (ret != 0)
+ break ;
+ }
+
+ /* Next: empty out the cli output */
+ ret = vio_fifo_write_nb(&vf->cli_obuf, vio->sock.fd, true) ;
+ if (ret != 0)
+ break ;
+
+ /* Finished now if not allowed to progress the command stuff */
+ if (!vio->cmd_out_enabled)
+ return not_ready ; /* done all can do */
+
+ /* Last: if there is something in the command buffer, do that */
+ if (!vio_fifo_empty(vf->obuf))
+ {
+ if (vio->cmd_out_done)
+ break ; /* ...but not if done once */
+
+ vio->cmd_out_done = 1 ; /* done this once */
+
+ assert(!vio->cli_more_wait) ;
+
+ if (vio->cmd_lc != NULL)
+ ret = uty_write_fifo_lc(vf, vf->obuf, vf->olc) ;
+ else
+ ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
+
+ /* If moved into "--more--" state@
+ *
+ * * the "--more--" prompt is ready to be written, so do that now
+ *
+ * * if that completes, then want to run the CLI *now* to perform the
+ * first stage of the "--more--" process.
+ */
+ if (vio->cli_more_wait)
+ {
+ ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
+ if (ret == 0)
+ return now_ready ;
+ } ;
+
+ if (ret != 0)
+ break ;
+ }
+
+ /* Exciting stuff: there is nothing left to output...
+ *
+ * ... watch out for half closed state.
+ */
+ if (vio->half_closed)
+ {
+ if (vio->close_reason != NULL)
+ {
+ vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
+
+ struct vty* vty = vio->vty ;
+ if (vio->cli_drawn || vio->cli_dirty)
+ vty_out(vty, VTY_NEWLINE) ;
+ vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
+
+ vio->cmd_in_progress = 0 ;
+
+ vio->close_reason = NULL ; /* MUST discard now... */
+ continue ; /* ... and write away */
+ } ;
+
+ if (!vio->closed) /* avoid recursion */
+ uty_close(vio) ;
+
+ return not_ready ; /* it's all over */
+ } ;
+
+ /* For VTY_TERM: if the command line is not drawn, now is a good
+ * time to do that.
+ */
+ if (vio->vty_type == VTY_TERM)
+ if (uty_cli_draw_if_required(vio))
+ continue ; /* do that now. */
+
+ /* There really is nothing left to output */
+ return not_ready ;
+ } ;
+
+ /* Arrives here if there is more to do, or failed (or was !write_open) */
+
+ if (ret >= 0)
+ return write_ready ;
+
+ /* If is write_open, then report the error
+ *
+ * If still read_open, let the reader pick up and report the error, when it
+ * has finished anything it has buffered.
+ */
+ if (vf->write_open)
+ {
+ if (!vf->read_open)
+ uty_file_error(vio, "write") ;
+
+ vf->write_open = false ; /* crash close write */
+ } ;
+
+ /* For whatever reason, is no longer write_open -- clear all buffers.
+ */
+ vio_fifo_clear(vf->obuf) ; /* throw away cli stuff */
+ uty_out_clear(vio) ; /* throw away cmd stuff */
+
+ vio->close_reason = NULL ; /* too late for this */
+
+ return not_ready ; /* NB: NOT blocked by I/O */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible -- for "monitor" output.
+ *
+ * Outputs only:
+ *
+ * a. outstanding line control stuff.
+ *
+ * b. contents of CLI buffer
+ *
+ * And:
+ *
+ * a. does not report any errors.
+ *
+ * b. does not change anything except the state of the buffers.
+ *
+ * In particular, for the qpthreaded world, does not attempt to change
+ * the state of the qfile or any other "thread private" structures.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed (or !write_open)
+ */
+static int
+uty_write_monitor(vio_vf vf)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vf->write_open)
+ return -1 ;
+
+ if (vf->olc != NULL)
+ {
+ int ret ;
+ ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
+
+ if (ret != 0)
+ return ret ;
+ } ;
+
+ return vio_fifo_write_nb(vf->obuf, uty_vf_fd(vf), true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write the given FIFO to output -- subject to possible line control.
+ *
+ * Note that even if no "--more--" is set, will have set some height, so
+ * that does not attempt to empty the FIFO completely all in one go.
+ *
+ * If the line control becomes "paused", it is time to enter "--more--" state
+ * -- unless the FIFO is empty (or "--more--" is not enabled).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * Returns: > 0 => blocked or completed one tranche
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+{
+ int ret ;
+ char* src ;
+ size_t have ;
+
+ /* Collect another line_control height's worth of output.
+ *
+ * Expect the line control to be empty at this point, but it does not have
+ * to be.
+ */
+ vio_lc_set_pause(lc) ; /* clears lc->paused */
+
+ src = vio_fifo_get_rdr(vfifo, &have) ;
+
+ while ((src != NULL) && (!lc->paused))
+ {
+ size_t take ;
+ take = vio_lc_append(lc, src, have) ;
+ src = vio_fifo_step_rdr(vfifo, &have, take) ;
+ } ;
+
+ vf->vio->cli_dirty = (lc->col != 0) ;
+
+ /* Write the contents of the line control */
+ ret = uty_write_lc(vf, vfifo, lc) ;
+
+ if (ret < 0)
+ return ret ; /* give up now if failed. */
+
+ if ((ret == 0) && vio_fifo_empty(vfifo))
+ return 0 ; /* FIFO and line control empty */
+
+ /* If should now do "--more--", now is the time to prepare for that.
+ *
+ * Entering more state issues a new prompt in the CLI buffer, which can
+ * be written once line control write completes.
+ *
+ * The "--more--" cli will not do anything until the CLI buffer has
+ * cleared.
+ */
+ if (lc->paused && vf->vio->cli_more_enabled)
+ uty_cli_enter_more_wait(vf->vio) ;
+
+ return 1 ; /* FIFO or line control, not empty */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of line control (if any).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * NB: does nothing other than write() and buffer management.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+{
+ int ret ;
+
+ ret = vio_lc_write_nb(uty_vf_fd(vf), lc) ;
+
+ if (ret <= 0)
+ vio_fifo_sync_rdr(vfifo) ; /* finished with FIFO contents */
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start command output -- clears down the line control.
+ *
+ * Requires that that current line is empty -- restarts the line control
+ * on the basis that is at column 0.
+ */
+extern void
+uty_cmd_output_start(vio_vf vf)
+{
+ if (vf->olc != NULL)
+ vio_lc_clear(vf->olc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the effective height for line control (if any)
+ *
+ * If using line_control, may enable the "--more--" output handling.
+ *
+ * If not, want some limit on the amount of stuff output at a time.
+ *
+ * Sets the line control window width and height.
+ * Sets cli_more_enabled if "--more--" is enabled.
+ */
+extern void
+uty_set_height(vio_vf vf)
+{
+ vty_io vio = vf->vio ;
+ bool on ;
+
+ on = 0 ; /* default state */
+
+ if ((vf->olc != NULL) && !vio->half_closed)
+ {
+ int height ;
+
+ height = 0 ; /* default state */
+
+ if ((vio->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (vio->lines >= 0)
+ height = vio->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = vio->height - 2 ;
+ if (height < 1)
+ height = 1 ;
+ } ;
+ }
+ else
+ {
+ /* If window size not known, use lines if that has been set
+ * explicitly for this terminal.
+ */
+ if (vio->lines_set)
+ height = vio->lines ;
+ } ;
+
+ if (height > 0)
+ on = 1 ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
+ } ;
+
+ vio->cli_more_enabled = on ;
+} ;
+
+/*==============================================================================
+ * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ */
+
+/*------------------------------------------------------------------------------
+ * Timer has expired.
+ *
+ * If half_closed, then this is curtains -- have waited long enough !
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static void
+uty_timer_expired (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return uty_close(vio) ; /* curtains */
+
+ uty_close(vio, "Timed out") ; /* bring input side to a halt */
+ } ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VTY_TERM
+ *
+ * Note that for VTY_TERM, set only one of read or write, and sets write for
+ * preference.
+ */
+extern void
+uty_file_set_readiness(vio_vf vf, enum vty_readiness ready)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vio_fd_set_read(vf, (ready == read_ready)) ;
+ vio_fd_set_write(vf, (ready >= write_ready)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a new timer value.
+ */
+extern void
+uty_file_set_timer(vio_vf vf, unsigned long timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vf->v_timeout = timeout ;
+} ;
+
+
+
+
+
+
+/*==============================================================================
+ * VTY Listener(s) for VTY_TERMINAL
+ */
+
+/* Prototypes for listener stuff */
+
+static int uty_term_listen_addrinfo(const char *addr, unsigned short port) ;
+static int uty_term_listen_simple(const char *addr, unsigned short port) ;
+static int uty_term_listen_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port) ;
+static void uty_term_accept(int sock_listen) ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERMINAL -- using getaddrinfo() for preference.
+ */
+extern void
+uty_term_open_listeners(const char *addr, unsigned short port)
+{
+ int n ;
+
+ if (VTY_USE_ADDRINFO)
+ n = uty_term_listen_addrinfo(addr, port);
+ else
+ n = uty_term_listen_simple(addr, port);
+
+ if (n == 0)
+ uzlog(NULL, LOG_ERR, "could not open any VTY_TERMINAL listeners") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERMINAL -- using getaddrinfo()
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_term_listen_addrinfo(const char *addr, unsigned short port)
+{
+#if VTY_USE_ADDRINFO /******************************************************/
+
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
+
+ int ret;
+ int n ;
+ struct addrinfo req;
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ char port_str[16];
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Want to listen, TCP-wise, on all available address families, on the
+ * given port.
+ */
+ memset (&req, 0, sizeof (struct addrinfo));
+ req.ai_flags = AI_PASSIVE;
+ req.ai_family = AF_UNSPEC;
+ req.ai_socktype = SOCK_STREAM;
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ ret = getaddrinfo (addr, port_str, &req, &ainfo);
+
+ if (ret != 0)
+ {
+ fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
+ exit (1);
+ }
+
+ /* Open up sockets on all AF_INET and AF_INET6 addresses */
+ ainfo_save = ainfo;
+
+ n = 0 ;
+ do
+ {
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
+ continue;
+
+ assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
+
+ ret = uty_term_listen_open(ainfo->ai_family, ainfo->ai_socktype,
+ ainfo->ai_protocol, ainfo->ai_addr, port) ;
+
+ if (ret >= 0)
+ ++n ;
+ }
+ while ((ainfo = ainfo->ai_next) != NULL);
+
+ freeaddrinfo (ainfo_save);
+
+ return n ;
+
+#else
+ zabort("uty_serv_sock_addrinfo not implemented") ;
+#endif /* VTY_USE_ADDRINFO ****************************************************/
+}
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_term_listen_simple(const char *addr, unsigned short port)
+{
+ int ret;
+ int n ;
+ union sockunion su_addr ;
+ struct sockaddr* sa ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ n = 0 ; /* nothing opened yet */
+
+ /* If have an address, see what kind and whether valid */
+ sa = NULL ;
+
+ if (addr != NULL)
+ {
+ ret = str2sockunion (addr, &su_addr) ;
+ if (ret == 0)
+ sa = &su_addr.sa ;
+ else
+ uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ } ;
+
+ /* Try for AF_INET */
+ ret = uty_term_listen_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+
+#if HAVE_IPV6
+ /* Try for AF_INET6 */
+ ret = uty_term_listen_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+#endif
+
+ /* If not used the address... something wrong */
+ if (sa != NULL)
+ uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+
+ /* Done */
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_TERMINAL listener socket.
+ *
+ * The sockaddr 'sa' may be NULL or of a different address family, in which
+ * case "any" address is used.
+ *
+ * If the sockaddr 'sa' is used, only the address portion is used.
+ *
+ * Returns: < 0 => failed
+ * == 0 => OK -- did not use the sockaddr 'sa'.
+ * > 1 => OK -- and did use the sockaddr 'sa'
+ */
+static int
+uty_term_listen_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port)
+{
+ union sockunion su ;
+ int sock ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Is there an address and is it for this family ? */
+ if ((sa != NULL) || (sa->sa_family == family))
+ /* Set up sockunion containing required family and address */
+ sockunion_new_sockaddr(&su, sa) ;
+ else
+ {
+ /* no address or wrong family -- set up empty sockunion of
+ * required family */
+ sockunion_init_new(&su, family) ;
+ sa = NULL ;
+ } ;
+
+ /* Open the socket and set its properties */
+ sock = sockunion_socket(family, type, protocol) ;
+ if (sock < 0)
+ return -1 ;
+
+ ret = sockopt_reuseaddr (sock);
+
+ if (ret >= 0)
+ ret = sockopt_reuseport (sock);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
+ /* Want only IPV6 on ipv6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ */
+ if ((ret >= 0) && (sa->sa_family == AF_INET6))
+ {
+ int on = 1;
+ ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
+ }
+#endif
+
+ if (ret >= 0)
+ ret = sockunion_bind (sock, &su, port, sa) ;
+
+ if (ret >= 0)
+ ret = sockunion_listen (sock, 3);
+
+ if (ret < 0)
+ {
+ close (sock);
+ return -1 ;
+ }
+
+ /* Socket is open -- set VTY_TERMINAL listener going */
+ uty_add_listener(sock, uty_term_accept) ;
+
+ /* Return OK and signal whether used address or not */
+ return (sa != NULL) ? 1 : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM
+ */
+static void
+uty_term_accept(int sock_listen)
+{
+ int sock_fd;
+ union sockunion su;
+ int ret;
+ unsigned int on;
+ struct prefix *p ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* We can handle IPv4 or IPv6 socket. */
+ sockunion_init_new(&su, AF_UNSPEC) ;
+
+ sock_fd = sockunion_accept (sock_listen, &su);
+
+ if (sock_fd < 0)
+ {
+ if (sock_fd == -1)
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ errtoa(errno, 0).str) ;
+ return ;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock_fd) ;
+ return ;
+ } ;
+
+ /* New socket is open... worry about access lists */
+ p = sockunion2hostprefix (&su);
+ ret = 0 ; /* so far, so good */
+
+ if ((p->family == AF_INET) && vty_accesslist_name)
+ {
+ /* VTY's accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+
+#ifdef HAVE_IPV6
+ if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ {
+ /* VTY's ipv6 accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+#endif /* HAVE_IPV6 */
+
+ prefix_free (p);
+
+ if (ret != 0)
+ {
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
+ close (sock_fd);
+ return ;
+ } ;
+
+ /* Final options (optional) */
+ on = 1 ;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof (on));
+ if (ret < 0)
+ uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
+ sock_fd, errtoa(errno, 0).str) ;
+
+ /* All set -- create the VTY_TERM */
+ uty_term_accept_new(sock_fd, &su);
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
+ sock_fd) ;
+
+ return ;
+} ;
+
+/*==============================================================================
+ * Output to vty which are set to "monitor".
+ *
+ * This is VERY TRICKY.
+ *
+ * If not running qpthreaded, then the objective is to get the message away
+ * immediately -- do not wish it to be delayed in any way by the thread
+ * system.
+ *
+ * So proceed as follows:
+ *
+ * a. wipe command line -- which adds output to the CLI buffer
+ *
+ * b. write the CLI buffer to the sock and any outstanding line control.
+ *
+ * c. write the monitor output.
+ *
+ * If that does not complete, put the tail end to the CLI buffer.
+ *
+ * d. restore any command line -- which adds output to the CLI buffer
+ *
+ * e. write the CLI buffer to the sock
+ *
+ * If that all succeeds, nothing has changed as far as the VTY stuff is
+ * concerned -- except that possibly some CLI output was sent before it got
+ * round to it.
+ *
+ * Note that step (b) will deal with any output hanging around from an
+ * earlier step (e). If cannot complete that, then does not add fuel to the
+ * fire -- but the message will be discarded.
+ *
+ * If that fails, or does not complete, then can set write on, to signal that
+ * there is some output in the CLI buffer that needs to be sent, or some
+ * error to be dealt with.
+ *
+ * The output should be tidy.
+ *
+ * To cut down the clutter, step (d) is performed only if the command line
+ * is not empty (or if in cli_more_wait). Once a the user has started to enter
+ * a command, the prompt and the command will remain visible.
+ *
+ * When logging an I/O error for a vty that happens to be a monitor, the
+ * monitor-ness has already been turned off. The monitor output code does not
+ * attempt to log any errors, sets write on so that the error will be picked
+ * up that way.
+ *
+ * However, in the event of an assertion failure, it is possible that an
+ * assertion will fail inside the monitor output. The monitor_busy flag
+ * prevents disaster. It is also left set if I/O fails in monitor output, so
+ * will not try to use the monitor again.
+ *
+ * Note that an assertion which is false for all vty monitors will recurse
+ * through all the monitors, setting each one busy, in turn !
+ *
+
+
+ * TODO: sort out write on in the qpthreads world ??
+ *
+ * The problem is that the qpselect structure is designed to be accessed ONLY
+ * within the thread to which it belongs. This makes it impossible for the
+ * monitor output to set/clear read/write on the vty sock... so some way
+ * around this is required.
+ */
+
+/*------------------------------------------------------------------------------
+ * Output logging information to all vty which are set to "monitor".
+ */
+extern void
+uty_log(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vio = sdl_head(vio_monitors_base) ;
+
+ if (vio == NULL)
+ return ; /* go no further if no "monitor" vtys */
+
+ /* Prepare line for output. */
+ uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
+
+ /* write to all known "monitor" vty
+ *
+ */
+ while (vio != NULL)
+ {
+ if (!vio->monitor_busy)
+ {
+ int ret ;
+
+ vio->monitor_busy = 1 ; /* close the door */
+
+ uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+
+ ret = uty_write_monitor(vio) ;
+ if (ret == 0)
+ {
+ ret = write_nb(uty_vf_fd(vf), ll->line, ll->len) ;
+
+ if (ret >= 0)
+ {
+ ret = uty_cli_post_monitor(vio, ll->line + ret,
+ ll->len - ret) ;
+ if (ret > 0)
+ ret = uty_write_monitor(vio) ;
+ } ;
+ } ;
+
+ if (ret != 0)
+ /* need to prod */ ;
+
+ if (ret >= 0)
+ vio->monitor_busy = 0 ;
+ } ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Async-signal-safe version of vty_log for fixed strings.
+ *
+ * This is last gasp operation.
+ */
+void
+vty_log_fixed (const char *buf, size_t len)
+{
+ vty_io vio ;
+
+ /* Write to all known "monitor" vty
+ *
+ * Forget all the niceties -- about to die in any case.
+ */
+ vio = sdl_head(vio_monitors_base) ;
+ while (vio != NULL)
+ {
+ write(uty_vf_fd(vf), buf, len) ;
+ write(uty_vf_fd(vf), "\r\n", 2) ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
diff --git a/lib/vty_io_term.h b/lib/vty_io_term.h
new file mode 100644
index 00000000..ef975a71
--- /dev/null
+++ b/lib/vty_io_term.h
@@ -0,0 +1,64 @@
+/* VTY IO TERM -- Telnet Terminal I/O -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VTY_IO_TERM_H
+#define _ZEBRA_VTY_IO_TERM_H
+
+#include "misc.h"
+#include <errno.h>
+
+#include "uty.h"
+#include "vty.h"
+#include "vty_io_basic.h"
+#include "vty_io.h"
+
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "keystroke.h"
+#include "thread.h"
+#include "command.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty_io.c -- the main VTY I/O stuff
+ *
+ * for I/O to Telnet Terminal.
+ */
+
+/*==============================================================================
+ * Functions
+ */
+
+extern void uty_term_new(vty_io vio, int sock_fd) ;
+
+extern void uty_term_half_close(vio_vf vf) ;
+extern int uty_term_vprintf(vio_vf vf, const char *format, va_list args) ;
+
+
+extern void uty_term_open_listeners(const char *addr, unsigned short port) ;
+
+
+#endif /* _ZEBRA_VTY_IO_TERM_H */
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c
new file mode 100644
index 00000000..d4f1c9ba
--- /dev/null
+++ b/lib/vty_pipe.c
@@ -0,0 +1,2867 @@
+/* VTY Command Line Pipe Handling
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "uty.h"
+#include "vty_cli.h"
+#include "vty_io.h"
+#include "vio_lines.h"
+
+#include "command.h"
+#include "command_execute.h"
+#include "command_queue.h"
+
+#include "memory.h"
+
+/*==============================================================================
+ * VTY Command Line Input Pipe
+ *
+ * Here are the mechanics which support the:
+ *
+ * < file_name
+ *
+ * <| shell_command
+ *
+ * and the:
+ *
+ * > file_name
+ * >> file_name
+ * | shell_command
+ *
+ *==============================================================================
+ * The file_name handling
+ *
+ * Two directories are supported:
+ *
+ * "home" -- being the root for configuration files
+ *
+ * "cd" -- being the root for relative filenames, in the usual way
+ *
+ * "here" -- being the directory for the enclosing "< filename"
+ * see below for more detailed semantics
+ *
+ * There are the global values:
+ *
+ * config home -- defaults to directory for configuration file.
+ * may be set by command.
+ *
+ * config cd -- defaults to cwd at start up
+ *
+ * There are the local values, within a given CLI instance (ie VTY):
+ *
+ * cli home -- set to config home when CLI instance starts, or when
+ * config home is set within the CLI.
+ *
+ * cli cd -- similarly
+ *
+ * cli here -- outside < is same as cli home, unless explicitly set
+ * - set by cli here
+ * - pushed each time executes <, and set to directory for
+ * the < filename.
+ * - pushed each time executes <| and left unchanged
+ * - popped each time exits < or <|
+ * - set to parent by "no cli here" inside <, or to
+ * default state outside <
+ *
+ * And then the filename syntax:
+ *
+ * /path -- absolute path -- in the usual way
+ *
+ * path -- path wrt "cli cd" -- in the usual way
+ *
+ * ~/path -- path in "cli home" -- so "config" stuff
+ *
+ * ~./path -- path in "here" -- so wrt to enclosing < file
+ *
+ *==============================================================================
+ * The Input Pipe Commands
+ *
+ * These are "universal commands".
+ *
+ * They are:
+ *
+ * < filename -- filename as above
+ *
+ * treat contents of given file as command lines.
+ * (That may include further < commands.)
+ *
+ * See notes on cli here for pushing/popping the here
+ * directory.
+ *
+ * TODO: Filename and quotes ??
+ *
+ * <| shell_command -- the shell command is executed "as is" by system().
+ *
+ * the stdout and stderr are collected.
+ *
+ * treats stdout as command lines.
+ * (That may include further < commands.)
+ *
+ * anything from stderr is sent to the VTY output.
+ *
+ * As far as the top level CLI is concerned, these are discrete commands.
+ * That is to say:
+ *
+ * -- except where blocked while reading the "pipe", all commands are
+ * executed one after another, in one Routing Engine operation.
+ *
+ * -- in any event, all output is gathered in the VTY buffering, and will
+ * be sent to the console (or where ever) only when the outermost command
+ * completes.
+ *
+ * There are three options associated with the output from a < operation:
+ *
+ * -- suppress command line echo
+ *
+ * whether to suppress echo of commands to the VTY before they are
+ * dispatched.
+ *
+ * The default is not to suppress command line echo.
+ *
+ * -- suppress command results
+ *
+ * whether to suppress any output generated by each command.
+ *
+ * The default is not to suppress command results.
+ *
+ * -- suppress "more"
+ *
+ * whether to do "--more--", if currently applies, when finally
+ * outputting all the command results.
+ *
+ * The default is not to suppress "more".
+ *
+ * This option can only be set for the outermost < operation.
+ *
+ * Remembering that all output occurs in one go when the outermost < operation
+ * completes.
+ *
+ * The default is to show everything and implement "--more--", pretty much as
+ * if the commands had been typed in. Except that "--more--" applies to
+ * everything (including the command lines) together, rather than to the output
+ * of each command individually.
+ *
+ * These options may be changed by flags attached to the <, as follows:
+ *
+ * ! suppress command line echo
+ *
+ * ? suppress "--more--"
+ *
+ * * suppress command output
+ *
+ * TODO: cli xxx commands for echo and output suppression and notes ....
+ *
+ * TODO: Error handling....
+ *
+ * TODO: Closing the CLI....
+ *
+ * TODO: Time out and the CLI....
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+/*==============================================================================
+ *
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+static char* vty_host_name = NULL ;
+int vty_host_name_set = 0 ;
+
+static void uty_new_host_name(const char* name) ;
+
+/*------------------------------------------------------------------------------
+ * Update vty_host_name as per "hostname" or "no hostname" command
+ */
+extern void
+uty_set_host_name(const char* name)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vty_host_name_set = (name != NULL) ;
+
+ if (vty_host_name_set)
+ uty_new_host_name(name) ;
+ else
+ uty_check_host_name() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If vty_host_name is set, free it.
+ */
+extern void
+uty_free_host_name(void)
+{
+ if (vty_host_name != NULL)
+ XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the host name is not set by command, see if the actual host name has
+ * changed, and if so change it.
+ *
+ * This is done periodically in case the actual host name changes !
+ */
+extern void
+uty_check_host_name(void)
+{
+ struct utsname names ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vty_host_name_set)
+ return ; /* nothing to do if set by command */
+
+ uname (&names) ;
+
+ if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
+ uty_new_host_name(names.nodename) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set new vty_host_name and run along list of VTYs to mark the change.
+ */
+static void
+uty_new_host_name(const char* name)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ uty_free_host_name() ;
+ vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
+
+ vio = vio_list_base ;
+ while (vio != NULL)
+ {
+ vio->cli_prompt_set = 0 ;
+ vio = sdl_next(vio, vio_list) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * General mechanism for command execution.
+ *
+ * Command execution is driven by select/pselect -- which means that the
+ * processing of commands is multiplexed with all other activity. In the
+ * following:
+ *
+ * -- read_ready and write_ready are events signalled by select/pselect
+ *
+ * -- setting read or write on, means enabling the file for select/pselect to
+ * consider it for read_ready or write_ready, respectively.
+ *
+ * State of the CLI:
+ *
+ * cli_blocked -- the CLI is unable to process any further keystrokes.
+ *
+ * cmd_in_progress -- a command has been dispatched and has not yet
+ * completed (may have been queued).
+ *
+ * cmd_out_enabled -- the command FIFO is may be emptied.
+ *
+ * This is set when a command completes, and cleared when
+ * everything is written away.
+ *
+ * cli_more_wait -- is in "--more--" wait state
+ *
+ * The following are the valid combinations:
+ *
+ * blkd : cip : o_en : m_wt :
+ * -----:------:------:------:--------------------------------------------
+ * 0 : 0 : 0 : 0 : collecting a new command
+ * 0 : 1 : 0 : 0 : command dispatched
+ * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
+ * 1 : 0 : 1 : 0 : waiting for command output to complete
+ * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
+ * 0 : 0 : 0 : 1 : waiting for "--more--" response
+ * 1 : 1 : 1 : 0 : waiting for command to complete, after the
+ * CLI has been closed
+ *
+ * There are two output FIFOs:
+ *
+ * 1. for the CLI itself -- uty_cli_out and friends
+ *
+ * 2. for output generated by commands -- vty_out and friends.
+ *
+ * The CLI FIFO is emptied whenever possible, in preference to the command
+ * FIFO. The command FIFO is emptied when cmd_out_enabled. While
+ * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a
+ * given command is collected together before being sent to the file.
+ *
+ * Note that only sets read on when the keystroke stream is empty and has not
+ * yet hit eof. The CLI process is driven mostly by write_ready -- which
+ * invokes read_ready.
+ *
+ * Note also that after each command dispatch the CLI processor exits, to be
+ * re-entered again on write_ready/read_ready -- so does one command line at
+ * a time, yielding the processor after each one.
+ *
+ * Note that select/pselect treat a socket which is at "EOF", or has seen an
+ * error, or has been half closed, etc. as readable and writable. This means
+ * that the CLI will continue to move forward even after the socket is no
+ * longer delivering any data.
+ *
+ *------------------------------------------------------------------------------
+ * The "--more--" handling.
+ *
+ * This is largely buried in the output handling.
+ *
+ * While cmd_in_progress is true cmd_out_enabled will be false. When the
+ * command completes:
+ *
+ * * cmd_in_progress is cleared
+ *
+ * * cmd_out_enabled is set
+ *
+ * * cli_blocked will be set
+ *
+ * * the line_control structure is reset
+ *
+ * * the output process is kicked off by setting write on
+ *
+ * The output process used the line_control structure to manage the output, and
+ * occasionally enter the trivial "--more--" CLI. This is invisible to the
+ * main CLI. (See the cli_more_wait flag and its handling.)
+ *
+ * When all the output has completed the CLI will be kicked, which will see
+ * that the output buffer is now empty, and it can proceed.
+ *
+ * It is expected that the output will end with a newline -- so that when the
+ * CLI is kicked, the cursor will be at the start of an empty line.
+ *
+ * This mechanism means that the command output FIFO only ever contains the
+ * output from the last command executed.
+ *
+ * If the user decides to abandon output at the "--more--" prompt, then the
+ * contents of the command output FIFO are discarded.
+ *
+ *------------------------------------------------------------------------------
+ * The qpthreads/qpnexus extension.
+ *
+ * When running in qnexus mode, many commands are not executed directly in the
+ * CLI, but are queued for execution by the main "routeing" nexus.
+ *
+ * The parsing of commands is context sensitive. The context depends may
+ * change during the execution of a command. So... it is not possible to
+ * dispatch a command until the previous one has completed.
+ *
+ * In qnexus mode, when a command is queued, the CLI does not go cli_blocked,
+ * even if some output has already been generated. This allows a further
+ * command to be entered while the previous one is executed. However, if the
+ * command is dispatched before the previous one completes, then the cli will
+ * block.
+ *
+ * While the previous command is executing, the current command line has a
+ * minimal prompt -- to show that the context is not known. When the previous
+ * command completes, the command line is redrawn in the new context.
+ *
+ *------------------------------------------------------------------------------
+ * Command line drawn state.
+ *
+ * When the cli_drawn flag is set, the current console line contains the
+ * current prompt and the user input to date. The cursor is positioned where
+ * the user last placed it.
+ *
+ * The command line can be "wiped" -- see uty_cli_wipe() -- which removes all
+ * output and prompt, and leaves the console at the start of an empty line
+ * where the command line used to be.
+ *
+ * On entry to the CLI, it will draw the command line again if it has been
+ * wiped.
+ *
+ * This mechanism is used to support the partial command line that may be
+ * entered while a queued command executes.
+ *
+ * It is also used for the command help/completion system.
+ *
+ * It is also used to support the "monitor" output.
+ */
+
+/*==============================================================================
+ * The CLI
+ */
+
+#define CONTROL(X) ((X) - '@')
+
+static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
+static enum vty_readiness uty_cli_standard(vty_io vio) ;
+static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
+static void uty_cli_draw(vty_io vio) ;
+static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
+static void uty_cli_wipe(vty_io vio, int len) ;
+
+static void uty_will_echo (vty_io vio) ;
+static void uty_will_suppress_go_ahead (vty_io vio) ;
+static void uty_dont_linemode (vty_io vio) ;
+static void uty_do_window_size (vty_io vio) ;
+static void uty_dont_lflow_ahead (vty_io vio) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise CLI.
+ *
+ * It is assumed that the following have been initialised, empty or zero:
+ *
+ * cli_prompt_for_node
+ * cl
+ * clx
+ * cli_vbuf
+ * cli_obuf
+ *
+ * cli_drawn
+ * cli_dirty
+ *
+ * cli_prompt_set
+ *
+ * cli_blocked
+ * cmd_in_progress
+ * cmd_out_enabled
+ * cli_wait_more
+ *
+ * cli_more_enabled
+ *
+ * Sets the CLI such that there is apparently a command in progress, so that
+ * further initialisation (in particular hello messages and the like) is
+ * treated as a "start up command".
+ *
+ * Sends a suitable set of Telnet commands to start the process.
+ */
+extern void
+uty_cli_init(vty_io vio)
+{
+ assert(vio->type == VTY_TERM) ;
+
+ vio->cmd_in_progress = 1 ;
+ vio->cli_blocked = 1 ;
+
+ vio->cli_do = cli_do_nothing ;
+
+ /* Setting up terminal. */
+ uty_will_echo (vio);
+ uty_will_suppress_go_ahead (vio);
+ uty_dont_linemode (vio);
+ uty_do_window_size (vio);
+ if (0)
+ uty_dont_lflow_ahead (vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start the CLI.
+ *
+ * All start-up operations are complete -- so the "command" is now complete.
+ *
+ * Returns: write_ready -- so the first event is a write event, to flush
+ * any output to date.
+ */
+extern enum vty_readiness
+uty_cli_start(vty_io vio)
+{
+ uty_cli_cmd_complete(vio, CMD_SUCCESS) ;
+ return write_ready ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the CLI
+ *
+ * Note that if any command is revoked, then will clear cmd_in_progress and
+ * set cmd_out_enabled -- so any output can now clear.
+ */
+extern void
+uty_cli_close(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ cq_revoke(vio->vty) ;
+
+ vio->cli_blocked = 1 ; /* don't attempt any more */
+ vio->cmd_out_enabled = 1 ; /* allow output to clear */
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI for VTY_TERM
+ *
+ * Do nothing at all if half closed.
+ *
+ * Otherwise do: standard CLI
+ * or: "--more--" CLI
+ *
+ * NB: on return, requires that an attempt is made to write away anything that
+ * may be ready for that.
+ */
+extern enum vty_readiness
+uty_cli(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ if (vio->half_closed)
+ return not_ready ; /* Nothing more if half closed */
+
+ /* Standard or "--more--" CLI ? */
+ if (vio->cli_more_wait)
+ return uty_cli_more_wait(vio) ;
+ else
+ return uty_cli_standard(vio) ;
+} ;
+
+/*==============================================================================
+ * The Standard CLI
+ */
+
+static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ;
+static void uty_cli_response(vty_io vio, enum cli_do cli_do) ;
+static bool uty_cli_dispatch(vty_io vio) ;
+
+/*------------------------------------------------------------------------------
+ * Standard CLI for VTY_TERM -- if not blocked, runs until:
+ *
+ * * runs out of keystrokes
+ * * executes a command
+ *
+ * Note that this executes at most one command each time it is called. This
+ * is to allow for a modicum of sharing of the system. For real keyboard input
+ * this will make no difference at all !
+ *
+ * Returns: not_ready blocked and was blocked when entered
+ * write_ready if there is anything in the keystroke stream
+ * read_ready otherwise
+ */
+static enum vty_readiness
+uty_cli_standard(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ /* cli_blocked is set when is waiting for a command, or its output to
+ * complete -- unless either of those has happened, is still blocked.
+ *
+ * NB: in both these cases, assumes that other forces are at work to
+ * keep things moving.
+ */
+ if (vio->cli_blocked)
+ {
+ assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
+
+ if (vio->cmd_in_progress)
+ {
+ assert(!vio->cmd_out_enabled) ;
+ return not_ready ;
+ } ;
+
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+ vio->cmd_out_enabled = 0 ;
+ } ;
+
+ /* If there is nothing pending, then can run the CLI until there is
+ * something to do, or runs out of input.
+ *
+ * If there is something to do, that is because a previous command has
+ * now completed, which may have wiped the pending command or changed
+ * the required prompt.
+ */
+ if (vio->cli_do == cli_do_nothing)
+ vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ else
+ uty_cli_draw_this(vio, vio->vty->node) ;
+
+ /* If have something to do, do it. */
+ if (vio->cli_do != cli_do_nothing)
+ {
+ /* Reflect immediate response */
+ uty_cli_response(vio, vio->cli_do) ;
+
+ /* If command not already in progress, dispatch this one, which may
+ * set the CLI blocked.
+ *
+ * Otherwise is now blocked until queued command completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cli_blocked = uty_cli_dispatch(vio) ;
+ else
+ vio->cli_blocked = 1 ;
+ } ;
+
+ /* Use write_ready as a proxy for read_ready on the keystroke stream.
+ *
+ * Also, if the command line is not drawn, then return write_ready, so
+ * that
+ *
+ * Note that if has just gone cli_blocked, still returns ready. This is
+ * defensive: at worst will generate one unnecessary read_ready/write_ready
+ * event.
+ */
+ if (keystroke_stream_empty(vio->key_stream))
+ return read_ready ;
+ else
+ return write_ready ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dispatch the current vio->cli_do -- queueing it if necessary.
+ *
+ * Requires that are NOT blocked and NO command is queued.
+ *
+ * Expects to be on new blank line, and when returns will be on new, blank
+ * line.
+ *
+ * Returns: true <=> command completed and output is pending
+ * false => command has been queued and is now in progress
+ *
+ * Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty.
+ *
+ * Can set vio->cl_do = and vio->cl to be a follow-on command.
+ */
+static bool
+uty_cli_dispatch(vty_io vio)
+{
+ qstring_t tmp ;
+ enum cli_do cli_do ;
+ enum cmd_return_code ret ;
+
+ struct vty* vty = vio->vty ;
+
+ VTY_ASSERT_LOCKED() ;
+ assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
+
+ /* Set vio->clx to the command about to execute.
+ *
+ * Clear vio->cl and vio->cl_do.
+ */
+ vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+ vio->cmd_out_enabled = 0 ; /* => collect all output */
+
+ tmp = vio->clx ; /* swap clx and cl */
+ vio->clx = vio->cl ;
+ vio->cl = tmp ;
+
+ qs_term(&vio->clx) ; /* ensure string is terminated */
+ vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
+ cli_do = vio->cli_do ; /* current operation */
+
+ vio->cli_do = cli_do_nothing ; /* clear */
+ qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+
+ /* Reset the command output FIFO and line_control */
+ assert(vio_fifo_empty(&vio->cmd_obuf)) ;
+ uty_out_clear(vio) ; /* clears FIFO and line control */
+
+ /* Dispatch command */
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ {
+ /* AUTH_NODE and AUTH_ENABLE_NODE are unique */
+ ret = uty_auth(vty, vty->buf, cli_do) ;
+ }
+ else
+ {
+ /* All other nodes... */
+ switch (cli_do)
+ {
+ case cli_do_nothing:
+ ret = CMD_SUCCESS ;
+ break ;
+
+ case cli_do_command:
+ ret = uty_command(vty) ;
+ break ;
+
+ case cli_do_ctrl_c:
+ ret = uty_stop_input(vty) ;
+ break ;
+
+ case cli_do_ctrl_d:
+ ret = uty_down_level(vty) ;
+ break ;
+
+ case cli_do_ctrl_z:
+ ret = uty_command(vty) ;
+ if (ret == CMD_QUEUED)
+ vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
+ else
+ ret = uty_end_config(vty) ; /* do the ^Z now */
+ break ;
+
+ case cli_do_eof:
+ ret = uty_cmd_close(vio->vty, "End") ;
+ break ;
+
+ default:
+ zabort("unknown cli_do_xxx value") ;
+ } ;
+ } ;
+
+ if (ret == CMD_QUEUED)
+ {
+ uty_cli_draw(vio) ; /* draw the prompt */
+ return false ; /* command not complete */
+ }
+ else
+ {
+ uty_cli_cmd_complete(vio, ret) ;
+ return true ; /* command complete */
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Queued command has completed.
+ *
+ * Note that sets write on whether there is anything in the output buffer
+ * or not... write_ready will kick read_ready.
+ */
+extern void
+vty_queued_result(struct vty *vty, enum cmd_return_code ret)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ if (!vio->closed)
+ {
+ uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
+
+ /* Do the command completion actions that were deferred because the
+ * command was queued.
+ *
+ * Return of CMD_QUEUED => command was revoked before being executed.
+ * However interesting that might be... frankly don't care.
+ */
+ uty_cli_cmd_complete(vio, ret) ;
+
+ /* Kick the socket -- to write away any outstanding output, and
+ * re-enter the CLI when that's done.
+ */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+ }
+ else
+ {
+ /* If the VTY is closed, the only reason it still exists is because
+ * there was cmd_in_progress.
+ */
+ vio->cmd_in_progress = 0 ;
+
+ uty_close(vio) ; /* Final close */
+ } ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Command has completed, so:
+ *
+ * * clear cmd_in_progress
+ * * set cmd_out_enabled -- so any output can now proceed
+ * * set cli_blocked -- waiting for output to complete
+ * * and prepare the line control for output
+ *
+ * If the return is CMD_CLOSE, then also now does the required half close.
+ *
+ * Note that apart from CMD_CLOSE, don't really care what the return was. Any
+ * diagnostics or other action must be dealt with elsewhere (as part of the
+ * command execution.
+ *
+ * Note that everything proceeds as if there is some output. So after every
+ * command goes through at least one write_ready event.
+ *
+ * This ensures some multiplexing at the command level.
+ *
+ * It also means that the decision about whether there is anything to output
+ * is left to the output code.
+ */
+static void
+uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+
+ if (ret == CMD_CLOSE)
+ uty_half_close(vio, NULL) ;
+
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vio->cmd_out_enabled = 1 ; /* enable the output */
+ vio->cli_blocked = 1 ; /* now blocked waiting for output */
+
+ vio->vty->buf = NULL ; /* finished with command line */
+
+ uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+} ;
+
+/*==============================================================================
+ * The "--more--" CLI
+ *
+ * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ *
+ * When the output side signals that "--more--" is required, it sets the
+ * cli_more_wait flag and clears the cmd_out_enabled flag.
+ *
+ * The first stage of handling "--more--" is to suck the input dry, so that
+ * (as far as is reasonably possible) does not steal a keystroke as the
+ * "--more--" response which was typed before the prompt was issued.
+ *
+ * The cli_blocked flag indicates that the CLI is in this first stage.
+ */
+
+/*------------------------------------------------------------------------------
+ * Change the CLI to the "--more--" CLI.
+ *
+ * Outputs the new prompt line.
+ */
+extern void
+uty_cli_enter_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
+ wiped before change the CLI state */
+
+ vio->cmd_out_enabled = 0 ; /* stop output pro tem */
+ vio->cli_more_wait = 1 ; /* new state */
+
+ uty_cli_draw(vio) ; /* draw the "--more--" */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Exit the "--more--" CLI.
+ *
+ * Wipes the "--more--" prompt.
+ *
+ * This is used when the user responds to the prompt.
+ *
+ * It is also used when the vty is "half-closed". In this case, it is (just)
+ * possible that the '--more--' prompt is yet to be completely written away,
+ * so:
+ *
+ * * assert that is either: !vio->cli_blocked (most of the time it will)
+ * or: !vio_fifo_empty(&vio->cli_obuf)
+ *
+ * * note that can wipe the prompt even though it hasn't been fully
+ * written away yet. (The effect is to append the wipe action to the
+ * cli_obuf !)
+ */
+extern void
+uty_cli_exit_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf))
+ && !vio->cmd_out_enabled && vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--')
+ before changing the CLI state */
+
+ vio->cli_blocked = 1 ; /* back to blocked waiting for output */
+ vio->cli_more_wait = 0 ; /* exit more_wait */
+ vio->cmd_out_enabled = 1 ; /* re-enable output */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle the "--more--" state.
+ *
+ * Deals with the first stage if cli_blocked.
+ *
+ * Tries to steal a keystroke, and when succeeds wipes the "--more--"
+ * prompt and exits cli_more_wait -- and may cancel all outstanding output.
+ *
+ * EOF on input causes immediate exit from cli_more_state.
+ *
+ * Returns: read_ready -- waiting to steal a keystroke
+ * now_ready -- just left cli_more_wait
+ * not_ready -- otherwise
+ */
+static enum vty_readiness
+uty_cli_more_wait(vty_io vio)
+{
+ struct keystroke steal ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Deal with the first stage of "--more--" */
+ if (vio->cli_blocked)
+ {
+ int get ;
+
+ /* If the CLI buffer is not yet empty, then is waiting for the
+ * initial prompt to clear, so nothing to be done here.
+ */
+ if (!vio_fifo_empty(&vio->cli_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+
+ /* empty the input buffer into the keystroke stream */
+ do
+ {
+ get = uty_read(vio, NULL) ;
+ } while (get > 0) ;
+ } ;
+
+ /* Go through the "--more--" process, unless no longer write_open (!) */
+ if (vio->sock.write_open)
+ {
+ /* The read fetches a reasonable lump from the I/O -- so if there
+ * is a complete keystroke available, expect to get it.
+ *
+ * If no complete keystroke available to steal, returns ks_null.
+ *
+ * If has hit EOF (or error etc), returns knull_eof.
+ */
+ uty_read(vio, &steal) ;
+
+ /* If nothing stolen, make sure prompt is drawn and wait for more
+ * input.
+ */
+ if ((steal.type == ks_null) && (steal.value != knull_eof))
+ {
+ if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */
+ return write_ready ;
+ else
+ return read_ready ;
+ } ;
+
+ /* Stolen a keystroke -- a (very) few terminate all output */
+ if (steal.type == ks_char)
+ {
+ switch (steal.value)
+ {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ uty_out_clear(vio) ;
+ break;
+
+ default: /* everything else, thrown away */
+ break ;
+ } ;
+ } ;
+ } ;
+
+ /* End of "--more--" process
+ *
+ * Wipe out the prompt and update state.
+ *
+ * Return write_ready to tidy up the screen and, unless cleared, write
+ * some more.
+ */
+ uty_cli_exit_more_wait(vio) ;
+
+ return now_ready ;
+} ;
+
+/*==============================================================================
+ * CLI VTY output
+ *
+ * This is buffered separately from the general (command) VTY output above.
+ *
+ * Has a dedicated buffer in the struct vty, which is flushed regularly during
+ * command processing.
+ *
+ * It is expected that can flush straight to the file, since this is running at
+ * CLI speed. However, if the CLI is being driven by something other than a
+ * keyboard, or "monitor" output has filled the buffers, then may need to
+ * have intermediate buffering.
+ *
+ * No actual I/O takes place here-- all "output" is to vio->cli_obuf
+ * and/or vio->cli_ex_obuf
+ *
+ * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
+ * This is used while passwords are entered and to allow command line changes
+ * to be made while the line is not visible.
+ */
+
+enum { cli_rep_count = 32 } ;
+
+typedef const char cli_rep_char[cli_rep_count] ;
+
+static const char telnet_backspaces[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ } ;
+CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
+
+static const char telnet_spaces[] =
+ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
+ } ;
+CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+
+static const char* telnet_newline = "\r\n" ;
+
+static void uty_cli_write(vty_io vio, const char *this, int len) ;
+static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf fprintf()
+ */
+static void
+uty_cli_out(vty_io vio, const char *format, ...)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->sock.write_open)
+ {
+ va_list args ;
+
+ va_start (args, format);
+ vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
+ va_end(args);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo user input
+ *
+ * Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
+ */
+static void
+uty_cli_echo(vty_io vio, const char *this, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
+ return ;
+
+ uty_cli_write(vio, this, len) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo 'n' characters using a cli_rep_char string
+ *
+ * Do nothing if echo suppressed (eg in AUTH_NODE)
+ */
+static void
+uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
+ return ;
+
+ uty_cli_write_n(vio, chars, n) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf write()
+ */
+inline static void
+uty_cli_write(vty_io vio, const char *this, int len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->sock.write_open)
+ vio_fifo_put(&vio->cli_obuf, this, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write 'n' characters using a cli_rep_char string
+ */
+static void
+uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
+{
+ int len ;
+
+ len = sizeof(cli_rep_char) ;
+ while (n > 0)
+ {
+ if (n < len)
+ len = n ;
+ uty_cli_write(vio, chars, len) ;
+ n -= len ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write given string
+ *
+ * Returns length of string.
+ */
+static int
+uty_cli_write_s(vty_io vio, const char *str)
+{
+ int len ;
+
+ len = strlen(str) ;
+ if (len != 0)
+ uty_cli_write(vio, str, len) ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Standard Messages
+ */
+
+/*------------------------------------------------------------------------------
+ * Send newline to the console.
+ *
+ * Clears the cli_drawn and the cli_dirty flags.
+ */
+static void
+uty_cli_out_newline(vty_io vio)
+{
+ uty_cli_write(vio, telnet_newline, 2) ;
+ vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Wipe 'n' characters.
+ *
+ * If 'n' < 0, wipes characters backwards and moves cursor back.
+ * 'n' > 0, wipes characters forwards, leaving cursor where it is
+ */
+static void
+uty_cli_out_wipe_n(vty_io vio, int n)
+{
+ if (n < 0)
+ {
+ n = abs(n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n);
+ } ;
+
+ if (n > 0)
+ {
+ uty_cli_write_n(vio, telnet_spaces, n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send response to the given cli_do
+ *
+ * If no command is in progress, then will send newline to signal that the
+ * command is about to be dispatched.
+ *
+ * If command is in progress, then leaves cursor on '^' to signal that is now
+ * waiting for previous command to complete.
+ */
+static const char* cli_response [2][cli_do_count] =
+{
+ { /* when not waiting for previous command to complete */
+ [cli_do_command] = "",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
+ },
+ { /* when waiting for a previous command to complete */
+ [cli_do_command] = "^",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
+ }
+} ;
+
+static void
+uty_cli_response(vty_io vio, enum cli_do cli_do)
+{
+ const char* str ;
+ int len ;
+
+ if ((cli_do == cli_do_nothing) || (vio->half_closed))
+ return ;
+
+ str = (cli_do < cli_do_count)
+ ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
+ assert(str != NULL) ;
+
+ len = uty_cli_write_s(vio, str) ;
+
+ if (vio->cmd_in_progress)
+ {
+ vio->cli_extra_len = len ;
+ uty_cli_write_n(vio, telnet_backspaces, len) ;
+ }
+ else
+ {
+ uty_cli_out_newline(vio) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send various messages with trailing newline.
+ */
+
+static void
+uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+static void
+uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*==============================================================================
+ * Command line draw and wipe
+ */
+
+/*------------------------------------------------------------------------------
+ * Wipe the current console line -- if any.
+ */
+static void
+uty_cli_wipe(vty_io vio, int len)
+{
+ int a ;
+ int b ;
+
+ if (!vio->cli_drawn)
+ return ; /* quit if already wiped */
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ /* Establish how much ahead and how much behind the cursor */
+ a = vio->cli_extra_len ;
+ b = vio->cli_prompt_len ;
+
+ if (!vio->cli_echo_suppress && !vio->cli_more_wait)
+ {
+ a += vio->cl.len - vio->cl.cp ;
+ b += vio->cl.cp ;
+ } ;
+
+ /* Wipe anything ahead of the current position and ahead of new len */
+ if ((a + b) > len)
+ uty_cli_out_wipe_n(vio, +a) ;
+
+ /* Wipe anything behind current position, but ahead of new len */
+ if (b > len)
+ {
+ uty_cli_out_wipe_n(vio, -(b - len)) ;
+ b = len ; /* moved the cursor back */
+ } ;
+
+ /* Back to the beginning of the line */
+ uty_cli_write_n(vio, telnet_backspaces, b) ;
+
+ /* Nothing there any more */
+ vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If not currently drawn, draw prompt etc according to the current state
+ * and node.
+ *
+ * See uty_cli_draw().
+ */
+extern bool
+uty_cli_draw_if_required(vty_io vio)
+{
+ if (vio->cli_drawn)
+ return false ;
+
+ uty_cli_draw(vio) ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt etc for the current vty node.
+ *
+ * See uty_cli_draw_this()
+ */
+static void
+uty_cli_draw(vty_io vio)
+{
+ uty_cli_draw_this(vio, vio->vty->node) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt and entire command line, leaving current position where it
+ * should be.
+ *
+ * If command line is currently drawn, this wipes and redraws.
+ *
+ * Otherwise, assumes is positioned at start of an empty line.
+ *
+ * Draws prompt according to the given 'node', except:
+ *
+ * * if is half_closed, draw nothing -- wipes the current line
+ *
+ * * if is cli_more_wait, draw the "--more--" prompt
+ *
+ * * if is cmd_in_progress, draw the vestigial prompt.
+ *
+ * By the time the current command completes, the node may have changed, so
+ * the current prompt may be invalid.
+ *
+ * Sets: cli_drawn = true
+ * cli_dirty = false
+ * cli_prompt_len = length of prompt used
+ * cli_extra_len = 0
+ * cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
+ */
+static void
+uty_cli_draw_this(vty_io vio, enum node_type node)
+{
+ const char* prompt ;
+ size_t l_len ;
+ int p_len ;
+
+ if (vio->cli_dirty)
+ uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
+
+ /* Sort out what the prompt is. */
+ if (vio->half_closed)
+ {
+ prompt = "" ;
+ p_len = 0 ;
+ l_len = 0 ;
+ }
+ else if (vio->cli_more_wait)
+ {
+ prompt = "--more--" ;
+ p_len = strlen(prompt) ;
+ l_len = 0 ;
+ }
+ else if (vio->cmd_in_progress)
+ {
+ /* If there is a queued command, the prompt is a minimal affair. */
+ prompt = "~ " ;
+ p_len = strlen(prompt) ;
+ l_len = vio->cl.len ;
+ }
+ else
+ {
+ /* The prompt depends on the node, and is expected to include the
+ * host name.
+ *
+ * Caches the prompt so doesn't re-evaluate it every time.
+ *
+ * If the host name changes, the cli_prompt_set flag is cleared.
+ */
+ if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
+ {
+ const char* prompt ;
+
+ if (vty_host_name == NULL)
+ uty_check_host_name() ; /* should never be required */
+
+ prompt = cmd_prompt(node) ;
+ if (prompt == NULL)
+ {
+ zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
+ prompt = "%s ???: " ;
+ } ;
+
+ qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+
+ vio->cli_prompt_node = node ;
+ vio->cli_prompt_set = 1 ;
+ } ;
+
+ prompt = vio->cli_prompt_for_node.body ;
+ p_len = vio->cli_prompt_for_node.len ;
+ l_len = vio->cl.len ;
+ } ;
+
+ /* Now, if line is currently drawn, time to wipe it */
+ if (vio->cli_drawn)
+ uty_cli_wipe(vio, p_len + l_len) ;
+
+ /* Set about writing the prompt and the line */
+ vio->cli_drawn = 1 ;
+ vio->cli_extra_len = 0 ;
+ vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ vio->cli_prompt_len = p_len ;
+
+ uty_cli_write(vio, prompt, p_len) ;
+
+ if (l_len != 0)
+ {
+ uty_cli_write(vio, qs_chars(&vio->cl), l_len) ;
+ if (vio->cl.cp < l_len)
+ uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Monitor output.
+ *
+ * To prepare for monitor output, wipe as much as is necessary for the
+ * monitor line to appear correctly.
+ *
+ * After monitor output, may need to do two things:
+ *
+ * * if the output was incomplete, place the rump in the CLI buffer,
+ * so that:
+ *
+ * a. don't mess up the console with partial lines
+ *
+ * b. don't lose part of a message
+ *
+ * c. act as a brake on further monitor output -- cannot do any more
+ * until the last, part, line is dealt with.
+ *
+ * * restore the command line, unless it is empty !
+ */
+
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_io vio, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(vio, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Recover from monitor line output.
+ *
+ * If monitor line failed to complete, append the rump to the CLI buffer.
+ *
+ * If have a non-empty command line, or is cli_more_wait, redraw the command
+ * line.
+ *
+ * Returns: 0 => rump was empty and no command line stuff written
+ * > 0 => rump not empty or some command line stuff written
+ */
+extern int
+uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (len != 0)
+ uty_cli_write(vio, buf, len) ;
+
+ if (vio->cli_more_wait || (vio->cl.len != 0))
+ {
+ uty_cli_draw(vio) ;
+ ++len ;
+ } ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Command line processing loop
+ */
+
+static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ;
+static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
+static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
+static int uty_cli_word_overwrite (vty_io vio, char *str) ;
+static int uty_cli_forwards(vty_io vio, int n) ;
+static int uty_cli_backwards(vty_io vio, int n) ;
+static int uty_cli_del_forwards(vty_io vio, int n) ;
+static int uty_cli_del_backwards(vty_io vio, int n) ;
+static int uty_cli_bol (vty_io vio) ;
+static int uty_cli_eol (vty_io vio) ;
+static int uty_cli_word_forwards_delta(vty_io vio) ;
+static int uty_cli_word_forwards(vty_io vio) ;
+static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
+static int uty_cli_word_backwards_pure (vty_io vio) ;
+static int uty_cli_word_backwards (vty_io vio) ;
+static int uty_cli_del_word_forwards(vty_io vio) ;
+static int uty_cli_del_word_backwards(vty_io vio) ;
+static int uty_cli_del_to_eol (vty_io vio) ;
+static int uty_cli_clear_line(vty_io vio) ;
+static int uty_cli_transpose_chars(vty_io vio) ;
+static void uty_cli_history_use(vty_io vio, int step) ;
+static void uty_cli_next_line(vty_io vio) ;
+static void uty_cli_previous_line (vty_io vio) ;
+static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
+static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
+
+/*------------------------------------------------------------------------------
+ * Fetch next keystroke, reading from the file if required.
+ */
+static inline bool
+uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+{
+ if (keystroke_get(vio->key_stream, stroke))
+ return 1 ;
+
+ uty_read(vio, NULL) ; /* not stealing */
+
+ return keystroke_get(vio->key_stream, stroke) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process keystrokes until run out of input, or get something to cli_do.
+ *
+ * If required, draw the prompt and command line.
+ *
+ * Process keystrokes until run out of stuff to do, or have a "command line"
+ * that must now be executed.
+ *
+ * Processes the contents of the keystroke stream. If exhausts that, will set
+ * ready to read and return. (To give some "sharing".)
+ *
+ * Returns: cli_do_xxxx
+ *
+ * When returns the cl is '\0' terminated.
+ */
+static enum cli_do
+uty_cli_process(vty_io vio, enum node_type node)
+{
+ struct keystroke stroke ;
+ uint8_t u ;
+ int auth ;
+ enum cli_do ret ;
+
+ auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ /* Now process as much as possible of what there is */
+ ret = cli_do_nothing ;
+ while (1)
+ {
+ if (!vio->cli_drawn)
+ uty_cli_draw_this(vio, node) ;
+
+ if (!uty_cli_get_keystroke(vio, &stroke))
+ {
+ ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ break ;
+ } ;
+
+ if (stroke.flags != 0)
+ {
+ /* TODO: deal with broken keystrokes */
+ continue ;
+ } ;
+
+ switch (stroke.type)
+ {
+ /* Straightforward character -----------------------------------*/
+ /* Note: only interested in 8-bit characters ! */
+ case ks_char:
+ u = (uint8_t)stroke.value ;
+
+ switch (stroke.value)
+ {
+ case CONTROL('A'):
+ uty_cli_bol (vio);
+ break;
+
+ case CONTROL('B'):
+ uty_cli_backwards(vio, 1);
+ break;
+
+ case CONTROL('C'):
+ ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
+ break ;
+
+ case CONTROL('D'):
+ if (vio->cl.len == 0) /* if at start of empty line */
+ ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
+ else
+ uty_cli_del_forwards(vio, 1);
+ break;
+
+ case CONTROL('E'):
+ uty_cli_eol (vio);
+ break;
+
+ case CONTROL('F'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_del_backwards(vio, 1);
+ break;
+
+ case CONTROL('K'):
+ uty_cli_del_to_eol (vio);
+ break;
+
+ case CONTROL('N'):
+ uty_cli_next_line (vio);
+ break;
+
+ case CONTROL('P'):
+ uty_cli_previous_line (vio);
+ break;
+
+ case CONTROL('T'):
+ uty_cli_transpose_chars (vio);
+ break;
+
+ case CONTROL('U'):
+ uty_cli_clear_line(vio);
+ break;
+
+ case CONTROL('W'):
+ uty_cli_del_word_backwards (vio);
+ break;
+
+ case CONTROL('Z'):
+ ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
+ break;
+
+ case '\n':
+ case '\r':
+ ret = cli_do_command ; /* Exit on CR or LF.............*/
+ break ;
+
+ case '\t':
+ if (auth)
+ break ;
+ else
+ uty_cli_complete_command (vio, node);
+ break;
+
+ case '?':
+ if (auth)
+ uty_cli_insert (vio, (char*)&u, 1);
+ else
+ uty_cli_describe_command (vio, node);
+ break;
+
+ default:
+ if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
+ uty_cli_insert (vio, (char*)&u, 1) ;
+ break;
+ }
+ break ;
+
+ /* ESC X -------------------------------------------------------------*/
+ case ks_esc:
+ switch (stroke.value)
+ {
+ case 'b':
+ uty_cli_word_backwards (vio);
+ break;
+
+ case 'f':
+ uty_cli_word_forwards (vio);
+ break;
+
+ case 'd':
+ uty_cli_del_word_forwards (vio);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_del_word_backwards (vio);
+ break;
+
+ default:
+ break;
+ } ;
+ break ;
+
+ /* ESC [ X -----------------------------------------------------------*/
+ case ks_csi:
+ if (stroke.len != 0)
+ break ; /* only recognise 3 byte sequences */
+
+ switch (stroke.value)
+ {
+ case ('A'):
+ uty_cli_previous_line (vio);
+ break;
+
+ case ('B'):
+ uty_cli_next_line (vio);
+ break;
+
+ case ('C'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case ('D'):
+ uty_cli_backwards(vio, 1);
+ break;
+ default:
+ break ;
+ } ;
+ break ;
+
+ /* Telnet Command ----------------------------------------------------*/
+ case ks_iac:
+ uty_telnet_command(vio, &stroke, false) ;
+ break ;
+
+ /* Unknown -----------------------------------------------------------*/
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+
+ /* After each keystroke..... */
+
+ if (ret != cli_do_nothing)
+ {
+ uty_cli_eol (vio) ; /* go to the end of the line */
+ break ; /* stop processing */
+ } ;
+ } ;
+
+ /* Tidy up and return where got to. */
+
+ qs_term(&vio->cl) ; /* add '\0' */
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ * Command line operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' characters at current position in the command line
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_insert (vty_io vio, const char* chars, int n)
+{
+ int after ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+
+ if (n <= 0)
+ return n ; /* avoid trouble */
+
+ after = qs_insert(&vio->cl, chars, n) ;
+
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+
+ if (after != 0)
+ uty_cli_echo_n(vio, telnet_backspaces, after) ;
+
+ vio->cl.cp += n ;
+
+ return n ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Overstrike 'n' characters at current position in the command line
+ *
+ * Move current position forwards.
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_overwrite (vty_io vio, char* chars, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+
+ if (n > 0)
+ {
+ qs_replace(&vio->cl, chars, n) ;
+ uty_cli_echo(vio, chars, n) ;
+
+ vio->cl.cp += n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Insert a word into vty interface with overwrite mode.
+ *
+ * NB: Assumes result will then be the end of the line.
+ *
+ * Returns number of characters inserted -- ie length of string
+ */
+static int
+uty_cli_word_overwrite (vty_io vio, char *str)
+{
+ int n ;
+ VTY_ASSERT_LOCKED() ;
+
+ n = uty_cli_overwrite(vio, str, strlen(str)) ;
+
+ vio->cl.len = vio->cl.cp ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Forward 'n' characters -- stop at end of line.
+ *
+ * Returns number of characters actually moved
+ */
+static int
+uty_cli_forwards(vty_io vio, int n)
+{
+ int have ;
+ VTY_ASSERT_LOCKED() ;
+
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ;
+
+ assert(n >= 0) ;
+
+ if (n > 0)
+ {
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
+ vio->cl.cp += n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Backwards 'n' characters -- stop at start of line.
+ *
+ * Returns number of characters actually moved
+ */
+static int
+uty_cli_backwards(vty_io vio, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if ((int)vio->cl.cp < n)
+ n = vio->cl.cp ;
+
+ assert(n >= 0) ;
+
+ if (n > 0)
+ {
+ uty_cli_echo_n(vio, telnet_backspaces, n) ;
+ vio->cl.cp -= n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete 'n' characters -- forwards -- stop at end of line.
+ *
+ * Returns number of characters actually deleted.
+ */
+static int
+uty_cli_del_forwards(vty_io vio, int n)
+{
+ int after ;
+ int have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ; /* cannot delete more than have */
+
+ assert(n >= 0) ;
+
+ if (n <= 0)
+ return 0 ;
+
+ after = qs_delete(&vio->cl, n) ;
+
+ if (after > 0)
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+
+ uty_cli_echo_n(vio, telnet_spaces, n) ;
+ uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete 'n' characters before the point.
+ *
+ * Returns number of characters actually deleted.
+ */
+static int
+uty_cli_del_backwards(vty_io vio, int n)
+{
+ return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Move to the beginning of the line.
+ *
+ * Returns number of characters moved over.
+ */
+static int
+uty_cli_bol (vty_io vio)
+{
+ return uty_cli_backwards(vio, vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move to the end of the line.
+ *
+ * Returns number of characters moved over
+ */
+static int
+uty_cli_eol (vty_io vio)
+{
+ return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Forward word delta -- distance to start of next word.
+ *
+ * Return number of characters to step over to reach next word.
+ *
+ * Steps over non-space characters and then any spaces.
+ */
+static int
+uty_cli_word_forwards_delta(vty_io vio)
+{
+ char* cp ;
+ char* tp ;
+ char* ep ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ ep = qs_ep_char(&vio->cl) ;
+
+ tp = cp ;
+
+ while ((tp < ep) && (*tp != ' '))
+ ++tp ;
+
+ while ((tp < ep) && (*tp == ' '))
+ ++tp ;
+
+ return tp - cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Forward word -- move to start of next word.
+ *
+ * Moves past any non-spaces, then past any spaces.
+ */
+static int
+uty_cli_word_forwards(vty_io vio)
+{
+ return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word delta -- distance to start of next word, back.
+ *
+ * Return number of characters to step over to reach next word.
+ *
+ * If "eat_spaces", starts by stepping over spaces.
+ * Steps back until next (backwards) character is space, or hits start of line.
+ */
+static int
+uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
+{
+ char* cp ;
+ char* tp ;
+ char* sp ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ sp = qs_chars(&vio->cl) ;
+
+ tp = cp ;
+
+ if (eat_spaces)
+ while ((tp > sp) && (*(tp - 1) == ' '))
+ --tp ;
+
+ while ((tp > sp) && (*(tp - 1) != ' '))
+ --tp ;
+
+ return cp - tp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word, but not trailing spaces.
+ *
+ * Move back until next (backwards) character is space or start of line.
+ *
+ * Returns number of characters stepped over.
+ */
+static int
+uty_cli_word_backwards_pure (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Backward word -- move to start of previous word.
+ *
+ * Moves past any spaces, then move back until next (backwards) character is
+ * space or start of line.
+ *
+ * Returns number of characters stepped over.
+ */
+static int
+uty_cli_word_backwards (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete to end of word -- forwards.
+ *
+ * Deletes any leading spaces, then deletes upto next space or end of line.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_word_forwards(vty_io vio)
+{
+ return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete to start of word -- backwards.
+ *
+ * Deletes any trailing spaces, then deletes upto next space or start of line.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_word_backwards(vty_io vio)
+{
+ return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill rest of line from current point.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_to_eol (vty_io vio)
+{
+ return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill line from the beginning.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_clear_line(vty_io vio)
+{
+ uty_cli_bol(vio) ;
+ return uty_cli_del_to_eol(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Transpose chars before or at the point.
+ *
+ * Return number of characters affected.
+ */
+static int
+uty_cli_transpose_chars(vty_io vio)
+{
+ char chars[2] ;
+ char* cp ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Give up if < 2 characters or at start of line. */
+ if ((vio->cl.len < 2) || (vio->cl.cp < 1))
+ return 0 ;
+
+ /* Move back to first of characters to exchange */
+ if (vio->cl.cp == vio->cl.len)
+ uty_cli_backwards(vio, 2) ;
+ else
+ uty_cli_backwards(vio, 1) ;
+
+ /* Pick up in the new order */
+ cp = qs_cp_char(&vio->cl) ;
+ chars[1] = *cp++ ;
+ chars[0] = *cp ;
+
+ /* And overwrite */
+ return uty_cli_overwrite(vio, chars, 2) ;
+} ;
+
+/*==============================================================================
+ * Command line history handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Add given command line to the history buffer.
+ *
+ * This is inserting the vty->buf line into the history.
+ */
+extern void
+uty_cli_hist_add (vty_io vio, const char* cmd_line)
+{
+ qstring prev_line ;
+ qstring_t line ;
+ int prev_index ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Construct a dummy qstring for the given command line */
+ qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
+
+ /* make sure have a suitable history vector */
+ vector_set_min_length(vio->hist, VTY_MAXHIST) ;
+
+ /* find the previous command line in the history */
+ prev_index = vio->hindex - 1 ;
+ if (prev_index < 0)
+ prev_index = VTY_MAXHIST - 1 ;
+
+ prev_line = vector_get_item(vio->hist, prev_index) ;
+
+ /* If the previous line is NULL, that means the history is empty.
+ *
+ * If the previous line is essentially the same as the current line,
+ * replace it with the current line -- so that the latest whitespace
+ * version is saved.
+ *
+ * Either way, replace the the previous line entry by moving hindex
+ * back !
+ */
+ if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
+ vio->hindex = prev_index ;
+ else
+ prev_line = vector_get_item(vio->hist, vio->hindex) ;
+
+ /* Now replace the hindex entry */
+ vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+
+ /* Advance to the near future and reset the history pointer */
+ vio->hindex++;
+ if (vio->hindex == VTY_MAXHIST)
+ vio->hindex = 0;
+
+ vio->hp = vio->hindex;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace command line by current history.
+ *
+ * This function is called from vty_next_line and vty_previous_line.
+ *
+ * Step +1 is towards the present
+ * -1 is into the past
+ */
+static void
+uty_cli_history_use(vty_io vio, int step)
+{
+ int index ;
+ unsigned old_len ;
+ unsigned after ;
+ unsigned back ;
+ qstring hist ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((step == +1) || (step == -1)) ;
+
+ index = vio->hp ;
+
+ /* Special case of being at the insertion point */
+ if (index == vio->hindex)
+ {
+ if (step > 0)
+ return ; /* already in the present */
+
+ /* before stepping back from the present, take a copy of the
+ * current command line -- so can get back to it.
+ */
+ hist = vector_get_item(&vio->hist, vio->hindex) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ } ;
+
+ /* Advance or retreat */
+ index += step ;
+ if (index < 0)
+ index = VTY_MAXHIST - 1 ;
+ else if (index >= VTY_MAXHIST)
+ index = 0 ;
+
+ hist = vector_get_item(vio->hist, index) ;
+
+ /* If moving backwards in time, may not move back to the insertion
+ * point (that would be wrapping round to the present) and may not
+ * move back to a NULL entry (that would be going back before '.').
+ */
+ if (step < 0)
+ if ((hist == NULL) || (index == vio->hindex))
+ return ;
+
+ /* Now, if arrived at the insertion point, this is returning to the
+ * present, which is fine.
+ */
+ vio->hp = index;
+
+ /* Move back to the start of the current line */
+ uty_cli_bol(vio) ;
+
+ /* Get previous line from history buffer and echo that */
+ old_len = vio->cl.len ;
+ qs_copy(&vio->cl, hist) ;
+
+ /* Sort out wiping out any excess and setting the cursor position */
+ if (old_len > vio->cl.len)
+ after = old_len - vio->cl.len ;
+ else
+ after = 0 ;
+
+ back = after ;
+ if (vio->cl.len > vio->cl.cp)
+ back += (vio->cl.len - vio->cl.cp) ;
+
+ if (vio->cl.len > 0)
+ uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+
+ if (after > 0)
+ uty_cli_echo_n(vio, telnet_spaces, after) ;
+
+ if (back > 0)
+ uty_cli_echo_n(vio, telnet_backspaces, back) ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Use next history line, if any.
+ */
+static void
+uty_cli_next_line(vty_io vio)
+{
+ uty_cli_history_use(vio, +1) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Use previous history line, if any.
+ */
+static void
+uty_cli_previous_line (vty_io vio)
+{
+ uty_cli_history_use(vio, -1) ;
+}
+
+/*==============================================================================
+ * Command Completion and Command Description
+ *
+ */
+static void uty_cli_describe_show(vty_io vio, vector describe) ;
+static void uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc) ;
+static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str) ;
+
+static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
+
+/*------------------------------------------------------------------------------
+ * Command completion
+ */
+static void
+uty_cli_complete_command (vty_io vio, enum node_type node)
+{
+ unsigned i ;
+ int ret ;
+ int len ;
+ int n ;
+ vector matched ;
+ vector vline ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ matched = cmd_complete_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ /* Show the result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ case CMD_COMPLETE_FULL_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ uty_cli_insert(vio, " ", 1);
+ break ;
+
+ case CMD_COMPLETE_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ break ;
+
+ case CMD_COMPLETE_LIST_MATCH:
+ len = 6 ;
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ int sl = strlen((char*)vector_get_item(matched, i)) ;
+ if (len < sl)
+ len = sl ;
+ } ;
+
+ n = vio->width ;
+ if (n == 0)
+ n = 60 ;
+ n = n / (len + 2) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ if ((i % n) == 0)
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
+ }
+ uty_cli_out_newline(vio) ;
+
+ break;
+
+ case CMD_COMPLETE_ALREADY:
+ default:
+ break;
+ } ;
+
+ cmd_free_strvec(matched);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command Description
+ */
+static void
+uty_cli_describe_command (vty_io vio, enum node_type node)
+{
+ int ret;
+ vector vline;
+ vector describe;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ describe = cmd_describe_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ uty_cli_out_newline(vio); /* clears cli_drawn */
+
+ /* Deal with result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ default:
+ uty_cli_describe_show(vio, describe) ;
+ break ;
+ } ;
+
+ if (describe != NULL)
+ vector_free (describe);
+}
+
+/*------------------------------------------------------------------------------
+ * Show the command description.
+ *
+ * Generates lines of the form:
+ *
+ * word description text
+ *
+ * Where the word field is adjusted to suit the longest word, and the
+ * description text is wrapped if required (if the width of the console is
+ * known) so that get:
+ *
+ * word description ..................................
+ * .............text
+ *
+ * If one of the options is '<cr>', that is always shown last.
+ */
+static void
+uty_cli_describe_show(vty_io vio, vector describe)
+{
+ unsigned int i, cmd_width, desc_width;
+ struct desc *desc, *desc_cr ;
+
+ /* Get width of the longest "word" */
+ cmd_width = 0;
+ for (i = 0; i < vector_active (describe); i++)
+ if ((desc = vector_slot (describe, i)) != NULL)
+ {
+ unsigned int len;
+
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ len = strlen (desc->cmd);
+ if (desc->cmd[0] == '.')
+ len--;
+
+ if (cmd_width < len)
+ cmd_width = len;
+ }
+
+ /* Set width of description string. */
+ desc_width = vio->width - (cmd_width + 6);
+
+ /* Print out description. */
+ desc_cr = NULL ; /* put <cr> last if it appears */
+
+ for (i = 0; i < vector_active (describe); i++)
+ if ((desc = vector_slot (describe, i)) != NULL)
+ {
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ if (strcmp (desc->cmd, command_cr) == 0)
+ {
+ desc_cr = desc;
+ continue;
+ }
+
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
+ }
+
+ if (desc_cr != NULL)
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one word and the description, folding the description as required.
+ */
+static void
+uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc)
+{
+ char *buf;
+ const char *cmd, *p;
+ int pos;
+
+ VTY_ASSERT_LOCKED() ;
+
+ cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+ p = desc->str ;
+
+ /* If have a sensible description width */
+ if (desc_width > 20)
+ {
+ buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+
+ while (strlen (p) > desc_width)
+ {
+ /* move back to first space */
+ for (pos = desc_width; pos > 0; pos--)
+ if (*(p + pos) == ' ')
+ break;
+
+ /* if did not find a space, break at width */
+ if (pos == 0)
+ pos = desc_width ;
+
+ strncpy (buf, p, pos);
+ buf[pos] = '\0';
+ uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+
+ cmd = ""; /* for 2nd and subsequent lines */
+
+ p += pos ; /* step past what just wrote */
+ while (*p == ' ')
+ ++p ; /* skip spaces */
+ } ;
+
+ XFREE (MTYPE_TMP, buf);
+ } ;
+
+ uty_cli_describe_line(vio, cmd_width, cmd, p) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one description line.
+ */
+static void
+uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str)
+{
+ if (str != NULL)
+ uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
+ else
+ uty_cli_out (vio, " %-s", cmd) ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare "vline" token array for command handler.
+ *
+ * For "help" (command completion/description), if the command line is empty,
+ * or ends in ' ', adds an empty token to the end of the token array.
+ */
+static vector
+uty_cli_cmd_prepare(vty_io vio, int help)
+{
+ vector vline ;
+
+ vline = cmd_make_strvec(qs_term(&vio->cl)) ;
+
+ /* Note that if there is a vector of tokens, then there is at least one
+ * token, so can guarantee that vio->cl.len >= 1 !
+ */
+ if (help)
+ if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
+ vline = cmd_add_to_strvec(vline, "") ;
+
+ return vline ;
+} ;
+
+/*==============================================================================
+ * VTY telnet stuff
+ */
+
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+static void
+uty_will_echo (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+static void
+uty_will_suppress_go_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+static void
+uty_dont_linemode (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+static void
+uty_do_window_size (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+static void
+uty_dont_lflow_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * The keystroke iac callback function.
+ *
+ * This deals with IAC sequences that should be dealt with as soon as they
+ * are read -- not stored in the keystroke stream for later processing.
+ */
+extern bool
+uty_cli_iac_callback(keystroke_iac_callback_args)
+{
+ return uty_telnet_command((vty_io)context, stroke, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process incoming Telnet Option(s)
+ *
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
+ * In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
+ */
+static bool
+uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
+{
+ uint8_t* p ;
+ uint8_t o ;
+ int left ;
+ bool dealt_with ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_wipe(vio, 0) ;
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(vio, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(vio, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (stroke->flags & kf_broken)
+ uty_cli_out (vio, "BROKEN") ;
+
+ uty_cli_out (vio, "\r\n") ;
+ } ;
+
+ /* Process the telnet command */
+ dealt_with = false ;
+
+ if (stroke->flags != 0)
+ return dealt_with ; /* go no further if broken */
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ passert(left >= 1) ; /* must be if not broken ! */
+ passert(stroke->value == *p) ; /* or something is wrong */
+
+ ++p ; /* step past X of IAC X */
+ --left ;
+
+ /* Decode the one command that is interesting -- "NAWS" */
+ switch (stroke->value)
+ {
+ case tn_SB:
+ passert(left > 0) ; /* or parser failed */
+
+ o = *p++ ; /* the option byte */
+ --left ;
+ switch(o)
+ {
+ case to_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ vio->width = *p++ << 8 ;
+ vio->width += *p++ ;
+ vio->height = *p++ << 8 ;
+ vio->height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ uty_cli_out(vio, "TELNET NAWS window size received: "
+ "width %d, height %d%s",
+ vio->width, vio->height, telnet_newline) ;
+ uty_set_height(vio) ;
+
+ dealt_with = true ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+
+ return dealt_with ;
+} ;
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 9ff7cdb5..34e68cd8 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -24,9 +24,7 @@
#ifndef _QUAGGA_WORK_QUEUE_H
#define _QUAGGA_WORK_QUEUE_H
-#ifndef Inline
-#define Inline static inline
-#endif
+#include "misc.h"
/* Hold time for the initial schedule of a queue run, in millisec */
#define WORK_QUEUE_DEFAULT_HOLD 50
diff --git a/lib/zassert.h b/lib/zassert.h
index 8ca2203f..9f2465f7 100644
--- a/lib/zassert.h
+++ b/lib/zassert.h
@@ -33,7 +33,7 @@ extern void _zlog_abort_err (const char *mess, int err, const char *file,
#define zassert(EX) ((void)((EX) ? 0 : \
(_zlog_assert_failed(#EX, __FILE__, __LINE__, \
- __ASSERT_FUNCTION), 0)))
+ __ASSERT_FUNCTION), 0)))
/* Implicitly *permanent* assert() -- irrespective of NDEBUG */
#undef assert
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
index fc81a562..a3c6ad59 100644
--- a/tests/test-list_util.c
+++ b/tests/test-list_util.c
@@ -20,10 +20,10 @@ static void test_ddl(void);
} while (0)
static void
-test_assert_fail(const char* true, const char* message, const char* func,
+test_assert_fail(const char* truth, const char* message, const char* func,
int line)
{
- printf("*** %s %d: (%s) not true: %s\n", func, line, true, message) ;
+ printf("*** %s %d: (%s) not true: %s\n", func, line, truth, message) ;
} ;
diff --git a/tests/test-symtab.c b/tests/test-symtab.c
index ed83e607..a4b0bcf2 100644
--- a/tests/test-symtab.c
+++ b/tests/test-symtab.c
@@ -56,24 +56,25 @@ test_symbol_table_init_new(void)
assert_true(table != NULL, "table == NULL");
/* expect to not find */
- sym = symbol_lookup(table, name, 0);
+ sym = symbol_lookup(table, name, no_add);
assert_true(sym == NULL, "sym != NULL");
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
assert_true(sym != NULL, "sym == NULL");
- assert_true(strcmp(symbol_get_name(sym), name) == 0, "strcmp(symbol_get_name(sym), name) != 0");
+ assert_true(strcmp(symbol_get_name(sym), name) == 0,
+ "strcmp(symbol_get_name(sym), name) != 0");
/* find */
- sym2 = symbol_lookup(table, name, 0);
+ sym2 = symbol_lookup(table, name, no_add);
assert_true(sym == sym2, "sym != sym2");
assert_true(symbol_get_value(sym) == value, "symbol_get_value(sym) != value");
old_value = symbol_delete(sym);
assert_true(value == old_value, "value != old_value");
- while ((old_value = symbol_table_ream(table, 1)) != NULL)
+ while ((old_value = symbol_table_ream(table, keep_it)) != NULL)
{
}
@@ -98,7 +99,7 @@ test_symbol_table_lookup(void)
for (i = 0; i < len; ++i)
{
sprintf(buf, "%d-name", i);
- sym = symbol_lookup(table, buf, 1);
+ sym = symbol_lookup(table, buf, add);
assert_true(sym != NULL, "add: sym == NULL");
assert_true(strcmp(symbol_get_name(sym), buf) == 0,
"strcmp(symbol_get_name(sym), buf) != 0");
@@ -114,7 +115,7 @@ test_symbol_table_lookup(void)
for (i = 0; i < len; ++i)
{
sprintf(buf, "%d-name", i);
- sym = symbol_lookup(table, buf, 0);
+ sym = symbol_lookup(table, buf, no_add);
assert_true(sym != NULL, "find: sym == NULL");
assert_true(strcmp(symbol_get_name(sym), buf) == 0,
"strcmp(symbol_get_name(sym), buf) != 0");
@@ -162,12 +163,12 @@ test_call_back(void)
/* add */
symbol_table_set_value_call_back(table, call_back_function_set);
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* change */
symbol_table_set_value_call_back(table, call_back_function_change);
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, new_value);
/* delete */
@@ -216,7 +217,7 @@ test_ref(void)
table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* create references, in reverse order so that walk in order */
@@ -272,7 +273,7 @@ test_ref_heavy(void)
table = symbol_table_init_new(table, NULL, 0, 0, NULL, NULL);
/* add */
- sym = symbol_lookup(table, name, 1);
+ sym = symbol_lookup(table, name, add);
symbol_set_value(sym, value);
/* create references, in reverse order so that walk in order */
diff --git a/tests/test-vector.c b/tests/test-vector.c
index b54ae9f9..a73e5637 100644
--- a/tests/test-vector.c
+++ b/tests/test-vector.c
@@ -424,12 +424,12 @@ void
do_test_insert(const int rider)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index ins = 50;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t ins = 50;
+ vector_index_t i;
char buf[10];
- vector_index check_end = len + 1;
- vector_index check_ins = ins;
+ vector_length_t check_end = len + 1;
+ vector_index_t check_ins = ins;
int check_shift = 1;
switch(rider)
@@ -563,9 +563,9 @@ test_vector_bsearch(void)
const int len = 2000;
char buf[20];
char target[20];
- vector_index target_index = 0;
+ vector_index_t target_index = 0;
int result;
- vector_index index;
+ vector_index_t index;
printf("test_vector_bsearch\n");
@@ -614,14 +614,14 @@ void
do_test_move_item_here(const int rider)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index ins = 50;
- const vector_index src = 70;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t ins = 50;
+ const vector_index_t src = 70;
+ vector_index_t i;
char buf[10];
- vector_index check_dest = 0;
- vector_index check_src = 0;
- vector_index check_end = 0;
+ vector_index_t check_dest = 0;
+ vector_index_t check_src = 0;
+ vector_index_t check_end = 0;
int check_shift = 0;
p_vector_item dest_item = NULL;
@@ -711,10 +711,10 @@ void
test_vector_part_reverse(void)
{
vector v = NULL;
- const vector_index len = 100;
- const vector_index rstart = 50;
- const vector_index rstop = 70;
- vector_index i;
+ const vector_length_t len = 100;
+ const vector_index_t rstart = 50;
+ const vector_index_t rstop = 70;
+ vector_index_t i;
char buf[10];
printf("test_vector_part_reverse\n");
@@ -770,8 +770,8 @@ test_vector_copy_here(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_copy_here\n");
@@ -809,8 +809,8 @@ test_vector_move_here(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_move_here\n");
@@ -851,8 +851,8 @@ test_vector_copy_append(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_copy_append\n");
@@ -900,8 +900,8 @@ test_vector_move_append(void)
{
vector v1 = NULL;
vector v2 = NULL;
- vector_index i;
- const vector_index len = 100;
+ vector_index_t i;
+ const vector_length_t len = 100;
char buf[10];
printf("test_vector_move_append\n");
@@ -949,10 +949,10 @@ void
test_vector_insert(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index istart = 50;
- const vector_index istop = 70;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t istart = 50;
+ const vector_index_t istop = 70;
char buf[10];
printf("test_vector_insert\n");
@@ -1004,10 +1004,10 @@ void
test_vector_delete(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index dstart = 50;
- const vector_index dstop = 70;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t dstart = 50;
+ const vector_index_t dstop = 70;
char buf[10];
printf("test_vector_delete\n");
@@ -1060,9 +1060,9 @@ void
test_vector_discard(void)
{
vector v = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index dstart = 50;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t dstart = 50;
char buf[10];
printf("test_vector_discard\n");
@@ -1110,12 +1110,12 @@ test_vector_sak(void)
vector v1 = NULL;
vector v2 = NULL;
vector v3 = NULL;
- vector_index i;
- const vector_index len = 100;
- const vector_index sstart = 60;
- const vector_index sstop = 70;
- const vector_index dstart = 40;
- const vector_index dstop = 50;
+ vector_index_t i;
+ const vector_length_t len = 100;
+ const vector_index_t sstart = 60;
+ const vector_index_t sstop = 70;
+ const vector_index_t dstart = 40;
+ const vector_index_t dstop = 50;
char buf[10];
printf("test_vector_sak\n");
@@ -1132,7 +1132,7 @@ test_vector_sak(void)
}
v1 = vector_sak(1, v1, v2, dstart, dstop - dstart,
- v3, sstart, sstop - sstart, 0);
+ v3, sstart, sstop - sstart, 0);
assert_true(v1 != NULL, "v1 == NULL");
assert_true(vector_end(v1) == (dstop - dstart),
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 64c1b549..3692a953 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -31,13 +31,14 @@
#include <readline/history.h>
#include "command.h"
+#include "command_execute.h"
#include "memory.h"
#include "vtysh/vtysh.h"
#include "log.h"
#include "bgpd/bgp_vty.h"
/* Struct VTY. */
-struct vty *vty;
+static struct vty* vtysh_vty;
/* VTY shell pager name. */
char *vtysh_pager_name = NULL;
@@ -51,13 +52,13 @@ struct vtysh_client
const char *path;
} vtysh_client[] =
{
- { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH},
- { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH},
+ { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH},
+ { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH},
{ .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH},
- { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH},
+ { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH},
{ .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH},
- { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
- { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
+ { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
+ { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
};
#define VTYSH_INDEX_MAX (sizeof(vtysh_client)/sizeof(vtysh_client[0]))
@@ -250,7 +251,7 @@ vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp)
}
}
-void
+static void
vtysh_exit_ripd_only (void)
{
if (ripd_client)
@@ -284,27 +285,27 @@ vtysh_execute_func (const char *line, int pager)
int saved_ret, saved_node;
/* TODO: how well does vtysh_execute_func work ?? -- esp. qpthreads_enabled */
- vty->buf = line ;
+ vtysh_vty->buf = line ;
- saved_ret = ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
+ saved_ret = ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
if ((ret == CMD_SUCCESS) && (cmd == NULL))
return ret ; /* quit if nothing to do ??? */
- saved_node = vty->node;
+ saved_node = vtysh_vty->node;
/* If command doesn't succeeded in current node, try to walk up in node tree.
- * Changing vty->node is enough to try it just out without actual walkup in
- * the vtysh. */
+ * Changing vtysh_vty->node is enough to try it just out without actual
+ * walkup in the vtysh. */
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING
- && vty->node > CONFIG_NODE)
+ && vtysh_vty->node > CONFIG_NODE)
{
- vty->node = node_parent(vty->node);
- ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
+ vtysh_vty->node = node_parent(vtysh_vty->node);
+ ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
tried++;
}
- vty->node = saved_node;
+ vtysh_vty->node = saved_node;
/* If command succeeded in any other node than current (tried > 0) we have
* to move into node in the vtysh where it succeeded. */
@@ -338,7 +339,7 @@ vtysh_execute_func (const char *line, int pager)
switch (ret)
{
case CMD_WARNING:
- if (vty->type == VTY_FILE)
+ if (vtysh_vty->type == VTY_FILE)
fprintf (stdout,"Warning...\n");
break;
case CMD_ERR_AMBIGUOUS:
@@ -382,16 +383,16 @@ vtysh_execute_func (const char *line, int pager)
{
line = "end";
- vty->buf = line ;
+ vtysh_vty->buf = line ;
- ret = cmd_execute_command (vty, cmd_parse_completion, &cmd);
+ ret = cmd_execute_command (vtysh_vty, cmd_parse_completion, &cmd);
if (ret != CMD_SUCCESS_DAEMON)
break;
}
else
if (cmd->func)
{
- (*cmd->func) (cmd, vty, 0, NULL);
+ (*cmd->func) (cmd, vtysh_vty, 0, NULL);
break;
}
}
@@ -410,7 +411,7 @@ vtysh_execute_func (const char *line, int pager)
break;
if (cmd->func)
- (*cmd->func) (cmd, vty, 0, NULL);
+ (*cmd->func) (cmd, vtysh_vty, 0, NULL);
}
}
if (pager && vtysh_pager_name && fp && closepager)
@@ -524,7 +525,7 @@ vtysh_config_from_file (struct vty *vty, FILE *fp)
}
/* We don't care about the point of the cursor when '?' is typed. */
-int
+static int
vtysh_rl_describe (void)
{
int ret;
@@ -546,7 +547,7 @@ vtysh_rl_describe (void)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- describe = cmd_describe_command (vline, vty->node, &ret);
+ describe = cmd_describe_command (vline, vtysh_vty->node, &ret);
fprintf (stdout,"\n");
@@ -626,7 +627,7 @@ command_generator (const char *text, int state)
{
index = 0;
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ if (vtysh_vty->node == AUTH_NODE || vtysh_vty->node == AUTH_ENABLE_NODE)
return NULL;
vline = cmd_make_strvec (rl_line_buffer);
@@ -636,7 +637,7 @@ command_generator (const char *text, int state)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty->node, &complete_status);
+ matched = cmd_complete_command (vline, vtysh_vty->node, &complete_status);
}
if (matched && matched[index])
@@ -671,7 +672,7 @@ vtysh_completion (char *text, int start, int end)
vector vline;
char **matched = NULL;
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ if (vtysh_vty->node == AUTH_NODE || vtysh_vty->node == AUTH_ENABLE_NODE)
return NULL;
vline = cmd_make_strvec (rl_line_buffer);
@@ -682,7 +683,7 @@ vtysh_completion (char *text, int start, int end)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty, &ret);
+ matched = cmd_complete_command (vline, vtysh_vty, &ret);
cmd_free_strvec (vline);
@@ -791,17 +792,17 @@ static struct cmd_node keychain_key_node =
extern struct cmd_node vty_node;
/* When '^Z' is received from vty, move down to the enable mode. */
-int
+static int
vtysh_end (void)
{
- switch (vty->node)
+ switch (vtysh_vty->node)
{
case VIEW_NODE:
case ENABLE_NODE:
/* Nothing to do. */
break;
default:
- vty->node = ENABLE_NODE;
+ vtysh_vty->node = ENABLE_NODE;
break;
}
return CMD_SUCCESS;
@@ -2196,17 +2197,17 @@ vtysh_prompt (void)
hostname = names.nodename;
}
- snprintf (buf, sizeof buf, cmd_prompt (vty->node), hostname);
+ snprintf (buf, sizeof buf, cmd_prompt (vtysh_vty->node), hostname);
return buf;
}
-void
+struct vty*
vtysh_init_vty (void)
{
- /* Make vty structure. */
- vty = vty_open(VTY_SHELL);
- vty->node = VIEW_NODE;
+ /* Make vtysh_vty structure. */
+ vtysh_vty = vty_open(VTY_SHELL);
+ vtysh_vty->node = VIEW_NODE;
/* Initialize commands. */
cmd_init (0);
@@ -2437,4 +2438,5 @@ vtysh_init_vty (void)
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
+ return vtysh_vty ;
}
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index e711d593..820e5506 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -36,7 +36,7 @@
/* vtysh local configuration file. */
#define VTYSH_DEFAULT_CONFIG "vtysh.conf"
-void vtysh_init_vty (void);
+struct vty* vtysh_init_vty (void);
void vtysh_init_cmd (void);
extern int vtysh_connect_all (const char *optional_daemon_name);
void vtysh_readline_init (void);
@@ -64,6 +64,4 @@ void vtysh_pager_init (void);
/* Child process execution flag. */
extern int execute_flag;
-extern struct vty *vty;
-
#endif /* VTYSH_H */
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 15938f43..dfdc020a 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -47,19 +47,19 @@ struct config
struct list *config_top;
-int
-line_cmp (char *c1, char *c2)
+static int
+line_cmp (const char *c1, const char *c2)
{
return strcmp (c1, c2);
}
-void
+static void
line_del (char *line)
{
XFREE (MTYPE_VTYSH_CONFIG_LINE, line);
}
-struct config *
+static struct config *
config_new ()
{
struct config *config;
@@ -67,13 +67,13 @@ config_new ()
return config;
}
-int
+static int
config_cmp (struct config *c1, struct config *c2)
{
return strcmp (c1->name, c2->name);
}
-void
+static void
config_del (struct config* config)
{
list_delete (config->line);
@@ -82,7 +82,7 @@ config_del (struct config* config)
XFREE (MTYPE_VTYSH_CONFIG, config);
}
-struct config *
+static struct config *
config_get (int index, const char *line)
{
struct config *config;
@@ -121,13 +121,13 @@ config_get (int index, const char *line)
return config;
}
-void
+static void
config_add_line (struct list *config, const char *line)
{
listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
-void
+static void
config_add_line_uniq (struct list *config, const char *line)
{
struct listnode *node, *nnode;
@@ -141,7 +141,7 @@ config_add_line_uniq (struct list *config, const char *line)
listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line));
}
-void
+static void
vtysh_config_parse_line (const char *line)
{
char c;
@@ -365,7 +365,7 @@ vtysh_read_file (FILE *confp)
vtysh_execute_no_pager ("end");
vtysh_execute_no_pager ("disable");
- vty_close (vty);
+ vty_close_final(vty);
if (ret != CMD_SUCCESS)
{
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 4a315a5c..c576d3e0 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -63,7 +63,7 @@ struct thread_master *master;
FILE *logfile;
/* SIGTSTP handler. This function care user's ^Z input. */
-void
+static void
sigtstp (int sig)
{
/* Execute "end" command. */
@@ -84,7 +84,7 @@ sigtstp (int sig)
}
/* SIGINT handler. This function care user's ^Z input. */
-void
+static void
sigint (int sig)
{
/* Check this process is not child process. */
@@ -98,7 +98,7 @@ sigint (int sig)
/* Signale wrapper for vtysh. We don't use sigevent because
* vtysh doesn't use threads. TODO */
-RETSIGTYPE *
+static RETSIGTYPE *
vtysh_signal_set (int signo, void (*func)(int))
{
int ret;
@@ -121,7 +121,7 @@ vtysh_signal_set (int signo, void (*func)(int))
}
/* Initialization of signal handles. */
-void
+static void
vtysh_signal_init ()
{
vtysh_signal_set (SIGINT, sigint);
@@ -168,7 +168,7 @@ struct option longopts[] =
};
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
-char *
+static char *
vtysh_rl_gets ()
{
HIST_ENTRY *last;
@@ -202,7 +202,7 @@ static void log_it(const char *line)
{
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
- char *user = getenv("USER") ? : "boot";
+ const char *user = getenv("USER") ? : "boot";
char tod[64];
strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
@@ -214,6 +214,7 @@ static void log_it(const char *line)
int
main (int argc, char **argv, char **env)
{
+ struct vty* vty ;
char *p;
int opt;
int dryrun = 0;
@@ -292,7 +293,7 @@ main (int argc, char **argv, char **env)
vtysh_signal_init ();
/* Make vty structure and register commands. */
- vtysh_init_vty ();
+ vty = vtysh_init_vty ();
vtysh_init_cmd ();
vtysh_user_init ();
vtysh_config_init ();
diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c
index 58676c10..5dd50bb7 100644
--- a/vtysh/vtysh_user.c
+++ b/vtysh/vtysh_user.c
@@ -21,6 +21,7 @@
#include <zebra.h>
#include <lib/version.h>
+#include <vtysh_user.h>
#include <pwd.h>
@@ -98,19 +99,21 @@ struct vtysh_user
struct list *userlist;
-struct vtysh_user *
+static struct vtysh_user *
user_new ()
{
return XCALLOC (0, sizeof (struct vtysh_user));
}
-void
+static void user_free (struct vtysh_user *user) __attribute__((unused)) ;
+
+static void
user_free (struct vtysh_user *user)
{
XFREE (0, user);
}
-struct vtysh_user *
+static struct vtysh_user *
user_lookup (const char *name)
{
struct listnode *node, *nnode;
@@ -124,8 +127,10 @@ user_lookup (const char *name)
return NULL;
}
-void
-user_config_write ()
+static void user_config_write (void) __attribute__((unused)) ;
+
+static void
+user_config_write (void)
{
struct listnode *node, *nnode;
struct vtysh_user *user;
@@ -137,7 +142,7 @@ user_config_write ()
}
}
-struct vtysh_user *
+static struct vtysh_user *
user_get (const char *name)
{
struct vtysh_user *user;
@@ -165,8 +170,8 @@ DEFUN (username_nopassword,
return CMD_SUCCESS;
}
-int
-vtysh_auth ()
+extern int
+vtysh_auth(void)
{
struct vtysh_user *user;
struct passwd *passwd;
@@ -186,8 +191,8 @@ vtysh_auth ()
return 0;
}
-void
-vtysh_user_init ()
+extern void
+vtysh_user_init(void)
{
userlist = list_new ();
install_element (CONFIG_NODE, &username_nopassword_cmd);
diff --git a/vtysh/vtysh_user.h b/vtysh/vtysh_user.h
index 8d0a4cf6..c02cfede 100644
--- a/vtysh/vtysh_user.h
+++ b/vtysh/vtysh_user.h
@@ -22,6 +22,7 @@
#ifndef _VTYSH_USER_H
#define _VTYSH_USER_H
-int vtysh_auth ();
+extern int vtysh_auth(void) ;
+extern void vtysh_user_init(void) ;
#endif /* _VTYSH_USER_H */