Some minor code refactors. Added tree structure manipulations and files autocompletion on Tab keypress

This commit is contained in:
Dmitriy Shishkov 2020-07-09 04:29:20 +05:00
parent 6f306a1b50
commit d8c665a625
14 changed files with 413 additions and 41 deletions

View File

@ -8,6 +8,7 @@ Work is still in porgress, buf when you will see a "finished" topic assigned to
* Command input with `left` and `right` arrow, `home` and `end` keys navigation and `backspace`, `delete` support * Command input with `left` and `right` arrow, `home` and `end` keys navigation and `backspace`, `delete` support
* Running commands in separate process and termination them with `ctrl+c` * Running commands in separate process and termination them with `ctrl+c`
* `cd` and `exit` builtin commands * `cd` and `exit` builtin commands
* Files autocompletion on `Tab` keypress
# Builtin commands # Builtin commands
* `cd`: changes current working directory to the one specified by user. If no arguments provided, shows error. * `cd`: changes current working directory to the one specified by user. If no arguments provided, shows error.
@ -18,3 +19,4 @@ Work is still in porgress, buf when you will see a "finished" topic assigned to
* Replace linux `echo` command with builtin one with support of environmental variables * Replace linux `echo` command with builtin one with support of environmental variables
* Environmental variables * Environmental variables
* `Ctrl+Z` running programm with `fd` * `Ctrl+Z` running programm with `fd`
* Commands autocompletion

15
include/complete.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _COMPLETE_H
#define _COMPLETE_H
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <features.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
void complete_line(int *pos, int *n, char **line, char **out);
#endif

View File

@ -14,8 +14,7 @@ enum keys {
BACKSPACE_KEY, BACKSPACE_KEY,
ENTER_KEY, ENTER_KEY,
TAB_KEY, TAB_KEY,
ESCAPE_KEY, ESCAPE_KEY
CTRL_C_KEY
}; };
void change_mode(int on); void change_mode(int on);

View File

@ -10,6 +10,7 @@ void home_key(int *pos);
void end_key(int *pos, int n); void end_key(int *pos, int n);
void backspace_key(int *pos, int *n, char **line); void backspace_key(int *pos, int *n, char **line);
void new_line(); void new_line();
void tab_key(int *pos, int *n, char **line);
void printable_key(int *pos, int *n, char c, char **line); void printable_key(int *pos, int *n, char c, char **line);
#endif #endif

View File

@ -23,6 +23,8 @@ void sig_handler();
int sh_cd(char **args); int sh_cd(char **args);
int sh_exit(char **args); int sh_exit(char **args);
char *compose_prompt();
#define BUILTIN_NUM sizeof(builtin) / sizeof(char *) #define BUILTIN_NUM sizeof(builtin) / sizeof(char *)
#endif #endif

24
include/tree.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _TREE_H
#define _TREE_H
#include <stdlib.h>
#include <string.h>
#define ALPHABET_SIZE 256
struct tree_node
{
struct tree_node *child[ALPHABET_SIZE];
int is_leaf;
};
struct tree_node *get_new_node();
void insert_tree(struct tree_node *root, char *key);
void free_tree(struct tree_node *root);
int search_tree(struct tree_node *root, char *key);
int is_last_node(struct tree_node *root);
size_t list_strings_containing(struct tree_node *root, char *key, char ***strings);
void get_all_substrings(struct tree_node *root, size_t *amount, char **curr_prefix, char ***strings);
#endif

View File

@ -7,7 +7,6 @@
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
void die(char *s);
void append_to_pos(char **str, int pos, char ch); void append_to_pos(char **str, int pos, char ch);
void remove_on_pos(char **str, int pos); void remove_on_pos(char **str, int pos);

93
src/complete.c Normal file
View File

@ -0,0 +1,93 @@
#include "../include/complete.h"
#include "../include/shell.h"
#include "../include/tree.h"
size_t get_dir_list(char ***dir_list)
{
size_t n = 0;
*dir_list = malloc(sizeof(char *) * n);
char *pwd = get_current_dir_name();
DIR *dir;
struct dirent *ent;
if ((dir = opendir(pwd)) == NULL)
{
perror("opendir");
return -1;
}
while ((ent = readdir(dir)) != NULL)
{
n++;
*dir_list = realloc(*dir_list, sizeof(char *) * n);
(*dir_list)[n - 1] = strdup(ent->d_name);
}
closedir(dir);
return n;
}
void complete_line(int *pos, int *n, char **line, char **out)
{
(*line)[*pos] = '\0';
*out = strdup("\x1b[2K");
char **dir_list;
size_t sz = get_dir_list(&dir_list);
char *curr_path, *tmp_line = strdup(*line);
if ((curr_path = strtok(tmp_line, " ")) != NULL && (*line)[*pos - 1] != ' ')
{
curr_path = strdup(curr_path);
char *tmp;
while ((tmp = strtok(NULL, " ")) != NULL)
{
free(curr_path);
curr_path = strdup(tmp);
}
struct tree_node *child_dirs_root = get_new_node();
for (size_t i = 0; i < sz; i++)
{
insert_tree(child_dirs_root, dir_list[i]);
}
free(dir_list);
sz = list_strings_containing(child_dirs_root, curr_path, &dir_list);
}
if (sz == 1)
{
*out = strdup(dir_list[0] + strlen(curr_path));
*pos += strlen(*out);
*n = *pos;
*line = realloc(*line, strlen(*line) + strlen(*out));
*line = strcat(*line, *out);
return;
}
for (int i = 0; i < sz; i++)
{
*out = realloc(*out, strlen(*out) + strlen(dir_list[i]) + 2);
*out = strcat(*out, dir_list[i]);
*out = strcat(*out, " ");
}
*out = realloc(*out, strlen(*out) + 2);
*out = strcat(*out, "\n");
char *prompt = compose_prompt();
*out = realloc(*out, strlen(*out) + strlen(prompt) + 1);
*out = strcat(*out, prompt);
*out = realloc(*out, strlen(*out) + *pos);
*out = strncat(*out, *line, *pos + 1);
*n = *pos;
}

View File

@ -72,22 +72,8 @@ char *read_line(char **line)
case TAB_KEY: case TAB_KEY:
{ {
// char *buff = malloc(1);
// buff[0] = '\0';
// size_t buff_size = 1;
// n += 2;
// append_to_buff(&buff, &buff_size, " ", 2);
// for (int i = 0; i < 2; i++)
// append_to_pos(line, pos, ' ');
// append_to_buff(&buff, &buff_size, "\0337", 2);
// pos += 2;
// append_to_buff(&buff, &buff_size, *line + pos, n - pos);
// append_to_buff(&buff, &buff_size, "\0338", 2);
// print_str(buff, buff_size);
// TODO: autocomplete // TODO: autocomplete
tab_key(&pos, &n, line);
} }
break; break;

View File

@ -1,7 +1,15 @@
#include "../include/keys.h" #include "../include/keys.h"
#include "../include/output.h" #include "../include/output.h"
#include "../include/utils.h" #include "../include/utils.h"
#include "../include/complete.h"
/**
* @brief Delete key action
*
* @param pos
* @param n
* @param line
*/
void delete_key(int pos, int *n, char **line) void delete_key(int pos, int *n, char **line)
{ {
if (pos < *n) if (pos < *n)
@ -22,6 +30,11 @@ void delete_key(int pos, int *n, char **line)
} }
} }
/**
* @brief Arrow left key action
*
* @param pos
*/
void move_left(int *pos) void move_left(int *pos)
{ {
if (*pos > 0) if (*pos > 0)
@ -31,6 +44,12 @@ void move_left(int *pos)
} }
} }
/**
* @brief Arrow right key action
*
* @param pos
* @param n
*/
void move_right(int *pos, int n) void move_right(int *pos, int n)
{ {
if (*pos < n) if (*pos < n)
@ -40,6 +59,11 @@ void move_right(int *pos, int n)
} }
} }
/**
* @brief Home key action
*
* @param pos
*/
void home_key(int *pos) void home_key(int *pos)
{ {
char *buff = malloc(1); char *buff = malloc(1);
@ -53,6 +77,12 @@ void home_key(int *pos)
*pos = 0; *pos = 0;
} }
/**
* @brief End key action
*
* @param pos
* @param n
*/
void end_key(int *pos, int n) void end_key(int *pos, int n)
{ {
char *buff = malloc(1); char *buff = malloc(1);
@ -67,6 +97,13 @@ void end_key(int *pos, int n)
*pos = n; *pos = n;
} }
/**
* @brief Backspace key action
*
* @param pos
* @param n
* @param line
*/
void backspace_key(int *pos, int *n, char **line) void backspace_key(int *pos, int *n, char **line)
{ {
if (*pos > 0) if (*pos > 0)
@ -89,11 +126,37 @@ void backspace_key(int *pos, int *n, char **line)
} }
} }
/**
* @brief Enter key action
*
*/
void new_line() void new_line()
{ {
print_str("\n", 1); print_str("\n", 1);
} }
void tab_key(int *pos, int *n, char **line)
{
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
char *options;
complete_line(pos, n, line, &options);
append_to_buff(&buff, &buff_size, options, strlen(options));
print_str(buff, buff_size);
}
/**
* @brief Any printable key action
*
* @param pos
* @param n
* @param c
* @param line
*/
void printable_key(int *pos, int *n, char c, char **line) void printable_key(int *pos, int *n, char c, char **line)
{ {
char *buff = malloc(1); char *buff = malloc(1);

View File

@ -1,5 +1,14 @@
#include "../include/output.h" #include "../include/output.h"
/**
* @brief Appends to buffer string
*
* @param buff
* @param buff_size
* @param ap
* @param ap_size
* @return size_t
*/
size_t append_to_buff(char **buff, size_t *buff_size, char *ap, size_t ap_size) size_t append_to_buff(char **buff, size_t *buff_size, char *ap, size_t ap_size)
{ {
*buff = realloc(*buff, *buff_size + ap_size); *buff = realloc(*buff, *buff_size + ap_size);
@ -11,6 +20,12 @@ size_t append_to_buff(char **buff, size_t *buff_size, char *ap, size_t ap_size)
return *buff_size; return *buff_size;
} }
/**
* @brief Prints buffer string
*
* @param buff
* @param size
*/
void print_str(char *buff, size_t size) void print_str(char *buff, size_t size)
{ {
write(STDOUT_FILENO, buff, size); write(STDOUT_FILENO, buff, size);

View File

@ -24,10 +24,9 @@ void process_command()
char *line = malloc(1); char *line = malloc(1);
line[0] = '\0'; line[0] = '\0';
if (getuid() == 0) char *prompt = compose_prompt();
print_str("# ", 2);
else print_str(prompt, strlen(prompt));
print_str("> ", 2);
line = read_line(&line); line = read_line(&line);
@ -154,3 +153,19 @@ int sh_exit(char **args)
{ {
exit(0); exit(0);
} }
char *compose_prompt()
{
char *prompt = malloc(1);
prompt[0] = '\0';
prompt = realloc(prompt, strlen(prompt) + 3);
if (getuid() == 0)
{
prompt = strcat(prompt, "\n# ");
}
else
prompt = strcat(prompt, "\n% ");
return prompt;
}

142
src/tree.c Normal file
View File

@ -0,0 +1,142 @@
#include "../include/tree.h"
struct tree_node *get_new_node()
{
struct tree_node *node = (struct tree_node *)malloc(sizeof(struct tree_node));
node->is_leaf = 0;
for (int i = 0; i < ALPHABET_SIZE; i++)
{
node->child[i] = NULL;
}
return node;
}
void insert_tree(struct tree_node *root, char *key)
{
struct tree_node *current = root;
for (int i = 0; i < strlen(key); i++)
{
char index = key[i];
if (current->child[index] == NULL)
{
current->child[index] = get_new_node();
}
current = current->child[index];
}
current->is_leaf = 1;
}
void free_tree(struct tree_node *root)
{
for (int i = 0; i < ALPHABET_SIZE; i++)
{
if (root->child[i] != NULL)
{
free_tree(root->child[i]);
}
}
free(root);
return;
}
int search_tree(struct tree_node *root, char *key)
{
struct tree_node *current = root;
for (int i = 0; i < strlen(key); i++)
{
char index = key[i];
if (current->child[index] == NULL)
{
return 0;
}
current = current->child[index];
}
return (current != NULL && current->is_leaf);
}
int is_last_node(struct tree_node *root)
{
for (int i = 0; i < ALPHABET_SIZE; i++)
if (root->child[i] != NULL)
return 0;
return 1;
}
size_t list_strings_containing(struct tree_node *root, char *key, char ***strings)
{
size_t amount = 0;
*strings = malloc(amount * sizeof(char *));
struct tree_node *current = root;
for (int level = 0; level < strlen(key); level++)
{
int index = key[level];
if (current->child[index] == NULL)
return amount;
current = current->child[index];
}
int key_presents = current->is_leaf;
int is_last = is_last_node(current);
if (key_presents && is_last)
{
amount++;
*strings = (char **)realloc(*strings, amount * sizeof(char *));
(*strings)[0] = strdup(key);
return amount;
}
if (is_last == 0)
{
char *prefix = strdup(key);
prefix = realloc(prefix, strlen(prefix) + 2);
get_all_substrings(current, &amount, &prefix, strings);
return amount;
}
return -1;
}
void get_all_substrings(struct tree_node *root, size_t *amount, char **curr_prefix, char ***strings)
{
if (root->is_leaf)
{
(*amount)++;
*strings = (char **)realloc(*strings, *amount * sizeof(char *));
(*strings)[*amount - 1] = strdup(*curr_prefix);
}
if (is_last_node(root))
return;
for (int index = 0; index < ALPHABET_SIZE; index++)
{
if (root->child[index] != NULL)
{
*curr_prefix = strncat(*curr_prefix, (char *)(&index), 1);
get_all_substrings(root->child[index], amount, curr_prefix, strings);
(*curr_prefix)[strlen(*curr_prefix) - 1] = '\0';
}
}
}

View File

@ -1,15 +1,17 @@
#include "../include/utils.h" #include "../include/utils.h"
void die(char *s) /**
{ * @brief Appends a character to the specified position of string
perror(s); *
exit(1); * @param str
} * @param pos
* @param ch
*/
void append_to_pos(char **str, int pos, char ch) void append_to_pos(char **str, int pos, char ch)
{ {
size_t len = strlen(*str); size_t len = strlen(*str);
if (pos <= len + 1)
{
*str = realloc(*str, len + 2); *str = realloc(*str, len + 2);
for (int i = len; i >= pos; i--) for (int i = len; i >= pos; i--)
@ -18,11 +20,22 @@ void append_to_pos(char **str, int pos, char ch)
} }
(*str)[pos] = ch; (*str)[pos] = ch;
(*str)[len + 1] = '\0'; (*str)[len + 1] = '\0';
}
else
fprintf(stderr, "Can't add \"%c\" symbol outside of the string\n", ch);
} }
/**
* @brief Removes a character located in the specified position
*
* @param str
* @param pos
*/
void remove_on_pos(char **str, int pos) void remove_on_pos(char **str, int pos)
{ {
size_t len = strlen(*str); size_t len = strlen(*str);
if (pos <= len)
{
for (int i = pos - 1; i <= len; i++) for (int i = pos - 1; i <= len; i++)
{ {
@ -30,4 +43,7 @@ void remove_on_pos(char **str, int pos)
} }
(*str)[len] = '\0'; (*str)[len] = '\0';
*str = realloc(*str, len); *str = realloc(*str, len);
}
else
fprintf(stderr, "Can't remove symbol outside the string\n");
} }