Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Командный итерпретатор
- *
- * main.c
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <string.h>
- #include <unistd.h>
- #include <limits.h>
- #include <sys/wait.h>
- #define START_SIZE 20
- #define MAX_CMD_LEN 10000
- #define MAX_NAME_LEN 256
- #define MAX_REDIRS 3
- char** read_command();
- char* read_word();
- int is_correct(char** command);
- int is_redirect(char* word);
- int not_special_word(char* word);
- int count_command(char** command);
- int execute_command(char** command);
- int set_redir(int redir, char* filename, int in, int out);
- int special_command(char** command);
- int set_redirs(int redirs[MAX_REDIRS],
- char* filenames[MAX_REDIRS], int in, int out);
- void free_str_array_by_len(char*** str_array_ptr, int word_num);
- void free_str_array(char*** str_array);
- void inter_handler(int signum);
- pid_t* child_table = NULL;
- size_t child_num = 0;
- /* Возвращает -1 в случае ошибки, 0 в случае успешной работы. */
- int main()
- {
- char cwd[PATH_MAX];
- char user_name[MAX_NAME_LEN];
- char host_name[MAX_NAME_LEN];
- char** command = NULL;
- int status;
- signal(SIGINT, inter_handler);
- printf("Command interpreter.\n\t'q' to exit\n\tСtrl+C to terminate launched processes\n\tcd <dir> to change directory\n");
- /* Получение имен */
- if (getlogin_r(user_name, MAX_NAME_LEN) != 0 ||
- gethostname(host_name, MAX_NAME_LEN) != 0)
- {
- printf("cmd: error in OS!\n");
- return -1;
- }
- while (1)
- {
- /* Получение рабочей директории */
- if (getcwd(cwd, sizeof(cwd)) == NULL)
- {
- printf("cmd: error in OS!\n");
- return -1;
- }
- printf("\n\x1B[32m%s@%s\e[0m \e[34m%s\e[0m > ", user_name, host_name, cwd);
- /* Читаем строку-команду */
- command = read_command();
- if (is_correct(command))
- {
- /* Команда может быть специальной, тогда вернется 1,
- * обработка внутри special_command */
- if (!special_command(command))
- {
- /* Обработка неспециальной команды */
- execute_command(command);
- /* Ждем, пока сыновья не закончат работу */
- while (wait(&status) > 0) sleep(0.1);
- free(child_table);
- child_table = NULL;
- command = NULL;
- child_num = 0;
- }
- }
- else
- {
- printf("cmd: wrong syntex!\n");
- }
- }
- return 0;
- }
- /* Читает команду интерпретатору. Возвращает указатель на массив слов
- * команды. В случае ошибки возвращает NULL. */
- char** read_command()
- {
- char** command;
- int command_len = 0;
- int word_num = 0;
- int max_words = START_SIZE;
- command = (char**) malloc(max_words * sizeof(char*));
- if (command == NULL)
- {
- printf("cmd: malloc error.\n");
- return NULL;
- }
- while (command_len < MAX_CMD_LEN)
- {
- command[word_num] = read_word();
- if (command[word_num] == NULL)
- return command;
- command_len += strlen(command[word_num]);
- word_num++;
- if (word_num >= max_words)
- {
- max_words *= 2;
- command = (char**) realloc(command, max_words * sizeof(char*));
- if (command == NULL)
- {
- printf("cmd: realloc error.\n");
- return NULL;
- }
- }
- }
- return command;
- }
- /* Читает слово команды. Возвращает указатель слово.
- * В случае ошибки либо конца команды возвращает NULL. */
- char* read_word()
- {
- char* word;
- int c;
- int word_len = 0;
- int max_syms = START_SIZE;
- word = (char*) malloc(max_syms * sizeof(char));
- if (word == NULL)
- {
- printf("cmd: malloc error.\n");
- return NULL;
- }
- while (word_len < MAX_CMD_LEN)
- {
- c = getchar();
- if (c == EOF || c == '\n' || c == ' ')
- {
- if (word_len == 0)
- {
- free(word);
- return NULL; /* Конец команды */
- }
- else
- {
- word[word_len] = '\0';
- if (c != ' ') ungetc(c, stdin);
- return word;
- }
- }
- else
- {
- word[word_len] = c;
- word_len++;
- }
- if (word_len >= max_syms)
- {
- max_syms *= 2;
- word = (char*) realloc(word, max_syms * sizeof(char));
- if (word == NULL)
- {
- printf("cmd: realloc error\n");
- return NULL;
- }
- }
- }
- return word;
- }
- /* Проверяет корректность синтаксиса команды. Возвращает 1, если синтаксис
- * корректен, иначе 0. */
- int is_correct(char** command)
- {
- int i = 0;
- int j = 0;
- int redirects[MAX_REDIRS];
- int redir;
- int redir_num = 0;
- if (command == NULL) return 0;
- while (command[i] != NULL)
- {
- /* Первое слово в блоке - имя утилиты */
- if (not_special_word(command[i]))
- {
- /* Далее могут идти аргументы для утилиты */
- i++;
- while (not_special_word(command[i])) i++;
- /* Далее могут идти перенаправления */
- redir_num = 0;
- for (j = 0; j < MAX_REDIRS; j++) redirects[j] = 0;
- while (redir = is_redirect(command[i]))
- {
- if (redir_num >= MAX_REDIRS) return 0;
- i++;
- if (!not_special_word(command[i])) return 0;
- /* Не должно быть повторений */
- for (j = 0; j < MAX_REDIRS; j++)
- {
- if (redirects[j] == redir) return 0;
- /* перенаправления > и >> несовместимы */
- if (redir == 2 && redirects[j] == 3) return 0;
- if (redir == 3 && redirects[j] == 2) return 0;
- }
- redirects[redir_num] = redir;
- redir_num++;
- i++;
- }
- /* Следующее слово - конец блока "|" */
- if (command[i] != NULL && !strcmp(command[i], "|"))
- {
- i++;
- continue;
- }
- /* Или - конец команды */
- else if (command[i] == NULL)
- {
- return 1;
- }
- else return 0;
- }
- else return 0;
- }
- return 1;
- }
- /* Возвращает номер перенаправления > 0, если переданное
- * слово - перенаправление, иначе 0 */
- int is_redirect(char* word)
- {
- char* redirects[] = {"<", ">", ">>", "2>"};
- int i;
- if (word == NULL) return 0;
- for (i = 1; i < 5; i++)
- {
- if (!strcmp(word, redirects[i - 1]))
- return i;
- }
- return 0;
- }
- /* Возвращает 1, если переданное слово является неспециальным:
- * не является перенаправлением или "|", не содержит спец. символов.
- * Иначе возвращает 0 */
- int not_special_word(char* word)
- {
- int len;
- int i;
- if (word == NULL || !strcmp(word, "|") ||
- is_redirect(word) || strlen(word) > MAX_NAME_LEN) return 0;
- len = strlen(word);
- for (i = 0; i < len; i++)
- {
- if (word[i] == '|' || word[i] == '<' || word[i] == '>')
- return 0;
- }
- return 1;
- }
- /* Возвращает число утилит, которые должны быть запущены в результате
- * выполнения команды */
- int count_command(char** command)
- {
- int count = 0;
- int word_num = 0;
- while (command[word_num] != NULL)
- {
- while (command[word_num] != NULL && strcmp(command[word_num], "|"))
- {
- word_num++;
- }
- count++;
- if (command[word_num] == NULL)
- {
- break;
- }
- else
- {
- word_num++;
- }
- }
- return count;
- }
- /* Если команда специальная, обработает ее и вернет 1, иначе вернет 0 */
- int special_command(char** command)
- {
- if (command == NULL || command[0] == NULL) return 0;
- /* Выход */
- if (!strcmp(command[0], "q"))
- {
- size_t i;
- int status;
- /* Убиваем детей */
- for (i = 0; i < child_num; i++)
- {
- kill(SIGTERM, child_table[i]);
- wait(&status);
- }
- free_str_array(&command);
- if (child_table != NULL) free(child_table);
- exit(0);
- }
- /* cd */
- if (!strcmp(command[0], "cd"))
- {
- if (command[1] == NULL || chdir(command[1]) == -1)
- {
- printf("cmd: can't change directory!\n");
- }
- free_str_array(&command);
- return 1;
- }
- return 0;
- }
- /* Выполняет переданную команду. Возвращает 0 в случае успеха,
- * -1 в случае ошибки */
- int execute_command(char** command)
- {
- int word_num = 0;
- int fd[2];
- int last_out = 0; /* stdin */
- int next_in = 1; /* stdout */
- int redir = 0;
- int redirs[MAX_REDIRS] = {0, 0, 0};
- int i, j;
- int args_end;
- int curr_prog_num = 0;
- pid_t son_pid;
- char end_flag = 0;
- char* filenames[MAX_REDIRS] = {NULL, NULL, NULL};
- char* util_path;
- char** util_args;
- if (command == NULL || command[0] == NULL) return 0;
- child_num = count_command(command);
- child_table = (pid_t*) malloc(child_num * sizeof(pid_t));
- if (child_table == NULL)
- {
- printf("cmd: malloc error.\n");
- free_str_array(&command);
- return -1;
- }
- while (command[word_num] != NULL)
- {
- curr_prog_num++;
- end_flag = 0;
- for (j = 0; j < MAX_REDIRS; j++)
- {
- redirs[j] = 0;
- filenames[j] = NULL;
- }
- /* Путь к утилите */
- util_path = command[word_num];
- /* Аргументы */
- util_args = command + word_num;
- word_num++;
- while (command[word_num] != NULL && !is_redirect(command[word_num]) &&
- strcmp(command[word_num], "|"))
- word_num++;
- args_end = word_num;
- /* Перенаправления */
- i = 0;
- while (redir = is_redirect(command[word_num]))
- {
- redirs[i] = redir;
- filenames[i] = command[word_num + 1];
- i++;
- word_num += 2;
- }
- if (command[word_num] == NULL) /* Конец команды */
- end_flag = 1;
- /* |, <, >, >>, 2> станет концом списка аргументов */
- if (command[args_end] != NULL)
- {
- free(command[args_end]);
- }
- command[args_end] = NULL;
- if (pipe(fd) == -1)
- {
- printf("cmd: pipe error.\n");
- free_str_array(&command);
- return -1;
- }
- next_in = fd[1]; /* Утилита пишет в канал, из которого будет читать следующая утилита */
- switch (son_pid = fork())
- {
- case 0: /* Сын - утилита из данного блока */
- free(child_table);
- if (curr_prog_num == 0) last_out = 0; /* Первая утилита получает ввод из stdin */
- if (curr_prog_num == child_num) next_in = 1; /* Последняя утилита печатает в stdout */
- dup2(last_out, 0);
- dup2(next_in, 1);
- /* Перенаправление в файл */
- if (set_redirs(redirs, filenames, last_out, next_in))
- {
- printf("cmd: error with redirect!\n");
- free_str_array_by_len(&command, word_num);
- exit(-1);
- }
- execvp(util_path, util_args);
- printf("cmd: exec error!\n");
- free_str_array_by_len(&command, word_num);
- close(fd[0]);
- close(fd[1]);
- exit(-1);
- case -1:
- printf("cmd: fork error!\n");
- close(fd[0]);
- close(fd[1]);
- return -1;
- default: /* Отец */
- last_out = fd[0]; /* Следующая читает отсюда */
- child_table[curr_prog_num - 1] = son_pid;
- if (next_in != 1) close(next_in);
- break;
- }
- if (end_flag) break;
- else
- {
- word_num++;
- }
- }
- free_str_array_by_len(&command, word_num);
- return 0;
- }
- void free_str_array_by_len(char*** str_array_ptr, int len)
- {
- int num = 0;
- char** str_array;
- if (str_array_ptr == NULL) return;
- str_array = *str_array_ptr;
- while (num < len)
- {
- if (str_array[num] != NULL)
- free(str_array[num]);
- num++;
- }
- free(str_array);
- return;
- }
- /* Освобождает память массива строк до указателя NULL */
- void free_str_array(char*** str_array)
- {
- char** ptr;
- char** next_ptr;
- if (str_array == NULL || *str_array == NULL) return;
- ptr = *str_array;
- while (*ptr != NULL)
- {
- next_ptr = ptr + 1;
- free(*ptr);
- ptr = next_ptr;
- }
- free(*str_array);
- }
- /* По коду перенаправлений устанавливает перенаправления.
- * "<", ">", ">>", "2>" : 1, 2, 3, 4
- * Возвращает 0 в случае успеха, -1 в случае ошибки */
- int set_redirs(int redirs[MAX_REDIRS],
- char* filenames[MAX_REDIRS], int in, int out)
- {
- int file;
- int i;
- if (in < 0 || out < 0) return -1;
- for (i = 0; i < MAX_REDIRS && redirs[i]; i++)
- {
- if (redirs[i] < 0 || filenames[i] == NULL) return -1;
- switch (redirs[i])
- {
- case 1:
- file = open(filenames[i], O_RDONLY, 0666);
- if (file == -1) return -1;
- close(in);
- dup2(file, 0);
- break;
- case 2:
- file = open(filenames[i], O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (file == -1) return -1;
- close(out);
- dup2(file, 1);
- break;
- case 3:
- file = open(filenames[i], O_WRONLY | O_APPEND | O_CREAT, 0666);
- if (file == -1) return -1;
- close(out);
- dup2(file, 1);
- break;
- case 4:
- file = open(filenames[i], O_WRONLY | O_CREAT, 0666);
- if (file == -1) return -1;
- close(2);
- dup2(file, 2);
- break;
- }
- }
- return 0;
- }
- /* Обрабатывает Ctrl+C, рассылает его детям */
- void inter_handler(int signum)
- {
- size_t i;
- signal(SIGINT, inter_handler);
- if (child_table != NULL)
- {
- for (i = 0; i < child_num; i++)
- {
- kill(SIGINT, child_table[i]);
- }
- }
- return;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement