Browse Source

Completed intelligent in-command navigate and keyboard handlers

Ivan Arkhipov 5 years ago
parent
commit
ae00d1163b
4 changed files with 135 additions and 16 deletions
  1. 117 13
      esh_main_loop.c
  2. 12 2
      esh_misc.c
  3. 1 0
      esh_misc.h
  4. 5 1
      esh_types.h

+ 117 - 13
esh_main_loop.c

@@ -2,27 +2,131 @@
 #include "esh_misc.h"
 
 void EShRunLoop() {
+    struct termios orig_term_attr;
+    struct termios new_term_attr;
+
+    /* set the terminal to raw mode */
+    tcgetattr(fileno(stdin), &orig_term_attr);
+    memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
+    new_term_attr.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
+    new_term_attr.c_cc[VTIME] = 0;
+    new_term_attr.c_cc[VMIN] = 1;
+    tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
+
 	for (;;) {
 		EShShowMsg();
-		char* command = NULL;
+		char* command = malloc(esh_info_global->max_command_length);
+		memset(command, 0, esh_info_global->max_command_length);
 		int command_length = 0;
-		ssize_t buf_size = 0;
-
-	    while ((command_length = getline(&command, &buf_size, stdin)) == 0) {
-	    	// repeating
-	    }
-
-	    if (command_length == -1)
-	    	break;
+		int current_command_pos = 0;
+
+		char input_char;
+
+		while (input_char = fgetc(stdin)) {
+			if (input_char == 10) {
+				printf("\n");
+				break; // 'Enter' pressed;
+			}
+
+			if (input_char == EOF) {
+				continue;
+			}
+
+			if (input_char == '\033') { // Arrow up/down/left/right sequence
+				fgetc(stdin);
+			    switch(fgetc(stdin)) {
+			        case 'A': // Up
+			        	break;
+			        case 'B': // Down
+			        	break;
+			        case 'C': // Right
+			        	if (current_command_pos < command_length) {
+			        		printf("%c", command[current_command_pos]);
+		        			++current_command_pos;
+			        	}
+			            break;
+			        case 'D': // Left
+			        	if (current_command_pos > 0) {
+			        		printf("\b");
+			        		--current_command_pos;
+			        	}
+			            break;
+			    }
+			    continue;
+			}
+
+			if (input_char == 4) {
+				// Ctrl + d
+				printf("\n");
+				exit(0);
+			}
+
+			if (input_char == 127) {
+				// Backspace
+				if (current_command_pos > 0) {
+					for (int i = current_command_pos - 1; i < command_length; ++i) {
+						command[i] = command[i + 1];
+					}
+					command[command_length - 1] = '\0';
+					printf("\b");
+					printf("%s", command + current_command_pos - 1);
+					printf(" \b");
+
+					--current_command_pos;
+					--command_length;
+
+					for (int i = command_length - 1; i >= current_command_pos; --i) {
+						printf("\b");
+					}
+				}
+				continue;
+			}
+
+
+			if (input_char == 3) {
+				// Ctrl + c
+				printf("\n");
+				exit(0);
+			}
+
+			if (EShIsShellLetter(input_char)) {
+				for (int i = command_length; i >= current_command_pos; --i) {
+					command[i + 1] = command[i];
+				}
+				command[current_command_pos] = input_char;
+				printf("%s", command + current_command_pos);
+				++current_command_pos;
+				++command_length;
+				for (int i = command_length; i > current_command_pos; --i) {
+					printf("\b");
+				}
+				continue;
+			}
+
+		}
+
+		if (command_length == 0) {
+			printf("\n");
+			continue;
+		}
+
+		int pid = fork();
+		if (pid == 0) {
+			execlp(command, command, NULL);
+			printf("Esh: run command %s failed.\n", command);
+			exit(1); // execlp failed
+		} else {
+			waitpid(pid, NULL, 0);
+			printf("\n");
+		}
 	}
+
+    /* restore the original terminal attributes */
+    tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
 }
 
 void EShShowMsg() {
 	EShUpdateInviteMessage();
-	EShUpdateInviteMessage();
-	EShUpdateInviteMessage();
-	EShUpdateInviteMessage();
-
 	printf("%s", esh_info_global->invite_message);
 }
 

+ 12 - 2
esh_misc.c

@@ -25,7 +25,7 @@ void EShUpdateInviteMessage() {
 void EShShowHelpAndExit() {
 	printf(ANSI_COLOR_RED "ESh (Endevir Shell) build %.8s\n" ANSI_COLOR_RESET
 		   "\n"
-		   "Usage: esh [-h] [-config FILENAME] [-history FILENAME] [-history_limit INT] [-max_active_jobs INT]\n"
+		   "Usage: esh [-h] [-config FILENAME] [-history FILENAME] [-history_limit INT] [-max_active_jobs INT] [-max_command_length INT]\n"
 		   "\n"
 		   "Description:\n"
 		   "This is a simple shell, written in C\n"
@@ -43,4 +43,14 @@ void EShShowHelpAndExit() {
 		   esh_info_global->build_ref);
 
    	exit(0);
-}
+}
+
+int EShIsShellLetter(char c) {
+	return (c >= 'a' && c <= 'z') || 
+		   (c >= 'A' && c <= 'Z') || 
+		   (c >= 'а' && c <= 'я') || 
+		   (c >= 'А' && c <= 'Я') || 
+		   (c >= '0' && c <= '9') || 
+		   (c >= ' ' && c <= '/') ||
+		   (c >= ':' && c <= '?');
+} 

+ 1 - 0
esh_misc.h

@@ -17,5 +17,6 @@ void EShUpdateInviteMessage(); // Update input invite message (ex. should be cal
 
 void EShShowHelpAndExit(); // Prints help to stdout and exits
 
+int EShIsShellLetter(char c); // Returns 1 if c is 'good' char (not a command char, but a written letter) 
 
 #endif // ESH_MISC_H

+ 5 - 1
esh_types.h

@@ -6,6 +6,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <limits.h>
+#include <termios.h>
+
+#include <sys/wait.h>
 
 #ifndef ESH_TYPES_H
 #define ESH_TYPES_H
@@ -34,6 +37,7 @@ typedef struct {
 
 	pid_t job_pid;
 
+	int stdin_pipe[2];
 	int stdout_pipe[2]; // 0 is for writing in child process and for reading in parent process.
 	int stderr_pipe[2];	// 1 is for writing in parent process and for reading in child process.
 } EShJob;
@@ -48,7 +52,7 @@ typedef struct {
 	int history_limit; // max lines of commands, which can be stored (changeable via settings/cmd line args)
 	int max_jobs_number; // max active jobs (foreground & background) limit (changeable via settings/cmd line args)
 	int max_command_length; // max length of single command (changeable via settings/cmd line args)
-	
+
 	int max_invite_message_len;
 	char* invite_message;