VTY: Introduce common code to add 'description' to objects like BTS

There is now an option to add a human-readable description to objects
that are configured in the VTY.
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 91527c9..5cd43d7 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -322,6 +322,8 @@
 	struct gsm_bts *bts;
 	/* number of this TRX in the BTS */
 	u_int8_t nr;
+	/* human readable name / description */
+	char *description;
 	/* how do we talk RSL with this TRX? */
 	struct gsm_e1_subslot rsl_e1_link;
 	u_int8_t rsl_tei;
@@ -428,6 +430,8 @@
 	struct gsm_network *network;
 	/* number of ths BTS in network */
 	u_int8_t nr;
+	/* human readable name / description */
+	char *description;
 	/* Cell Identity */
 	u_int16_t cell_identity;
 	/* location area code of this BTS */
diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h
index f1b1148..55f5461 100644
--- a/openbsc/include/openbsc/vty.h
+++ b/openbsc/include/openbsc/vty.h
@@ -1,10 +1,18 @@
 #ifndef OPENBSC_VTY_H
 #define OPENBSC_VTY_H
 
+#include <vty/vty.h>
+#include <vty/buffer.h>
+
 struct gsm_network;
 struct vty;
 
 void openbsc_vty_add_cmds(void);
 void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
 
+struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
+
+extern struct cmd_element cfg_description_cmd;
+extern struct cmd_element cfg_no_description_cmd;
+
 #endif
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 2799706..8af9153 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -193,6 +193,8 @@
 		bts->cell_identity,
 		bts->location_area_code, bts->bsic, bts->tsc,
 		bts->num_trx, VTY_NEWLINE);
+	vty_out(vty, "Description: %s%s",
+		bts->description ? bts->description : "(null)", VTY_NEWLINE);
 	vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
 	vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
 		rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
@@ -294,6 +296,9 @@
 	int i;
 
 	vty_out(vty, "  trx %u%s", trx->nr, VTY_NEWLINE);
+	if (trx->description)
+		vty_out(vty, "   description %s%s", trx->description,
+			VTY_NEWLINE);
 	vty_out(vty, "   rf_locked %u%s",
 		trx->nm_state.administrative == NM_STATE_LOCKED ? 1 : 0,
 		VTY_NEWLINE);
@@ -352,6 +357,8 @@
 
 	vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
 	vty_out(vty, "  type %s%s", btstype2str(bts->type), VTY_NEWLINE);
+	if (bts->description)
+		vty_out(vty, "  description %s%s", bts->description, VTY_NEWLINE);
 	vty_out(vty, "  band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
 	vty_out(vty, "  cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
 	vty_out(vty, "  location_area_code %u%s", bts->location_area_code,
@@ -456,6 +463,8 @@
 {
 	vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
 		trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
+	vty_out(vty, "Description: %s%s",
+		trx->description ? trx->description : "(null)", VTY_NEWLINE);
 	vty_out(vty, "  RF Nominal Power: %d dBm, reduced by %u dB, "
 		"resulting BS power: %d dBm%s",
 		trx->nominal_power, trx->max_power_red,
@@ -1216,6 +1225,7 @@
 	}
 
 	vty->index = bts;
+	vty->index_sub = &bts->description;
 	vty->node = BTS_NODE;
 
 	return CMD_SUCCESS;
@@ -1763,6 +1773,7 @@
 		return CMD_WARNING;
 
 	vty->index = trx;
+	vty->index_sub = &trx->description;
 	vty->node = TRX_NODE;
 
 	return CMD_SUCCESS;
@@ -1976,6 +1987,8 @@
 	install_node(&bts_node, config_write_bts);
 	install_default(BTS_NODE);
 	install_element(BTS_NODE, &cfg_bts_type_cmd);
+	install_element(BTS_NODE, &cfg_description_cmd);
+	install_element(BTS_NODE, &cfg_no_description_cmd);
 	install_element(BTS_NODE, &cfg_bts_band_cmd);
 	install_element(BTS_NODE, &cfg_bts_ci_cmd);
 	install_element(BTS_NODE, &cfg_bts_lac_cmd);
@@ -2011,6 +2024,8 @@
 	install_node(&trx_node, dummy_config_write);
 	install_default(TRX_NODE);
 	install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
+	install_element(TRX_NODE, &cfg_description_cmd);
+	install_element(TRX_NODE, &cfg_no_description_cmd);
 	install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
 	install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
 	install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
diff --git a/openbsc/src/vty_interface_cmds.c b/openbsc/src/vty_interface_cmds.c
index 8c78caa..4e5dc29 100644
--- a/openbsc/src/vty_interface_cmds.c
+++ b/openbsc/src/vty_interface_cmds.c
@@ -41,6 +41,23 @@
 		vty_out(vty, "\r");
 }
 
+struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base)
+{
+	struct buffer *b = buffer_new(NULL, 1024);
+	int i;
+
+	if (!b)
+		return NULL;
+
+	for (i = base; i < argc; i++) {
+		buffer_putstr(b, argv[i]);
+		buffer_putc(b, ' ');
+	}
+	buffer_putc(b, '\0');
+
+	return b;
+}
+
 struct log_target *log_target_create_vty(struct vty *vty)
 {
 	struct log_target *target;
@@ -307,6 +324,52 @@
 	return CMD_SUCCESS;
 }
 
+gDEFUN(cfg_description, cfg_description_cmd,
+	"description .TEXT",
+	"Save human-readable decription of the object\n")
+{
+	char **dptr = vty->index_sub;
+	struct buffer *b;
+
+	if (!dptr) {
+		vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	b = vty_argv_to_buffer(argc, argv, 0);
+	if (!b)
+		return CMD_WARNING;
+
+	if (*dptr)
+		talloc_free(*dptr);
+
+	*dptr = talloc_strdup(NULL, buffer_getstr(b));
+
+	buffer_free(b);
+
+	return CMD_SUCCESS;
+}
+
+gDEFUN(cfg_no_description, cfg_no_description_cmd,
+	"no description",
+	NO_STR
+	"Remove description of the object\n")
+{
+	char **dptr = vty->index_sub;
+
+	if (!dptr) {
+		vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	if (*dptr) {
+		talloc_free(*dptr);
+		*dptr = NULL;
+	}
+
+	return CMD_SUCCESS;
+}
+
 void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
 {
 	vty_out(vty, "Channel Requests        : %lu total, %lu no channel%s",
diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c
index 1b2adbb..029f0c4 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -56,23 +56,6 @@
 	return CMD_SUCCESS;
 }
 
-static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
-{
-	struct buffer *b = buffer_new(NULL, 1024);
-	int i;
-
-	if (!b)
-		return NULL;
-
-	for (i = base; i < argc; i++) {
-		buffer_putstr(b, argv[i]);
-		buffer_putc(b, ' ');
-	}
-	buffer_putc(b, '\0');
-
-	return b;
-}
-
 static int hexparse(const char *str, u_int8_t *b, int max_len)
 
 {
@@ -308,7 +291,7 @@
 			argv[0], argv[1], VTY_NEWLINE);
 		return CMD_WARNING;
 	}
-	b = argv_to_buffer(argc, argv, 2);
+	b = vty_argv_to_buffer(argc, argv, 2);
 	rc = _send_sms_buffer(subscr, b, 0);
 	buffer_free(b);
 
@@ -332,7 +315,7 @@
 		return CMD_WARNING;
 	}
 
-	b = argv_to_buffer(argc, argv, 2);
+	b = vty_argv_to_buffer(argc, argv, 2);
 	rc = _send_sms_buffer(subscr, b, 64);
 	buffer_free(b);