initial commit

This commit is contained in:
Dmitriy Shishkov 2020-07-08 18:13:58 +05:00
commit b702e6e92e
14 changed files with 643 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
build/
.vscode/
a.out
val/
tmp*

29
Makefile Normal file
View File

@ -0,0 +1,29 @@
TARGET_EXEC ?= msh
BUILD_DIR ?= ./build
SRC_DIRS ?= ./src
SRCS := $(shell find $(SRC_DIRS) -name *.c)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) -ggdb3 $(OBJS) -o $@ $(LDFLAGS)
$(BUILD_DIR)/%.c.o: %.c
$(MKDIR_P) $(dir $@)
$(CC) -ggdb3 $(CPPFLAGS) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
$(RM) -r $(BUILD_DIR)
-include $(DEPS)
MKDIR_P ?= mkdir -p

0
README.md Normal file
View File

25
include/input.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _INPUT_H
#define _INPUT_H
#include <termio.h>
#include <unistd.h>
#include <stdlib.h>
enum keys {
DELETE_KEY = 1000,
LEFT_KEY,
RIGHT_KEY,
HOME_KEY,
END_KEY,
BACKSPACE_KEY,
ENTER_KEY,
TAB_KEY,
ESCAPE_KEY,
CTRL_C_KEY
};
void change_mode(int on);
char *read_line(char **line);
int process_keypress(char c);
#endif

15
include/keys.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _KEYS_H
#define _KEYS_H
#include <stdlib.h>
void delete_key(int pos, int *n, char **line);
void move_left(int *pos);
void move_right(int *pos, int n);
void home_key(int *pos);
void end_key(int *pos, int n);
void backspace_key(int *pos, int *n, char **line);
void new_line();
void printable_key(int *pos, int *n, char c, char **line);
#endif

11
include/output.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _OUTPUT_H
#define _OUTPUT_H
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
size_t append_to_buff(char **buff, size_t *buff_size, char *ap, size_t ap_size);
void print_str(char *buff, size_t size);
#endif

28
include/shell.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _SHELL_H
#define _SHELL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFF_SIZE 1024
#define ARG_SIZE 32
char *read_line();
void process_line(char *line, char ***args);
int launch(char **args);
void process_command();
int execute(char **args);
void sig_handler();
int sh_cd(char **args);
int sh_exit(char **args);
#define BUILTIN_NUM sizeof(builtin) / sizeof(char *)
#endif

14
include/utils.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef _UTILS_H
#define _UTILS_H
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void die(char *s);
void append_to_pos(char **str, int pos, char ch);
void remove_on_pos(char **str, int pos);
#endif

173
src/input.c Normal file
View File

@ -0,0 +1,173 @@
#include "../include/input.h"
#include "../include/keys.h"
/**
* @brief Switches console raw mode
*
* @param on
*/
void change_mode(int on)
{
static struct termios oldt, newt;
if (on)
{
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
}
else
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
/**
* @brief Reads line from user input and puts it into "line"
*
* @param line
* @return char*
*/
char *read_line(char **line)
{
int c;
int n = 0, pos = 0;
while (read(STDIN_FILENO, &c, 1))
{
c = process_keypress(c);
if (c)
{
switch (c)
{
case DELETE_KEY:
delete_key(pos, &n, line);
break;
case LEFT_KEY:
move_left(&pos);
break;
case RIGHT_KEY:
move_right(&pos, n);
break;
case HOME_KEY:
home_key(&pos);
break;
case END_KEY:
end_key(&pos, n);
break;
case BACKSPACE_KEY:
backspace_key(&pos, &n, line);
break;
case ENTER_KEY:
new_line();
return *line;
break;
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
}
break;
case ESCAPE_KEY:
break;
default:
if ((c > 31 && c < 127) || (c > 127 && c < 255))
printable_key(&pos, &n, (char)c, line);
break;
}
}
}
}
/**
* @brief Processes user keypress, filters special characters
*
* @param c
* @return int
*/
int process_keypress(char c)
{
char seq[3];
if (c == '\x1b')
{
for (int i = 0; i < 2; i++)
if (read(STDIN_FILENO, &seq[i], 1) == -1)
return ESCAPE_KEY;
if (seq[0] == '[')
{
if (seq[1] >= '0' && seq[1] <= '9')
{
read(STDIN_FILENO, &seq[2], 1);
if (seq[2] == '~')
{
switch (seq[1])
{
case '3':
return DELETE_KEY;
break;
}
}
}
switch (seq[1])
{
case 'D':
return LEFT_KEY;
break;
case 'C':
return RIGHT_KEY;
break;
case 'H':
return HOME_KEY;
break;
case 'F':
return END_KEY;
break;
}
}
return ESCAPE_KEY;
}
else if (c == 127)
{
return BACKSPACE_KEY;
}
else if (c == '\n')
{
return ENTER_KEY;
}
else if (c == '\t')
{
return TAB_KEY;
}
else
{
return c;
}
}

114
src/keys.c Normal file
View File

@ -0,0 +1,114 @@
#include "../include/keys.h"
#include "../include/output.h"
#include "../include/utils.h"
void delete_key(int pos, int *n, char **line)
{
if (pos < *n)
{
remove_on_pos(line, pos + 1);
(*n)--;
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
append_to_buff(&buff, &buff_size, "\0337", 2);
append_to_buff(&buff, &buff_size, *line + pos, *n - pos);
append_to_buff(&buff, &buff_size, " ", 1);
append_to_buff(&buff, &buff_size, "\0338", 2);
print_str(buff, buff_size);
}
}
void move_left(int *pos)
{
if (*pos > 0)
{
print_str("\033[D", 3);
(*pos)--;
}
}
void move_right(int *pos, int n)
{
if (*pos < n)
{
print_str("\033[C", 3);
(*pos)++;
}
}
void home_key(int *pos)
{
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
for (int i = 0; i < *pos; i++)
append_to_buff(&buff, &buff_size, "\033[D", 3);
print_str(buff, buff_size);
*pos = 0;
}
void end_key(int *pos, int n)
{
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
for (int i = 0; i < n - *pos; i++)
append_to_buff(&buff, &buff_size, "\033[C", 3);
print_str(buff, buff_size);
*pos = n;
}
void backspace_key(int *pos, int *n, char **line)
{
if (*pos > 0)
{
remove_on_pos(line, *pos);
(*n)--;
(*pos)--;
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
append_to_buff(&buff, &buff_size, "\033[D", 3);
append_to_buff(&buff, &buff_size, "\0337", 2);
append_to_buff(&buff, &buff_size, *line + *pos, *n - *pos);
append_to_buff(&buff, &buff_size, " ", 1);
append_to_buff(&buff, &buff_size, "\0338", 2);
print_str(buff, buff_size);
}
}
void new_line()
{
print_str("\n", 1);
}
void printable_key(int *pos, int *n, char c, char **line)
{
char *buff = malloc(1);
buff[0] = '\0';
size_t buff_size = 1;
(*n)++;
append_to_buff(&buff, &buff_size, &c, 1);
append_to_pos(line, *pos, c);
append_to_buff(&buff, &buff_size, "\0337", 2);
(*pos)++;
append_to_buff(&buff, &buff_size, *line + *pos, *n - *pos);
append_to_buff(&buff, &buff_size, "\0338", 2);
print_str(buff, buff_size);
}

23
src/main.c Normal file
View File

@ -0,0 +1,23 @@
#include <signal.h>
#include "../include/input.h"
#include "../include/utils.h"
#include "../include/shell.h"
void exit_shell()
{
change_mode(0);
}
int main(int argc, char **argv)
{
change_mode(1);
signal(SIGINT, SIG_IGN);
atexit(exit_shell);
while (1)
{
process_command();
}
}

17
src/output.c Normal file
View File

@ -0,0 +1,17 @@
#include "../include/output.h"
size_t append_to_buff(char **buff, size_t *buff_size, char *ap, size_t ap_size)
{
*buff = realloc(*buff, *buff_size + ap_size);
memcpy(*buff + *buff_size - 1, ap, ap_size);
*buff_size += ap_size;
(*buff)[*buff_size - 1] = '\0';
return *buff_size;
}
void print_str(char *buff, size_t size)
{
write(STDOUT_FILENO, buff, size);
}

156
src/shell.c Normal file
View File

@ -0,0 +1,156 @@
#include "../include/shell.h"
#include "../include/input.h"
#include "../include/output.h"
/* Global definitions */
char *builtin[] = {
"cd",
"exit"};
int (*builtin_func[])(char **) = {
&sh_cd,
&sh_exit};
/**
* @brief Function for main loop. It prints prompt, reads user's input and executes it
*/
void process_command()
{
char **args;
int status;
do
{
char *line = malloc(1);
line[0] = '\0';
if (getuid() == 0)
print_str("# ", 2);
else
print_str("> ", 2);
line = read_line(&line);
process_line(line, &args);
status = execute(args);
free(line);
free(args);
} while (status);
}
/**
* @brief Extracts command and its arguments from line
*
* @param line
* @param args
*/
void process_line(char *line, char ***args)
{
int buff_size = ARG_SIZE, pos = 0;
*args = malloc(buff_size * sizeof(char *));
char *tok, *rest = strdup(line);
while ((tok = strtok_r(rest, " \t", &rest)) != NULL)
{
(*args)[pos] = tok;
pos++;
if (pos > buff_size)
{
buff_size += ARG_SIZE;
*args = realloc(*args, buff_size * sizeof(char *));
if (*args == NULL)
{
fprintf(stderr, "myshell: allocation error");
exit(EXIT_FAILURE);
}
}
}
(*args)[pos] = NULL;
}
/**
* @brief Executes either builtin or system command
*
* @param args
* @return int
*/
int execute(char **args)
{
if (args[0] == NULL)
return 1;
for (int i = 0; i < BUILTIN_NUM; i++)
if (strcmp(args[0], builtin[i]) == 0)
return (*builtin_func[i])(args);
return launch(args);
}
/**
* @brief Launches separate process and executes specified command
*
* @param args
* @return int
*/
int launch(char **args)
{
pid_t pid, wpid;
int status;
pid = fork();
if (pid == 0)
{
change_mode(0);
signal(SIGINT, SIG_DFL);
if (execvp(args[0], args) < 0)
{
perror("myshell");
}
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
perror("myshell");
}
else
do
wpid = waitpid(pid, &status, WUNTRACED);
while (!WIFEXITED(status) && !WIFSIGNALED(status));
change_mode(1);
return status;
}
/**
* @brief Shell builtin command. Changes current working directory
*
* @param args
* @return int
*/
int sh_cd(char **args)
{
if (args[1] == NULL)
fprintf(stderr, "myshell: expected arguments for \"cd\"\n");
else if (chdir(args[1]) < 0)
perror("myshell");
return 1;
}
/**
* @brief Shell builtin command. Exits shell
*
* @param args
* @return int
*/
int sh_exit(char **args)
{
exit(0);
}

33
src/utils.c Normal file
View File

@ -0,0 +1,33 @@
#include "../include/utils.h"
void die(char *s)
{
perror(s);
exit(1);
}
void append_to_pos(char **str, int pos, char ch)
{
size_t len = strlen(*str);
*str = realloc(*str, len + 2);
for (int i = len; i >= pos; i--)
{
(*str)[i + 1] = (*str)[i];
}
(*str)[pos] = ch;
(*str)[len + 1] = '\0';
}
void remove_on_pos(char **str, int pos)
{
size_t len = strlen(*str);
for (int i = pos - 1; i <= len; i++)
{
(*str)[i] = (*str)[i + 1];
}
(*str)[len] = '\0';
*str = realloc(*str, len);
}