@@ -1016,6 +1016,47 @@ static void cli_clear_line(int sockfd, char *cmd, int l, int cursor) {
10161016 memset ((char * )cmd , 0 , l );
10171017}
10181018
1019+ // return new cursor position
1020+ static int cli_word_right (char * cmd , int l , int cursor ) {
1021+
1022+ while (cursor < l && cmd [cursor ] == ' ' ) {
1023+ cursor ++ ;
1024+ }
1025+
1026+ while (cursor < l && cmd [cursor ] != ' ' ) {
1027+ cursor ++ ;
1028+ }
1029+ return cursor ;
1030+ }
1031+
1032+ static int cli_word_left (char * cmd , int cursor ) {
1033+ // like readline compare char before cursor
1034+ while (cursor > 0 && cmd [cursor - 1 ] == ' ' ) {
1035+ cursor -- ;
1036+ }
1037+
1038+ while (cursor > 0 && cmd [cursor - 1 ] != ' ' ) {
1039+ cursor -- ;
1040+ }
1041+ return cursor ;
1042+ }
1043+
1044+ static int cli_move_cursor (struct cli_def * cli , int sockfd , char * cmd , int l , int cursor , int nc ) {
1045+ while (cursor && cursor > nc ) {
1046+ if (cli -> state != STATE_PASSWORD && cli -> state != STATE_ENABLE_PASSWORD ) {
1047+ _write (sockfd , "\b" , 1 );
1048+ }
1049+ cursor -- ;
1050+ }
1051+ while (cursor < l && cursor < nc ) {
1052+ if (cli -> state != STATE_PASSWORD && cli -> state != STATE_ENABLE_PASSWORD ) {
1053+ _write (sockfd , & cmd [cursor ], 1 );
1054+ }
1055+ cursor ++ ;
1056+ }
1057+ return cursor ;
1058+ }
1059+
10191060void cli_reprompt (struct cli_def * cli ) {
10201061 if (!cli ) return ;
10211062 cli -> showprompt = 1 ;
@@ -1070,7 +1111,9 @@ static int show_prompt(struct cli_def *cli, int sockfd) {
10701111}
10711112
10721113int cli_loop (struct cli_def * cli , int sockfd ) {
1073- int n , l , oldl = 0 , is_telnet_option = 0 , skip = 0 , esc = 0 , cursor = 0 ;
1114+ int n , l , oldl = 0 , is_telnet_option = 0 , skip = 0 , esc = 0 , cursor = 0 , nc ;
1115+ char esc_buff [10 ] = {0 };
1116+ int esc_pos = 0 ;
10741117 char * cmd = NULL , * oldcmd = 0 ;
10751118 char * username = NULL , * password = NULL ;
10761119
@@ -1269,7 +1312,16 @@ int cli_loop(struct cli_def *cli, int sockfd) {
12691312
12701313 // Handle ANSI arrows
12711314 if (esc ) {
1272- if (esc == '[' ) {
1315+ if (esc == '[' ) { // 0x5b
1316+
1317+ // terminate ESC seq
1318+ if (c >= 0x40 && c <= 0x7E ) {
1319+ esc = 0 ;
1320+ }
1321+ if (c >= 0x30 && c <= 0x3F && esc_pos < (int )sizeof (esc_buff ) - 2 ) {
1322+ esc_buff [esc_pos ++ ] = c ;
1323+ }
1324+
12731325 // Remap to readline control codes
12741326 switch (c ) {
12751327 case 'A' : // Up
@@ -1281,23 +1333,79 @@ int cli_loop(struct cli_def *cli, int sockfd) {
12811333 break ;
12821334
12831335 case 'C' : // Right
1284- c = CTRL ('F' );
1336+ if (strcmp (esc_buff , "1;5" ) == 0 ) {
1337+ nc = cli_word_right (cmd , l , cursor );
1338+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1339+ c = 0 ;
1340+ }
1341+ else {
1342+ c = CTRL ('F' );
1343+ }
12851344 break ;
12861345
12871346 case 'D' : // Left
1288- c = CTRL ('B' );
1347+ if (strcmp (esc_buff , "1;5" ) == 0 ) {
1348+ nc = cli_word_left (cmd , cursor );
1349+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1350+ c = 0 ;
1351+ }
1352+ else {
1353+ c = CTRL ('B' );
1354+ }
1355+ break ;
1356+
1357+ case 'H' : // Home
1358+ c = CTRL ('A' );
1359+ break ;
1360+
1361+ case 'F' : // End
1362+ c = CTRL ('E' );
1363+ break ;
1364+
1365+ case '~' : {
1366+ // Delete, do not remap to EOF if l==0
1367+ if (strcmp (esc_buff , "3" ) == 0 && l ) {
1368+ c = CTRL ('D' );
1369+ }
1370+ else {
1371+ c = 0 ;
1372+ }
12891373 break ;
1374+ }
12901375
12911376 default :
12921377 c = 0 ;
12931378 }
12941379
1295- esc = 0 ;
12961380 } else {
1297- esc = (c == '[' ) ? c : 0 ;
1381+
1382+ switch (c ) {
1383+
1384+ case 'b' : // Left by word
1385+ nc = cli_word_left (cmd , cursor );
1386+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1387+ break ;
1388+
1389+ case 'f' : // Right by word
1390+ nc = cli_word_right (cmd , l , cursor );
1391+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1392+ break ;
1393+
1394+ case '[' :
1395+ esc = c ;
1396+ continue ;
1397+
1398+ default :
1399+ break ;
1400+ }
1401+ esc = 0 ;
12981402 continue ;
12991403 }
13001404 }
1405+ else {
1406+ memset (esc_buff , 0 , sizeof (esc_buff ));
1407+ esc_pos = 0 ;
1408+ }
13011409
13021410 if (c == 0 ) continue ;
13031411 if (c == '\n' ) continue ;
@@ -1307,7 +1415,7 @@ int cli_loop(struct cli_def *cli, int sockfd) {
13071415 break ;
13081416 }
13091417
1310- if (c == 27 ) {
1418+ if (c == 27 ) { // 0x1b ESC
13111419 esc = 1 ;
13121420 continue ;
13131421 }
@@ -1424,7 +1532,26 @@ int cli_loop(struct cli_def *cli, int sockfd) {
14241532 if (c == CTRL ('D' )) {
14251533 if (cli -> state == STATE_PASSWORD || cli -> state == STATE_ENABLE_PASSWORD ) break ;
14261534
1427- if (l ) continue ;
1535+ if (l ) {
1536+
1537+ if (cursor < l ) {
1538+ _write (sockfd , cmd + cursor + 1 , l - cursor - 1 );
1539+ _write (sockfd , " " , 1 );
1540+
1541+ // Move everything one char left
1542+ memmove (cmd + cursor , cmd + cursor + 1 , l - cursor - 1 );
1543+
1544+ l -- ;
1545+
1546+ // And reposition cursor
1547+ for (int i = l ; i >= cursor ; i -- ) _write (sockfd , "\b" , 1 );
1548+
1549+ // Set former last char to null
1550+ cmd [l ] = 0 ;
1551+ }
1552+
1553+ continue ;
1554+ }
14281555
14291556 l = -1 ;
14301557 break ;
0 commit comments