From ef4604d9c146dbab1a653f66209103b74e6feb36 Mon Sep 17 00:00:00 2001 From: scratko Date: Thu, 20 Jun 2024 18:38:11 +0300 Subject: Shell-edit release Autocomplete program and file names (tab). Moving the cursor to edit commands (left and right arrows). Deleting a character in a command (backspace). --- file_suggestions.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 file_suggestions.c (limited to 'file_suggestions.c') diff --git a/file_suggestions.c b/file_suggestions.c new file mode 100644 index 0000000..528d717 --- /dev/null +++ b/file_suggestions.c @@ -0,0 +1,258 @@ +#include "file_suggestions.h" +#include "readline.h" + +#include +#include +#include +#include +#include + +extern char **environ; + +static void clean_name_and_path(char *name, char *path) +{ + if(name) + free(name); + if(strcmp(path, ".")) + free(path); +} + +static int print_all_filenames(const char *name, const char *path) +{ + DIR *dir; + struct dirent *dent; + char *next_filename; + int first_suggestion; + + first_suggestion = 1; + + dir = opendir(path); + if(!dir) { + perror(path); + return 0; + } + while((dent = readdir(dir)) != NULL) { + next_filename = dent->d_name; + if(first_suggestion) { + putchar('\n'); + first_suggestion = 0; + } + printf("%s\n", next_filename); + } + closedir(dir); + return 1; +} + +static int matchmaking(const char *name, const char *path) +{ + DIR *dir; + struct dirent *dent; + char *next_filename; + int mismatch_status, first_suggestion, something_found; + + mismatch_status = 0; + something_found = 0; + first_suggestion = 1; + + dir = opendir(path); + if(!dir) { + perror(path); + return 0; + } + while((dent = readdir(dir)) != NULL) { + next_filename = dent->d_name; + int i; + for(i = 0; name[i] && next_filename[i]; ++i) { + if(name[i] != next_filename[i]) { + mismatch_status = 1; + break; + } + } + if(!mismatch_status && name[i] == '\0') { + if(first_suggestion) { + putchar('\n'); + first_suggestion = 0; + } + printf("%s\n", next_filename); + if(!something_found) + something_found = 1; + } + mismatch_status = 0; + } + closedir(dir); + return something_found; +} + +static void extract_path_and_name(char **path, char **name, + struct readline_type *readline, + int start_filename_idx) +{ + int i, j, k; + + for(i = readline->last_element_index; i >= start_filename_idx; --i) { + if(readline->arr[i] == '/') + break; + } + *path = malloc(i - start_filename_idx+ 2); + *name = malloc(readline->last_element_index - i + 1); + + /* extract path */ + for(j = start_filename_idx, k = 0; j <= i; ++j, ++k) + (*path)[k] = readline->arr[j]; + (*path)[k] = '\0'; + + /* extract name */ + for(j = i + 1, k = 0; j <= readline->last_element_index; ++j, ++k) + (*name)[k] = readline->arr[j]; + (*name)[k] = '\0'; +} + +static void copy_filename(char *dest, char *source, int start_idx, int last_idx) +{ + int i, j; + for(i = 0, j = start_idx; j <= last_idx; ++i, ++j) + dest[i] = source[j]; +} + +static void extract_only_filename(char **name, struct readline_type *readline, + int start_filename_idx) +{ + int size = readline->last_element_index - start_filename_idx + 2; + *name = malloc(size); + copy_filename(*name, readline->arr, start_filename_idx, + readline->last_element_index); + (*name)[size-1] = '\0'; +} + +int get_filename_match(struct readline_type *readline, int start_filename_idx) +{ + int extract_path_status, match_result; + + char *name = NULL; + extract_path_status = 0; + match_result = 0; + /* default path */ + char *path = "."; + int i; + + for(i = start_filename_idx; i <= readline->last_element_index; ++i) { + if(readline->arr[i] == '/') { + extract_path_status = 1; + break; + } + } + /* not empty filename */ + if(readline->arr[start_filename_idx] != ' ') + extract_path_status ? extract_path_and_name(&path, &name, readline, + start_filename_idx) : + extract_only_filename(&name, readline, + start_filename_idx); + match_result = + (name == NULL && !strcmp(path, ".")) ? print_all_filenames(name, path) : + matchmaking(name, path); + clean_name_and_path(name, path); + return match_result; +} + +static int is_token(int ch) +{ + return ch == ' ' || ch == '|' || ch == '&' || ch == ';' || ch == '<' || + ch == '>'; +} + +int get_start_filename_idx(struct readline_type *readline) +{ + /* empty filename */ + if(readline->last_element_index == ' ') + return readline->last_element_index; + + int i; + for(i = readline->last_element_index; i != -1; --i) { + if(is_token(readline->arr[i])) + return ++i; + } + return 0; +} + +/* + * 1 searching for suggestions for file name, + * 0 searching for program name from PATH + */ +int suggestions_for_filename(struct readline_type *readline) +{ + int token_status, i; + token_status = 0; + + for(i = readline->last_element_index; i != -1; --i) { + if(is_token(readline->arr[i])) { + token_status = 1; + continue; + } + if(token_status && !is_token(readline->arr[i])) + return 1; + } + return 0; +} + +int find_separator_index(char *path, int begin_idx) +{ + int i; + for(i = begin_idx; path[i] != ':' && path[i]; ++i) + {} + return i; +} + +int get_program_name_match(struct readline_type *readline) +{ + char *name = NULL; + char *path = NULL; + char *path_variable = NULL; + int size, begin_idx, end_idx, result; + + begin_idx = 0; + end_idx = 0; + result = 0; + size = readline->last_element_index + 2; + name = malloc(size); + + /* extracting prog name */ + int i; + for(i = 0; i <= readline->last_element_index; ++i) + name[i] = readline->arr[i]; + name[i] = '\0'; + + /* searching PATH */ + for(i = 0; environ[i]; ++i) + if(!strncmp(environ[i], "PATH", 4)) { + path_variable = environ[i]; + break; + } + if(!path_variable) + return 0; + + /* skipping PATH= */ + path_variable = path_variable + 5; + + /* directory search by separator (:) */ + end_idx = find_separator_index(path_variable, begin_idx); + size = end_idx + 1; + path = malloc(size); + copy_filename(path, path_variable, begin_idx, end_idx - 1); + path[end_idx] = '\0'; + result = matchmaking(name, path) ? 1 : result; + free(path); + + /* if end_idx is beginning with ':' */ + while(path_variable[end_idx]) { + begin_idx = end_idx + 1; + end_idx = find_separator_index(path_variable, begin_idx); + size = end_idx + 1; + path = malloc(size); + copy_filename(path, path_variable, begin_idx, end_idx - 1); + path[end_idx] = '\0'; + result = matchmaking(name, path) ? 1 : result; + free(path); + } + free(name); + return result; +} -- cgit v1.2.3