back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/file_suggestions.c
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-06-20 18:38:11 +0300
committerscratko <m@scratko.xyz>2024-06-21 01:00:37 +0300
commitef4604d9c146dbab1a653f66209103b74e6feb36 (patch)
tree62562f68a0080cd65d94a525110180dab3b99919 /file_suggestions.c
parent8f4f87eabec13330a2b3a974975053c1e4632a11 (diff)
downloadshell-ef4604d9c146dbab1a653f66209103b74e6feb36.tar.gz
shell-ef4604d9c146dbab1a653f66209103b74e6feb36.tar.bz2
shell-ef4604d9c146dbab1a653f66209103b74e6feb36.zip
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).
Diffstat (limited to 'file_suggestions.c')
-rw-r--r--file_suggestions.c258
1 files changed, 258 insertions, 0 deletions
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 <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+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;
+}