@@ -1013,6 +1013,47 @@ static void cli_clear_line(int sockfd, char *cmd, int l, int cursor) {
10131013 memset ((char * )cmd , 0 , l );
10141014}
10151015
1016+ // return new cursor position
1017+ static int cli_word_right (char * cmd , int l , int cursor ) {
1018+
1019+ while (cursor < l && cmd [cursor ] == ' ' ) {
1020+ cursor ++ ;
1021+ }
1022+
1023+ while (cursor < l && cmd [cursor ] != ' ' ) {
1024+ cursor ++ ;
1025+ }
1026+ return cursor ;
1027+ }
1028+
1029+ static int cli_word_left (char * cmd , int cursor ) {
1030+ // like readline compare char before cursor
1031+ while (cursor > 0 && cmd [cursor - 1 ] == ' ' ) {
1032+ cursor -- ;
1033+ }
1034+
1035+ while (cursor > 0 && cmd [cursor - 1 ] != ' ' ) {
1036+ cursor -- ;
1037+ }
1038+ return cursor ;
1039+ }
1040+
1041+ static int cli_move_cursor (struct cli_def * cli , int sockfd , char * cmd , int l , int cursor , int nc ) {
1042+ while (cursor && cursor > nc ) {
1043+ if (cli -> state != STATE_PASSWORD && cli -> state != STATE_ENABLE_PASSWORD ) {
1044+ _write (sockfd , "\b" , 1 );
1045+ }
1046+ cursor -- ;
1047+ }
1048+ while (cursor < l && cursor < nc ) {
1049+ if (cli -> state != STATE_PASSWORD && cli -> state != STATE_ENABLE_PASSWORD ) {
1050+ _write (sockfd , & cmd [cursor ], 1 );
1051+ }
1052+ cursor ++ ;
1053+ }
1054+ return cursor ;
1055+ }
1056+
10161057void cli_reprompt (struct cli_def * cli ) {
10171058 if (!cli ) return ;
10181059 cli -> showprompt = 1 ;
@@ -1067,7 +1108,9 @@ static int show_prompt(struct cli_def *cli, int sockfd) {
10671108}
10681109
10691110int cli_loop (struct cli_def * cli , int sockfd ) {
1070- int n , l , oldl = 0 , is_telnet_option = 0 , skip = 0 , esc = 0 , cursor = 0 ;
1111+ int n , l , oldl = 0 , is_telnet_option = 0 , skip = 0 , esc = 0 , cursor = 0 , nc ;
1112+ char esc_buff [10 ] = {0 };
1113+ int esc_pos = 0 ;
10711114 char * cmd = NULL , * oldcmd = 0 ;
10721115 char * username = NULL , * password = NULL ;
10731116
@@ -1266,7 +1309,16 @@ int cli_loop(struct cli_def *cli, int sockfd) {
12661309
12671310 // Handle ANSI arrows
12681311 if (esc ) {
1269- if (esc == '[' ) {
1312+ if (esc == '[' ) { // 0x5b
1313+
1314+ // terminate ESC seq
1315+ if (c >= 0x40 && c <= 0x7E ) {
1316+ esc = 0 ;
1317+ }
1318+ if (c >= 0x30 && c <= 0x3F && esc_pos < (int )sizeof (esc_buff ) - 2 ) {
1319+ esc_buff [esc_pos ++ ] = c ;
1320+ }
1321+
12701322 // Remap to readline control codes
12711323 switch (c ) {
12721324 case 'A' : // Up
@@ -1278,23 +1330,79 @@ int cli_loop(struct cli_def *cli, int sockfd) {
12781330 break ;
12791331
12801332 case 'C' : // Right
1281- c = CTRL ('F' );
1333+ if (strcmp (esc_buff , "1;5" ) == 0 ) {
1334+ nc = cli_word_right (cmd , l , cursor );
1335+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1336+ c = 0 ;
1337+ }
1338+ else {
1339+ c = CTRL ('F' );
1340+ }
12821341 break ;
12831342
12841343 case 'D' : // Left
1285- c = CTRL ('B' );
1344+ if (strcmp (esc_buff , "1;5" ) == 0 ) {
1345+ nc = cli_word_left (cmd , cursor );
1346+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1347+ c = 0 ;
1348+ }
1349+ else {
1350+ c = CTRL ('B' );
1351+ }
1352+ break ;
1353+
1354+ case 'H' : // Home
1355+ c = CTRL ('A' );
1356+ break ;
1357+
1358+ case 'F' : // End
1359+ c = CTRL ('E' );
1360+ break ;
1361+
1362+ case '~' : {
1363+ // Delete, do not remap to EOF if l==0
1364+ if (strcmp (esc_buff , "3" ) == 0 && l ) {
1365+ c = CTRL ('D' );
1366+ }
1367+ else {
1368+ c = 0 ;
1369+ }
12861370 break ;
1371+ }
12871372
12881373 default :
12891374 c = 0 ;
12901375 }
12911376
1292- esc = 0 ;
12931377 } else {
1294- esc = (c == '[' ) ? c : 0 ;
1378+
1379+ switch (c ) {
1380+
1381+ case 'b' : // Left by word
1382+ nc = cli_word_left (cmd , cursor );
1383+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1384+ break ;
1385+
1386+ case 'f' : // Right by word
1387+ nc = cli_word_right (cmd , l , cursor );
1388+ cursor = cli_move_cursor (cli , sockfd , cmd , l , cursor , nc );
1389+ break ;
1390+
1391+ case '[' :
1392+ esc = c ;
1393+ continue ;
1394+
1395+ default :
1396+ break ;
1397+ }
1398+ esc = 0 ;
12951399 continue ;
12961400 }
12971401 }
1402+ else {
1403+ memset (esc_buff , 0 , sizeof (esc_buff ));
1404+ esc_pos = 0 ;
1405+ }
12981406
12991407 if (c == 0 ) continue ;
13001408 if (c == '\n' ) continue ;
@@ -1304,7 +1412,7 @@ int cli_loop(struct cli_def *cli, int sockfd) {
13041412 break ;
13051413 }
13061414
1307- if (c == 27 ) {
1415+ if (c == 27 ) { // 0x1b ESC
13081416 esc = 1 ;
13091417 continue ;
13101418 }
@@ -1421,7 +1529,26 @@ int cli_loop(struct cli_def *cli, int sockfd) {
14211529 if (c == CTRL ('D' )) {
14221530 if (cli -> state == STATE_PASSWORD || cli -> state == STATE_ENABLE_PASSWORD ) break ;
14231531
1424- if (l ) continue ;
1532+ if (l ) {
1533+
1534+ if (cursor < l ) {
1535+ _write (sockfd , cmd + cursor + 1 , l - cursor - 1 );
1536+ _write (sockfd , " " , 1 );
1537+
1538+ // Move everything one char left
1539+ memmove (cmd + cursor , cmd + cursor + 1 , l - cursor - 1 );
1540+
1541+ l -- ;
1542+
1543+ // And reposition cursor
1544+ for (int i = l ; i >= cursor ; i -- ) _write (sockfd , "\b" , 1 );
1545+
1546+ // Set former last char to null
1547+ cmd [l ] = 0 ;
1548+ }
1549+
1550+ continue ;
1551+ }
14251552
14261553 l = -1 ;
14271554 break ;
0 commit comments