Browse code

sercmd: tab completion support for cfg. vars & groups

- tab completion is now supported for cfg. groups and variables
parameters for the following rpc commands: cfg.help, cfg,get,
cfg.set_now_int, cfg.set_now_string, cfg.set_delayed_int,
cfg.set_delayed_int.
E.g.:
sercmd> cfg.help t<TAB>
tcp tm
sercmd> cfg.help tm fr_<TAB>
fr_inv_timer fr_timer
sercmd> cfg.help tm fr_i<TAB>
sercmd> cfg.help tm fr_inv_timer

- tab completion fixed when spaces are in front of the command
name (e.g. sercmd> wh<TAB> works now).

Andrei Pelinescu-Onciul authored on 28/06/2009 22:59:17
Showing 2 changed files
... ...
@@ -5,7 +5,7 @@ include $(COREPATH)/Makefile.targets
5 5
 
6 6
 auto_gen=
7 7
 NAME=sercmd
8
-RELEASE=0.1
8
+RELEASE=0.2
9 9
 
10 10
 readline_locations= /usr/include/readline/readline.h \
11 11
 					$(LOCALBASE)/include/readline/readline.h
... ...
@@ -30,6 +30,7 @@
30 30
  * History:
31 31
  * --------
32 32
  *  2006-02-14  created by andrei
33
+ *  2009-06-29  command line completion for cfg groups and vars (andrei)
33 34
  */
34 35
 
35 36
 
... ...
@@ -49,6 +50,8 @@
49 49
 #ifdef USE_READLINE
50 50
 #include <readline/readline.h>
51 51
 #include <readline/history.h>
52
+
53
+#define USE_CFG_VARS
52 54
 #endif
53 55
 
54 56
 #include "parse_listen_id.h"
... ...
@@ -63,7 +66,7 @@
63 63
 #define NAME    "sercmd"
64 64
 #endif
65 65
 #ifndef VERSION
66
-#define VERSION "0.1"
66
+#define VERSION "0.2"
67 67
 #endif
68 68
 
69 69
 #define IOVEC_CNT 20
... ...
@@ -122,6 +125,23 @@ int quit; /* used only in interactive mode */
122 122
 struct binrpc_val* rpc_array;
123 123
 int rpc_no=0;
124 124
 
125
+#ifdef USE_CFG_VARS
126
+
127
+
128
+struct binrpc_val* cfg_vars_array;
129
+int cfg_vars_no;
130
+
131
+struct cfg_var_grp{
132
+	struct cfg_var_grp* next;
133
+	str grp_name; /**< group name */
134
+	str* var_names; /**< str array, null terminated */
135
+	int var_no;
136
+};
137
+
138
+struct cfg_var_grp* cfg_grp_lst; /** cfg groups list, allong with var names*/
139
+struct cfg_var_grp* crt_cfg_grp;
140
+#endif /* USE_CFG_VARS */
141
+
125 142
 
126 143
 
127 144
 
... ...
@@ -241,12 +261,22 @@ static struct sercmd_builtin builtins[]={
241 241
 
242 242
 #ifdef USE_READLINE
243 243
 
244
+enum complete_states {
245
+	COMPLETE_NOTHING,
246
+	COMPLETE_CMD_NAME,
247
+#ifdef USE_CFG_VARS
248
+	COMPLETE_CFG_GRP,
249
+	COMPLETE_CFG_VAR
250
+#endif /* USE_CFG_VARS */
251
+};
252
+
244 253
 /* instead of rl_attempted_completion_over which is not present in
245
-   some readline emulations */
246
-static int attempted_completion_over=0; 
254
+   some readline emulations, use attempted_completion_state */
255
+static enum complete_states attempted_completion_state;
256
+static int crt_param_no;
247 257
 
248
-/* commands for which we complete the params to other command names */
249
-char* complete_params[]={
258
+/* commands for which we complete the params to other method names */
259
+char* complete_params_methods[]={
250 260
 	"?",
251 261
 	"h",
252 262
 	"help",
... ...
@@ -254,7 +284,21 @@ char* complete_params[]={
254 254
 	"system.methodHelp",
255 255
 	0
256 256
 };
257
-#endif
257
+
258
+#ifdef USE_CFG_VARS
259
+/* commands for which we complete the first param with a cfg var grp*/
260
+char* complete_params_cfg_var[]={
261
+	"cfg.get",
262
+	"cfg.help",
263
+	"cfg.set_delayed_int",
264
+	"cfg.set_delayed_string",
265
+	"cfg.set_now_int",
266
+	"cfg.set_now_string",
267
+	0
268
+};
269
+#endif /* USE_CFG_VARS */
270
+
271
+#endif /* USE_READLINE */
258 272
 
259 273
 
260 274
 
... ...
@@ -1129,6 +1173,154 @@ error:
1129 1129
 
1130 1130
 
1131 1131
 
1132
+#ifdef USE_CFG_VARS
1133
+/* retrieve the cfg vars and group list */
1134
+static int get_cfgvars_list(int s)
1135
+{
1136
+	struct binrpc_cmd cmd;
1137
+	int cookie;
1138
+	unsigned char reply_buf[MAX_REPLY_SIZE];
1139
+	unsigned char* msg_body;
1140
+	struct binrpc_parse_ctx in_pkt;
1141
+	struct cfg_var_grp* grp;
1142
+	struct cfg_var_grp* last_grp;
1143
+	char* p;
1144
+	char* end;
1145
+	str grp_name;
1146
+	str var_name;
1147
+	int r;
1148
+	int ret;
1149
+	
1150
+	cmd.method="cfg.list";
1151
+	cmd.argc=0;
1152
+	
1153
+	cookie=gen_cookie();
1154
+	if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){
1155
+		if (ret==-1) goto error_send;
1156
+		else goto binrpc_err;
1157
+	}
1158
+	/* read reply */
1159
+	memset(&in_pkt, 0, sizeof(in_pkt));
1160
+	if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt,
1161
+					&msg_body))<0){
1162
+		goto error;
1163
+	}
1164
+	switch(in_pkt.type){
1165
+		case BINRPC_FAULT:
1166
+			if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){
1167
+				goto error;
1168
+			}
1169
+			break;
1170
+		case BINRPC_REPL:
1171
+			cfg_vars_no=100; /* default cmd list */
1172
+			if ((cfg_vars_array=parse_reply_body(&cfg_vars_no, &in_pkt,
1173
+												msg_body, in_pkt.tlen))==0)
1174
+				goto error;
1175
+			break;
1176
+		default:
1177
+			fprintf(stderr, "ERROR: not a reply\n");
1178
+			goto error;
1179
+	}
1180
+	/* get the config groups */
1181
+	last_grp=0;
1182
+	for (r=0; r<cfg_vars_no; r++){
1183
+		grp_name.s=0; grp_name.len=0;
1184
+		if (cfg_vars_array[r].type!=BINRPC_T_STR)
1185
+			continue;
1186
+		grp_name.s=cfg_vars_array[r].u.strval.s;
1187
+		end=cfg_vars_array[r].u.strval.len+grp_name.s;
1188
+		/* parse <grp>: <var_name>*/
1189
+		for (p=grp_name.s; p<end; p++){
1190
+			if (*p==':'){
1191
+				grp_name.len=(int)(long)(p-grp_name.s);
1192
+				break;
1193
+			}
1194
+		}
1195
+		for (grp=cfg_grp_lst; grp; grp=grp->next){
1196
+			if (grp->grp_name.len==grp_name.len &&
1197
+					memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){
1198
+				break; /* found */
1199
+			}
1200
+		}
1201
+		if (grp==0){
1202
+			/* not found => create a new one  */
1203
+			grp=malloc(sizeof(*grp));
1204
+			if (grp==0) goto error_mem;
1205
+			memset(grp, 0, sizeof(*grp));
1206
+			grp->grp_name=grp_name;
1207
+			if (last_grp){
1208
+				last_grp->next=grp;
1209
+				last_grp=grp;
1210
+			}else{
1211
+				cfg_grp_lst=grp;
1212
+				last_grp=cfg_grp_lst;
1213
+			}
1214
+		}
1215
+		grp->var_no++;
1216
+	}
1217
+	/* alloc the var arrays per group */
1218
+	for (grp=cfg_grp_lst; grp; grp=grp->next){
1219
+		grp->var_names=malloc(sizeof(str)*grp->var_no);
1220
+		if (grp->var_names==0) goto error_mem;
1221
+		memset(grp->var_names, 0, sizeof(str)*grp->var_no);
1222
+		grp->var_no=0;
1223
+	}
1224
+	/* reparse to get the var names per group */
1225
+	for (r=0; r<cfg_vars_no; r++){
1226
+		grp_name.s=0; grp_name.len=0;
1227
+		var_name.s=0; var_name.len=0;
1228
+		if (cfg_vars_array[r].type!=BINRPC_T_STR)
1229
+			continue;
1230
+		grp_name.s=cfg_vars_array[r].u.strval.s;
1231
+		end=cfg_vars_array[r].u.strval.len+grp_name.s;
1232
+		/* parse <grp>: <var_name>*/
1233
+		for (p=grp_name.s; p<end; p++){
1234
+			if (*p==':'){
1235
+				grp_name.len=(int)(long)(p-grp_name.s);
1236
+				p++;
1237
+				for (; p<end && *p==' '; p++);
1238
+				var_name.s=p;
1239
+				var_name.len=(int)(long)(end-p);
1240
+				if (var_name.len==0) break;
1241
+				for (grp=cfg_grp_lst; grp; grp=grp->next){
1242
+					if (grp->grp_name.len==grp_name.len &&
1243
+						memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){
1244
+						/* add var */
1245
+						grp->var_names[grp->var_no]=var_name;
1246
+						grp->var_no++;
1247
+					}
1248
+				}
1249
+				break;
1250
+			}
1251
+		}
1252
+	}
1253
+	return 0;
1254
+binrpc_err:
1255
+error_send:
1256
+error:
1257
+error_mem:
1258
+	return -1;
1259
+}
1260
+
1261
+
1262
+
1263
+void free_cfg_grp_lst()
1264
+{
1265
+	struct cfg_var_grp* grp;
1266
+	struct cfg_var_grp* last;
1267
+	
1268
+	grp=cfg_grp_lst;
1269
+	while(grp){
1270
+		last=grp;
1271
+		grp=grp->next;
1272
+		free(last);
1273
+	}
1274
+	cfg_grp_lst=0;
1275
+}
1276
+#endif /* USE_CFG_VARS */
1277
+
1278
+
1279
+
1132 1280
 static void print_formatting(char* prefix, char* format, char* suffix)
1133 1281
 {
1134 1282
 	if (format){
... ...
@@ -1245,73 +1437,200 @@ static char* sercmd_generator(const char* text, int state)
1245 1245
 	static int list; /* aliases, builtins, rpc_array */
1246 1246
 	static int len;
1247 1247
 	char* name;
1248
-	
1249
-	if (attempted_completion_over)
1250
-		return 0;
1251
-	
1252
-	if (state==0){
1253
-		/* init */
1254
-		idx=list=0;
1255
-		len=strlen(text);
1256
-	}
1257
-	/* return next partial match */
1258
-	switch(list){
1259
-		case 0: /* aliases*/
1260
-			while((name=cmd_aliases[idx].name)){
1261
-				idx++;
1262
-				if (strncmp(name, text, len)==0)
1263
-					return strdup(name);
1248
+#ifdef USE_CFG_VARS
1249
+	static struct cfg_var_grp* grp;
1250
+#endif
1251
+	switch(attempted_completion_state){
1252
+		case COMPLETE_NOTHING:
1253
+			return 0;
1254
+		case COMPLETE_CMD_NAME:
1255
+			if (state==0){
1256
+				/* init */
1257
+				idx=list=0;
1258
+				len=strlen(text);
1264 1259
 			}
1265
-			list++;
1266
-			idx=0;
1267
-			/* no break */
1268
-		case 1: /* builtins */
1269
-			while((name=builtins[idx].name)){
1270
-				idx++;
1271
-				if (strncmp(name, text, len)==0)
1272
-					return strdup(name);
1260
+			/* return next partial match */
1261
+			switch(list){
1262
+				case 0: /* aliases*/
1263
+					while((name=cmd_aliases[idx].name)){
1264
+						idx++;
1265
+						if (strncmp(name, text, len)==0)
1266
+							return strdup(name);
1267
+					}
1268
+					list++;
1269
+					idx=0;
1270
+					/* no break */
1271
+				case 1: /* builtins */
1272
+					while((name=builtins[idx].name)){
1273
+						idx++;
1274
+						if (strncmp(name, text, len)==0)
1275
+							return strdup(name);
1276
+					}
1277
+					list++;
1278
+					idx=0;
1279
+					/* no break */
1280
+				case 2: /* rpc_array */
1281
+					while(idx < rpc_no){
1282
+						if (rpc_array[idx].type==BINRPC_T_STR){
1283
+							name=rpc_array[idx].u.strval.s;
1284
+							idx++;
1285
+							if (strncmp(name, text, len)==0)
1286
+								return strdup(name);
1287
+						}else{
1288
+							idx++;
1289
+						}
1290
+					}
1273 1291
 			}
1274
-			list++;
1275
-			idx=0;
1276
-			/* no break */
1277
-		case 2: /* rpc_array */
1278
-			while(idx < rpc_no){
1279
-				if (rpc_array[idx].type==BINRPC_T_STR){
1280
-					name=rpc_array[idx].u.strval.s;
1281
-					idx++;
1282
-					if (strncmp(name, text, len)==0)
1283
-						return strdup(name);
1284
-				}else{
1292
+			break;
1293
+#ifdef USE_CFG_VARS
1294
+		case COMPLETE_CFG_GRP:
1295
+			if (state==0){
1296
+				/* init */
1297
+				len=strlen(text);
1298
+				grp=cfg_grp_lst;
1299
+			}else{
1300
+				grp=grp->next;
1301
+			}
1302
+			for(;grp; grp=grp->next){
1303
+				if (len<=grp->grp_name.len &&
1304
+						memcmp(text, grp->grp_name.s, len)==0) {
1305
+					/* zero-term copy of the grp name */
1306
+					name=malloc(grp->grp_name.len+1);
1307
+					if (name){
1308
+						memcpy(name, grp->grp_name.s, grp->grp_name.len);
1309
+						name[grp->grp_name.len]=0;
1310
+					}
1311
+					return name;
1312
+				}
1313
+			}
1314
+			break;
1315
+		case COMPLETE_CFG_VAR:
1316
+			if (state==0){
1317
+				/* init */
1318
+				len=strlen(text);
1319
+				idx=0;
1320
+			}
1321
+			while(idx < crt_cfg_grp->var_no){
1322
+				if (len<=crt_cfg_grp->var_names[idx].len &&
1323
+						memcmp(text, crt_cfg_grp->var_names[idx].s, len)==0) {
1324
+					/* zero-term copy of the var name */
1325
+					name=malloc(crt_cfg_grp->var_names[idx].len+1);
1326
+					if (name){
1327
+						memcpy(name, crt_cfg_grp->var_names[idx].s,
1328
+									 crt_cfg_grp->var_names[idx].len);
1329
+						name[crt_cfg_grp->var_names[idx].len]=0;
1330
+					}
1285 1331
 					idx++;
1332
+					return name;
1286 1333
 				}
1334
+				idx++;
1287 1335
 			}
1336
+			break;
1337
+#endif /* USE_CFG_VARS */
1288 1338
 	}
1289 1339
 	/* no matches */
1290 1340
 	return 0;
1291 1341
 }
1292 1342
 
1293 1343
 
1344
+
1294 1345
 char** sercmd_completion(const char* text, int start, int end)
1295 1346
 {
1296
-	int r;
1297
-	int i;
1347
+	int i, j;
1348
+	int cmd_start, cmd_end, cmd_len;
1349
+	int whitespace;
1350
+#ifdef USE_CFG_VARS
1351
+	struct cfg_var_grp* grp;
1352
+	static int grp_start;
1353
+	int grp_len;
1354
+#endif /* USE_CFG_VARS */
1298 1355
 	
1299
-	attempted_completion_over=1;
1300
-	/* complete only at beginning */
1301
-	if (start==0){
1302
-		attempted_completion_over=0;
1356
+	crt_param_no=0;
1357
+	/* skip over whitespace at the beginning */
1358
+	for (j=0; (j<start) && (rl_line_buffer[j]==' ' ||
1359
+							rl_line_buffer[j]=='\t'); j++);
1360
+	cmd_start=j;
1361
+	if (start==cmd_start){
1362
+		/* complete cmd name at beginning */
1363
+		attempted_completion_state=COMPLETE_CMD_NAME;
1364
+#ifdef USE_CFG_VARS
1365
+		grp_start=0;
1366
+#endif /* USE_CFG_VARS */
1303 1367
 	}else{ /* or if this is a command for which we complete the parameters */
1304
-		/* find first whitespace */
1305
-		for(r=0; (r<start) && (rl_line_buffer[r]!=' ') && 
1306
-				(rl_line_buffer[r]!='\t'); r++);
1307
-		for(i=0; complete_params[i]; i++){
1308
-			if ((r==strlen(complete_params[i])) &&
1309
-					(strncmp(rl_line_buffer, complete_params[i], r)==0)){
1310
-					attempted_completion_over=0;
1311
-					break;
1368
+		/* find first whitespace after command name*/
1369
+		for(; (j<start) && (rl_line_buffer[j]!=' ') &&
1370
+					(rl_line_buffer[j]!='\t'); j++);
1371
+		cmd_end=j;
1372
+		cmd_len=cmd_end-cmd_start;
1373
+		/* count params before the current one */
1374
+		whitespace=1;
1375
+		for (; j<start; j++){
1376
+			if (rl_line_buffer[j]!=' ' && rl_line_buffer[j]!='\t'){
1377
+				if (whitespace) crt_param_no++;
1378
+				whitespace=0;
1379
+			}else
1380
+				whitespace=1;
1381
+		}
1382
+		crt_param_no++;
1383
+		if (crt_param_no==1){
1384
+			for(i=0; complete_params_methods[i]; i++){
1385
+				if ((cmd_len==strlen(complete_params_methods[i])) &&
1386
+						(strncmp(&rl_line_buffer[cmd_start],
1387
+								 complete_params_methods[i],
1388
+								 cmd_len)==0)){
1389
+						attempted_completion_state=COMPLETE_CMD_NAME;
1390
+						goto end;
1391
+				}
1312 1392
 			}
1393
+#ifdef USE_CFG_VARS
1394
+			/* try  complete_param*_cfg_grp */
1395
+			for(i=0; complete_params_cfg_var[i]; i++){
1396
+				if ((cmd_len==strlen(complete_params_cfg_var[i])) &&
1397
+						(strncmp(&rl_line_buffer[cmd_start],
1398
+								 complete_params_cfg_var[i],
1399
+								 cmd_len)==0)){
1400
+						attempted_completion_state=COMPLETE_CFG_GRP;
1401
+						grp_start=start;
1402
+						goto end;
1403
+				}
1404
+			}
1405
+		}else if (crt_param_no==2){
1406
+			if (attempted_completion_state!=COMPLETE_CFG_GRP){
1407
+				for(i=0; complete_params_cfg_var[i]; i++){
1408
+					if ((cmd_len==strlen(complete_params_cfg_var[i])) &&
1409
+						(strncmp(&rl_line_buffer[cmd_start],
1410
+								 complete_params_cfg_var[i],
1411
+								 cmd_len)==0)){
1412
+						attempted_completion_state=COMPLETE_CFG_GRP;
1413
+						/* find grp_start */
1414
+						for(j=cmd_end; (j<start) && ((rl_line_buffer[j]==' ') 
1415
+									|| (rl_line_buffer[j]=='\t')); j++);
1416
+						grp_start=j;
1417
+						break;
1418
+					}
1419
+				}
1420
+			}
1421
+			if (attempted_completion_state==COMPLETE_CFG_GRP){
1422
+				/* get the group name from the grp_param */
1423
+				/* find first whitespace for the group name*/
1424
+				for(j=grp_start; (j<start) && (rl_line_buffer[j]!=' ') &&
1425
+						(rl_line_buffer[j]!='\t'); j++);
1426
+				grp_len=j-grp_start;
1427
+				for(grp=cfg_grp_lst; grp; grp=grp->next){
1428
+					if (grp_len==grp->grp_name.len &&
1429
+							memcmp(&rl_line_buffer[grp_start], grp->grp_name.s,
1430
+										grp_len)==0) {
1431
+						attempted_completion_state=COMPLETE_CFG_VAR;
1432
+						crt_cfg_grp=grp;
1433
+						goto end;
1434
+					}
1435
+				}
1436
+			}
1437
+#endif /* USE_CFG_VARS */
1313 1438
 		}
1439
+		attempted_completion_state=COMPLETE_NOTHING;
1314 1440
 	}
1441
+end:
1315 1442
 	return 0; /* let readline call sercmd_generator */
1316 1443
 }
1317 1444
 
... ...
@@ -1471,6 +1790,9 @@ int main(int argc, char** argv)
1471 1471
 	}
1472 1472
 	/* interactive mode */
1473 1473
 	get_sercmd_list(s);
1474
+#ifdef USE_CFG_VARS
1475
+	get_cfgvars_list(s);
1476
+#endif /* USE_CFG_VARS */
1474 1477
 	/* banners */
1475 1478
 	printf("%s %s\n", NAME, VERSION);
1476 1479
 	printf("%s\n", COPYRIGHT);
... ...
@@ -1520,6 +1842,12 @@ end:
1520 1520
 		free(format);
1521 1521
 	if (rpc_array)
1522 1522
 		free_rpc_array(rpc_array, rpc_no);
1523
+#ifdef USE_CFG_VARS
1524
+	if (cfg_grp_lst)
1525
+		free_cfg_grp_lst();
1526
+	if (cfg_vars_array)
1527
+		free_rpc_array(cfg_vars_array, cfg_vars_no);
1528
+#endif /* USE_CFG_VARS */
1523 1529
 	cleanup();
1524 1530
 	exit(0);
1525 1531
 error:
... ...
@@ -1529,6 +1857,12 @@ error:
1529 1529
 		free(format);
1530 1530
 	if (rpc_array)
1531 1531
 		free_rpc_array(rpc_array, rpc_no);
1532
+#ifdef USE_CFG_VARS
1533
+	if (cfg_grp_lst)
1534
+		free_cfg_grp_lst();
1535
+	if (cfg_vars_array)
1536
+		free_rpc_array(cfg_vars_array, cfg_vars_no);
1537
+#endif /* USE_CFG_VARS */
1532 1538
 	cleanup();
1533 1539
 	exit(-1);
1534 1540
 }