[sr-dev] git:master: sercmd: tab completion support for mi commands

Andrei Pelinescu-Onciul andrei at iptel.org
Mon Jun 29 11:08:35 CEST 2009


Module: sip-router
Branch: master
Commit: 17ce4b4f36122b515efc7d62f70b55cb0cb17675
URL:    http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=17ce4b4f36122b515efc7d62f70b55cb0cb17675

Author: Andrei Pelinescu-Onciul <andrei at iptel.org>
Committer: Andrei Pelinescu-Onciul <andrei at iptel.org>
Date:   Mon Jun 29 11:03:46 2009 +0200

sercmd: tab completion support for mi commands

- tab completion is now supported for mi commands.
  E.g.:
    sercmd> mi p<TAB><TAB>
    ps   pwd
    sercmd> mi  pw<TAB>
    sercmd> mi  pwd

---

 utils/sercmd/sercmd.c |  259 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 248 insertions(+), 11 deletions(-)

diff --git a/utils/sercmd/sercmd.c b/utils/sercmd/sercmd.c
index 1c41daf..0c26d9e 100644
--- a/utils/sercmd/sercmd.c
+++ b/utils/sercmd/sercmd.c
@@ -31,6 +31,7 @@
  * --------
  *  2006-02-14  created by andrei
  *  2009-06-29  command line completion for cfg groups and vars (andrei)
+ *  2009-06-30  command line completion for mi cmds (andrei)
  */
 
 
@@ -51,7 +52,8 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
-#define USE_CFG_VARS
+#define USE_CFG_VARS /* cfg group and vars completion */
+#define USE_MI  /* mi completion */
 #endif
 
 #include "parse_listen_id.h"
@@ -127,7 +129,6 @@ int rpc_no=0;
 
 #ifdef USE_CFG_VARS
 
-
 struct binrpc_val* cfg_vars_array;
 int cfg_vars_no;
 
@@ -142,6 +143,13 @@ struct cfg_var_grp* cfg_grp_lst; /** cfg groups list, allong with var names*/
 struct cfg_var_grp* crt_cfg_grp;
 #endif /* USE_CFG_VARS */
 
+#ifdef USE_MI
+struct binrpc_val* mi_which_array;
+int mi_which_no;
+
+str* mi_cmds;
+int mi_cmds_no;
+#endif /* USE_MI */
 
 
 
@@ -262,12 +270,16 @@ static struct sercmd_builtin builtins[]={
 #ifdef USE_READLINE
 
 enum complete_states {
-	COMPLETE_NOTHING,
+	COMPLETE_INIT,
 	COMPLETE_CMD_NAME,
 #ifdef USE_CFG_VARS
 	COMPLETE_CFG_GRP,
-	COMPLETE_CFG_VAR
+	COMPLETE_CFG_VAR,
 #endif /* USE_CFG_VARS */
+#ifdef USE_MI
+	COMPLETE_MI,
+#endif /* USE_Mi */
+	COMPLETE_NOTHING
 };
 
 /* instead of rl_attempted_completion_over which is not present in
@@ -298,6 +310,14 @@ char* complete_params_cfg_var[]={
 };
 #endif /* USE_CFG_VARS */
 
+#ifdef USE_MI
+/* commands for which we complete the first param with an mi command*/
+char* complete_params_mi[]={
+	"mi",
+	0
+};
+#endif /* USE_MI */
+
 #endif /* USE_READLINE */
 
 
@@ -1155,7 +1175,7 @@ static int get_sercmd_list(int s)
 			}
 			break;
 		case BINRPC_REPL:
-			rpc_no=10; /* default cmd list */
+			rpc_no=100; /* default cmd list */
 			if ((rpc_array=parse_reply_body(&rpc_no, &in_pkt, msg_body,
 												in_pkt.tlen))==0)
 				goto error;
@@ -1173,6 +1193,30 @@ error:
 
 
 
+#if defined(USE_CFG_VARS) || defined (USE_MI)
+/** check if cmd is a rpc command.
+ * Quick check (using the internal rpc_array) if cmd is a valid rpc command.
+ * @param cmd - null terminated ascii string
+ * @return 1 on success, 0 on failure.
+ */
+static int is_rpc_cmd(char* cmd)
+{
+	int r;
+	int cmd_len;
+	
+	cmd_len=strlen(cmd);
+	for (r=0; r<rpc_no; r++){
+		if ((rpc_array[r].type==BINRPC_T_STR) &&
+			(rpc_array[r].u.strval.len==cmd_len) &&
+			(strncmp(cmd, rpc_array[r].u.strval.s, cmd_len)==0))
+			return 1;
+	}
+	return 0;
+}
+#endif /* USE_CFG_VARS || USE_MI */
+
+
+
 #ifdef USE_CFG_VARS
 /* retrieve the cfg vars and group list */
 static int get_cfgvars_list(int s)
@@ -1193,6 +1237,7 @@ static int get_cfgvars_list(int s)
 	
 	cmd.method="cfg.list";
 	cmd.argc=0;
+	if (!is_rpc_cmd(cmd.method)) goto error;
 	
 	cookie=gen_cookie();
 	if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){
@@ -1321,6 +1366,132 @@ void free_cfg_grp_lst()
 
 
 
+#ifdef USE_MI
+/* retrieve the mi list */
+static int get_mi_list(int s)
+{
+	struct binrpc_cmd cmd;
+	int cookie;
+	unsigned char reply_buf[MAX_REPLY_SIZE];
+	unsigned char* msg_body;
+	struct binrpc_parse_ctx in_pkt;
+	char* p;
+	char* end;
+	str mi_name;
+	int mi_which_results;
+	int r;
+	int ret;
+	
+	cmd.method="mi";
+	cmd.argv[0].type=BINRPC_T_STR;
+	cmd.argv[0].u.strval.s="which";
+	cmd.argv[0].u.strval.len=strlen(cmd.argv[0].u.strval.s);
+	cmd.argc=1;
+	if (!is_rpc_cmd(cmd.method)) goto error;
+	
+	cookie=gen_cookie();
+	if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){
+		if (ret==-1) goto error_send;
+		else goto binrpc_err;
+	}
+	/* read reply */
+	memset(&in_pkt, 0, sizeof(in_pkt));
+	if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt,
+					&msg_body))<0){
+		goto error;
+	}
+	switch(in_pkt.type){
+		case BINRPC_FAULT:
+			if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){
+				goto error;
+			}
+			break;
+		case BINRPC_REPL:
+			mi_which_no=25; /* default rpc list */
+			if ((mi_which_array=parse_reply_body(&mi_which_no, &in_pkt,
+												msg_body, in_pkt.tlen))==0)
+				goto error;
+			break;
+		default:
+			fprintf(stderr, "ERROR: not a reply\n");
+			goto error;
+	}
+	
+	
+	/* get the mi commands number */
+	mi_which_results=0;
+	for (r=0; r<mi_which_no; r++){
+		if (mi_which_array[r].type!=BINRPC_T_STR)
+			continue;
+		/* we are interestend only in lines starting with '+', e.g.:
+		   + :: version */
+		if ((mi_which_array[r].u.strval.len) &&
+			(mi_which_array[r].u.strval.s[0]=='+'))
+			mi_which_results++;
+	}
+	/* no mi commands */
+	if (mi_which_results==0)
+		goto error;
+	/* alloc the mi_cmds array */
+	mi_cmds=malloc(mi_which_results*sizeof(*mi_cmds));
+	if (mi_cmds==0) goto error_mem;
+	memset(mi_cmds, 0, mi_which_results* sizeof(mi_cmds));
+	/* get the mi names list */
+	for (r=0; r<mi_which_no; r++){
+		if (mi_which_array[r].type!=BINRPC_T_STR)
+			continue;
+		p=mi_which_array[r].u.strval.s;
+		end=p+mi_which_array[r].u.strval.len;
+		/* we are interestend only in lines starting with '+', e.g.:
+		   + :: version */
+		if ((p>=end) || (*p!='+'))
+			continue;
+		p++;
+		/* skip over to the first ':' */
+		for(;p<end && *p!=':'; p++);
+		if (p>=end) continue;
+		p++;
+		/* skip over to the next ':' */
+		for(;p<end && *p!=':'; p++);
+		if (p>=end) continue;
+		p++;
+		/* skip over spaces */
+		for(;p<end && (*p==' ' || *p=='\t'); p++);
+		if (p>=end) continue;
+		if (mi_cmds_no >= mi_which_results){
+			fprintf(stderr, "BUG: wrong mi cmds no (%d >= %d)\n",
+							mi_cmds_no, mi_which_results);
+			goto error;
+		}
+		mi_name.s=p;
+		mi_name.len=(int)(long)(end-p);
+		mi_cmds[mi_cmds_no]=mi_name;
+		mi_cmds_no++;
+	}
+	
+	return 0;
+binrpc_err:
+error_send:
+error:
+error_mem:
+	return -1;
+}
+
+
+
+void free_mi_cmds()
+{
+	if (mi_cmds){
+		free(mi_cmds);
+		mi_cmds=0;
+		mi_cmds_no=0;
+	}
+}
+#endif /* USE_MI */
+
+
+
+
 static void print_formatting(char* prefix, char* format, char* suffix)
 {
 	if (format){
@@ -1441,6 +1612,7 @@ static char* sercmd_generator(const char* text, int state)
 	static struct cfg_var_grp* grp;
 #endif
 	switch(attempted_completion_state){
+		case COMPLETE_INIT:
 		case COMPLETE_NOTHING:
 			return 0;
 		case COMPLETE_CMD_NAME:
@@ -1527,6 +1699,29 @@ static char* sercmd_generator(const char* text, int state)
 			}
 			break;
 #endif /* USE_CFG_VARS */
+#ifdef USE_MI
+		case COMPLETE_MI:
+			if (state==0){
+				/* init */
+				len=strlen(text);
+				idx=0;
+			}
+			while(idx < mi_cmds_no){
+				if (len<=mi_cmds[idx].len &&
+						memcmp(text, mi_cmds[idx].s, len)==0) {
+					/* zero-term copy of the var name */
+					name=malloc(mi_cmds[idx].len+1);
+					if (name){
+						memcpy(name, mi_cmds[idx].s, mi_cmds[idx].len);
+						name[mi_cmds[idx].len]=0;
+					}
+					idx++;
+					return name;
+				}
+				idx++;
+			}
+			break;
+#endif /* USE_MI */
 	}
 	/* no matches */
 	return 0;
@@ -1594,6 +1789,20 @@ char** sercmd_completion(const char* text, int start, int end)
 						goto end;
 				}
 			}
+#endif /* USE_CFG_VARS */
+#ifdef USE_MI
+			/* try complete_parms_mi */
+			for(i=0; complete_params_mi[i]; i++){
+				if ((cmd_len==strlen(complete_params_mi[i])) &&
+						(strncmp(&rl_line_buffer[cmd_start],
+								 complete_params_mi[i],
+								 cmd_len)==0)){
+						attempted_completion_state=COMPLETE_MI;
+						goto end;
+				}
+			}
+#endif /* USE_MI */
+#ifdef USE_CFG_VARS
 		}else if (crt_param_no==2){
 			if (attempted_completion_state!=COMPLETE_CFG_GRP){
 				for(i=0; complete_params_cfg_var[i]; i++){
@@ -1789,10 +1998,14 @@ int main(int argc, char** argv)
 		goto end;
 	}
 	/* interactive mode */
-	get_sercmd_list(s);
-#ifdef USE_CFG_VARS
-	get_cfgvars_list(s);
-#endif /* USE_CFG_VARS */
+	if (get_sercmd_list(s)==0){
+	#ifdef USE_CFG_VARS
+		get_cfgvars_list(s);
+	#endif /* USE_CFG_VARS */
+	#ifdef USE_MI
+		get_mi_list(s);
+	#endif /* USE_MI */
+	}
 	/* banners */
 	printf("%s %s\n", NAME, VERSION);
 	printf("%s\n", COPYRIGHT);
@@ -1845,9 +2058,21 @@ end:
 #ifdef USE_CFG_VARS
 	if (cfg_grp_lst)
 		free_cfg_grp_lst();
-	if (cfg_vars_array)
+	if (cfg_vars_array){
 		free_rpc_array(cfg_vars_array, cfg_vars_no);
+		cfg_vars_array=0;
+		cfg_vars_no=0;
+	}
 #endif /* USE_CFG_VARS */
+#ifdef USE_MI
+	if (mi_cmds)
+		free_mi_cmds();
+	if (mi_which_array){
+		free_rpc_array(mi_which_array, mi_which_no);
+		mi_which_array=0;
+		mi_which_no=0;
+	}
+#endif /* USE_MI */
 	cleanup();
 	exit(0);
 error:
@@ -1860,9 +2085,21 @@ error:
 #ifdef USE_CFG_VARS
 	if (cfg_grp_lst)
 		free_cfg_grp_lst();
-	if (cfg_vars_array)
+	if (cfg_vars_array){
 		free_rpc_array(cfg_vars_array, cfg_vars_no);
+		cfg_vars_array=0;
+		cfg_vars_no=0;
+	}
 #endif /* USE_CFG_VARS */
+#ifdef USE_MI
+	if (mi_cmds)
+		free_mi_cmds();
+	if (mi_which_array){
+		free_rpc_array(mi_which_array, mi_which_no);
+		mi_which_array=0;
+		mi_which_no=0;
+	}
+#endif /* USE_MI */
 	cleanup();
 	exit(-1);
 }




More information about the sr-dev mailing list