Advertisement
Guest User

Untitled

a guest
Dec 12th, 2019
181
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.76 KB | None | 0 0
  1. /*
  2.  * Командный итерпретатор
  3.  *
  4.  * main.c
  5.  */
  6.  
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <fcntl.h>
  11. #include <sys/types.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14. #include <limits.h>
  15. #include <sys/wait.h>
  16.  
  17.  
  18. #define START_SIZE 20
  19. #define MAX_CMD_LEN 10000
  20. #define MAX_NAME_LEN 256
  21. #define MAX_REDIRS 3
  22.  
  23.  
  24. char** read_command();
  25. char* read_word();
  26.  
  27. int is_correct(char** command);
  28. int is_redirect(char* word);
  29. int not_special_word(char* word);
  30. int count_command(char** command);
  31. int execute_command(char** command);
  32. int set_redir(int redir, char* filename, int in, int out);
  33. int special_command(char** command);
  34. int set_redirs(int redirs[MAX_REDIRS],
  35.                char* filenames[MAX_REDIRS], int in, int out);
  36.  
  37. void free_str_array_by_len(char*** str_array_ptr, int word_num);
  38. void free_str_array(char*** str_array);
  39. void inter_handler(int signum);
  40.  
  41.  
  42. pid_t* child_table = NULL;
  43. size_t child_num = 0;
  44.  
  45.  
  46. /* Возвращает -1 в случае ошибки, 0 в случае успешной работы. */
  47. int main()
  48. {
  49.     char cwd[PATH_MAX];
  50.     char user_name[MAX_NAME_LEN];
  51.     char host_name[MAX_NAME_LEN];
  52.     char** command = NULL;
  53.     int status;
  54.    
  55.     signal(SIGINT, inter_handler);
  56.    
  57.     printf("Command interpreter.\n\t'q' to exit\n\tСtrl+C to terminate launched processes\n\tcd <dir> to change directory\n");
  58.    
  59.     /* Получение имен */
  60.     if (getlogin_r(user_name, MAX_NAME_LEN) != 0 ||
  61.         gethostname(host_name, MAX_NAME_LEN) != 0)
  62.     {
  63.         printf("cmd: error in OS!\n");
  64.         return -1;
  65.     }
  66.        
  67.     while (1)
  68.     {
  69.         /* Получение рабочей директории */
  70.         if (getcwd(cwd, sizeof(cwd)) == NULL)
  71.         {
  72.             printf("cmd: error in OS!\n");
  73.             return -1;
  74.         }
  75.         printf("\n\x1B[32m%s@%s\e[0m \e[34m%s\e[0m > ", user_name, host_name, cwd);
  76.        
  77.         /* Читаем строку-команду */
  78.         command = read_command();
  79.        
  80.         if (is_correct(command))
  81.         {
  82.             /* Команда может быть специальной, тогда вернется 1,
  83.              * обработка внутри special_command */
  84.             if (!special_command(command))
  85.             {
  86.                 /* Обработка неспециальной команды */
  87.                 execute_command(command);
  88.                
  89.                 /* Ждем, пока сыновья не закончат работу */
  90.                 while (wait(&status) > 0) sleep(0.1);
  91.                 free(child_table);
  92.                 child_table = NULL;
  93.                 command = NULL;
  94.                 child_num = 0;
  95.             }
  96.         }
  97.         else
  98.         {
  99.             printf("cmd: wrong syntex!\n");
  100.         }
  101.     }
  102.  
  103.    
  104.     return 0;
  105. }
  106.  
  107.  
  108. /* Читает команду интерпретатору. Возвращает указатель на массив слов
  109.  * команды. В случае ошибки возвращает NULL. */
  110. char** read_command()
  111. {
  112.     char** command;
  113.     int command_len = 0;
  114.     int word_num = 0;
  115.     int max_words = START_SIZE;
  116.    
  117.     command = (char**) malloc(max_words * sizeof(char*));
  118.     if (command == NULL)
  119.     {
  120.         printf("cmd: malloc error.\n");
  121.         return NULL;
  122.     }
  123.    
  124.     while (command_len < MAX_CMD_LEN)
  125.     {
  126.         command[word_num] = read_word();
  127.         if (command[word_num] == NULL)
  128.             return command;
  129.  
  130.         command_len += strlen(command[word_num]);
  131.         word_num++;
  132.        
  133.         if (word_num >= max_words)
  134.         {
  135.             max_words *= 2;
  136.             command = (char**) realloc(command, max_words * sizeof(char*));
  137.             if (command == NULL)
  138.             {
  139.                 printf("cmd: realloc error.\n");
  140.                 return NULL;
  141.             }
  142.         }
  143.     }
  144.    
  145.     return command;
  146. }
  147.        
  148.  
  149. /* Читает слово команды. Возвращает указатель слово.
  150.  * В случае ошибки либо конца команды возвращает NULL. */
  151. char* read_word()
  152. {
  153.     char* word;
  154.     int c;
  155.     int word_len = 0;
  156.     int max_syms = START_SIZE;
  157.    
  158.     word = (char*) malloc(max_syms * sizeof(char));
  159.     if (word == NULL)
  160.     {
  161.         printf("cmd: malloc error.\n");
  162.         return NULL;
  163.     }
  164.    
  165.     while (word_len < MAX_CMD_LEN)
  166.     {
  167.         c = getchar();
  168.         if (c == EOF || c == '\n' || c == ' ')
  169.         {
  170.             if (word_len == 0)
  171.             {
  172.                 free(word);
  173.                 return NULL; /* Конец команды */
  174.             }
  175.             else
  176.             {
  177.                 word[word_len] = '\0';
  178.                 if (c != ' ') ungetc(c, stdin);
  179.                 return word;
  180.             }
  181.         }
  182.         else
  183.         {
  184.             word[word_len] = c;
  185.             word_len++;
  186.         }
  187.        
  188.         if (word_len >= max_syms)
  189.         {
  190.             max_syms *= 2;
  191.             word = (char*) realloc(word, max_syms * sizeof(char));
  192.             if (word == NULL)
  193.             {
  194.                 printf("cmd: realloc error\n");
  195.                 return NULL;
  196.             }
  197.         }
  198.     }
  199.    
  200.     return word;
  201. }
  202.  
  203.  
  204. /* Проверяет корректность синтаксиса команды. Возвращает 1, если синтаксис
  205.  * корректен, иначе 0. */
  206. int is_correct(char** command)
  207. {
  208.     int i = 0;
  209.     int j = 0;
  210.     int redirects[MAX_REDIRS];
  211.     int redir;
  212.     int redir_num = 0;
  213.    
  214.     if (command == NULL) return 0;
  215.    
  216.     while (command[i] != NULL)
  217.     {
  218.         /* Первое слово в блоке - имя утилиты */
  219.         if (not_special_word(command[i]))
  220.         {
  221.             /* Далее могут идти аргументы для утилиты */
  222.             i++;
  223.             while (not_special_word(command[i])) i++;
  224.            
  225.             /* Далее могут идти перенаправления */
  226.             redir_num = 0;
  227.             for (j = 0; j < MAX_REDIRS; j++) redirects[j] = 0;
  228.             while (redir = is_redirect(command[i]))
  229.             {
  230.                 if (redir_num >= MAX_REDIRS) return 0;
  231.                    
  232.                 i++;
  233.                 if (!not_special_word(command[i])) return 0;
  234.                
  235.                 /* Не должно быть повторений */
  236.                 for (j = 0; j < MAX_REDIRS; j++)
  237.                 {
  238.                     if (redirects[j] == redir) return 0;
  239.                     /* перенаправления > и >> несовместимы */
  240.                     if (redir == 2 && redirects[j] == 3) return 0;
  241.                     if (redir == 3 && redirects[j] == 2) return 0;
  242.                 }
  243.                
  244.                 redirects[redir_num] = redir;
  245.                 redir_num++;
  246.                 i++;
  247.             }
  248.            
  249.             /* Следующее слово - конец блока "|" */
  250.             if (command[i] != NULL && !strcmp(command[i], "|"))
  251.             {
  252.                 i++;
  253.                 continue;
  254.             }
  255.             /* Или - конец команды */
  256.             else if (command[i] == NULL)
  257.             {
  258.                 return 1;
  259.             }
  260.             else return 0;
  261.         }
  262.         else return 0;
  263.     }
  264.    
  265.     return 1;
  266. }
  267.  
  268.  
  269. /* Возвращает номер перенаправления > 0, если переданное
  270.  * слово - перенаправление, иначе 0 */
  271. int is_redirect(char* word)
  272. {
  273.     char* redirects[] = {"<", ">", ">>", "2>"};
  274.     int i;
  275.    
  276.     if (word == NULL) return 0;
  277.    
  278.     for (i = 1; i < 5; i++)
  279.     {
  280.         if (!strcmp(word, redirects[i - 1]))
  281.             return i;
  282.     }
  283.    
  284.     return 0;
  285. }
  286.  
  287. /* Возвращает 1, если переданное слово является неспециальным:
  288.  * не является перенаправлением или "|", не содержит спец. символов.
  289.  * Иначе возвращает 0 */
  290. int not_special_word(char* word)
  291. {
  292.     int len;
  293.     int i;
  294.    
  295.     if (word == NULL || !strcmp(word, "|") ||
  296.         is_redirect(word) || strlen(word) > MAX_NAME_LEN) return 0;
  297.    
  298.     len = strlen(word);
  299.     for (i = 0; i < len; i++)
  300.     {
  301.         if (word[i] == '|' || word[i] == '<' || word[i] == '>')
  302.             return 0;
  303.     }
  304.    
  305.     return 1;
  306. }
  307.  
  308.  
  309. /* Возвращает число утилит, которые должны быть запущены в результате
  310.  * выполнения команды */
  311. int count_command(char** command)
  312. {
  313.     int count = 0;
  314.     int word_num = 0;
  315.    
  316.     while (command[word_num] != NULL)
  317.     {
  318.         while (command[word_num] != NULL && strcmp(command[word_num], "|"))
  319.         {
  320.             word_num++;
  321.         }
  322.         count++;
  323.        
  324.         if (command[word_num] == NULL)
  325.         {
  326.             break;
  327.         }
  328.         else
  329.         {
  330.             word_num++;
  331.         }
  332.     }
  333.        
  334.     return count;
  335. }
  336.  
  337.  
  338. /* Если команда специальная, обработает ее и вернет 1, иначе вернет 0 */
  339. int special_command(char** command)
  340. {
  341.     if (command == NULL || command[0] == NULL) return 0;
  342.    
  343.     /* Выход */
  344.     if (!strcmp(command[0], "q"))
  345.     {
  346.         size_t i;
  347.         int status;
  348.        
  349.         /* Убиваем детей */
  350.         for (i = 0; i < child_num; i++)
  351.         {
  352.             kill(SIGTERM, child_table[i]);
  353.             wait(&status);
  354.         }
  355.        
  356.         free_str_array(&command);
  357.         if (child_table != NULL) free(child_table);
  358.         exit(0);
  359.     }
  360.    
  361.     /* cd */
  362.     if (!strcmp(command[0], "cd"))
  363.     {
  364.         if (command[1] == NULL || chdir(command[1]) == -1)
  365.         {
  366.             printf("cmd: can't change directory!\n");
  367.         }
  368.         free_str_array(&command);
  369.         return 1;
  370.     }
  371.    
  372.     return 0;
  373. }
  374.  
  375.  
  376. /* Выполняет переданную команду. Возвращает 0 в случае успеха,
  377.  * -1 в случае ошибки */
  378. int execute_command(char** command)
  379. {
  380.     int word_num = 0;
  381.     int fd[2];
  382.     int last_out = 0; /* stdin */
  383.     int next_in = 1; /* stdout */
  384.     int redir = 0;
  385.     int redirs[MAX_REDIRS] = {0, 0, 0};
  386.     int i, j;
  387.     int args_end;
  388.     int curr_prog_num = 0;
  389.    
  390.     pid_t son_pid;
  391.    
  392.     char end_flag = 0;
  393.    
  394.     char* filenames[MAX_REDIRS] = {NULL, NULL, NULL};
  395.     char* util_path;
  396.     char** util_args;
  397.    
  398.     if (command == NULL || command[0] == NULL) return 0;
  399.    
  400.     child_num = count_command(command);
  401.     child_table = (pid_t*) malloc(child_num * sizeof(pid_t));
  402.     if (child_table == NULL)
  403.     {
  404.         printf("cmd: malloc error.\n");
  405.         free_str_array(&command);
  406.         return -1;
  407.     }
  408.    
  409.     while (command[word_num] != NULL)
  410.     {
  411.         curr_prog_num++;
  412.         end_flag = 0;
  413.         for (j = 0; j < MAX_REDIRS; j++)
  414.         {
  415.             redirs[j] = 0;
  416.             filenames[j] = NULL;
  417.         }
  418.        
  419.         /* Путь к утилите */
  420.         util_path = command[word_num];
  421.        
  422.         /* Аргументы */
  423.         util_args = command + word_num;
  424.         word_num++;
  425.         while (command[word_num] != NULL && !is_redirect(command[word_num]) &&
  426.                strcmp(command[word_num], "|"))
  427.             word_num++;
  428.         args_end = word_num;
  429.        
  430.         /* Перенаправления */
  431.         i = 0;
  432.         while (redir = is_redirect(command[word_num]))
  433.         {
  434.             redirs[i] = redir;
  435.             filenames[i] = command[word_num + 1];
  436.             i++;
  437.             word_num += 2;
  438.         }
  439.    
  440.         if (command[word_num] == NULL) /* Конец команды */
  441.             end_flag = 1;
  442.        
  443.         /* |, <, >, >>, 2> станет концом списка аргументов */
  444.         if (command[args_end] != NULL)
  445.         {
  446.             free(command[args_end]);
  447.         }
  448.         command[args_end] = NULL;
  449.        
  450.         if (pipe(fd) == -1)
  451.         {
  452.             printf("cmd: pipe error.\n");
  453.             free_str_array(&command);
  454.             return -1;
  455.         }
  456.         next_in = fd[1]; /* Утилита пишет в канал, из которого будет читать следующая утилита */
  457.        
  458.         switch (son_pid = fork())
  459.         {
  460.             case 0: /* Сын - утилита из данного блока */
  461.                 free(child_table);
  462.                 if (curr_prog_num == 0) last_out = 0; /* Первая утилита получает ввод из stdin */
  463.                 if (curr_prog_num == child_num) next_in = 1; /* Последняя утилита печатает в stdout */
  464.                 dup2(last_out, 0);
  465.                 dup2(next_in, 1);
  466.                
  467.                 /* Перенаправление в файл */
  468.                 if (set_redirs(redirs, filenames, last_out, next_in))
  469.                 {
  470.                     printf("cmd: error with redirect!\n");
  471.                     free_str_array_by_len(&command, word_num);
  472.                     exit(-1);
  473.                 }
  474.                
  475.                 execvp(util_path, util_args);
  476.                
  477.                 printf("cmd: exec error!\n");
  478.                 free_str_array_by_len(&command, word_num);
  479.                 close(fd[0]);
  480.                 close(fd[1]);
  481.                 exit(-1);
  482.            
  483.             case -1:
  484.                 printf("cmd: fork error!\n");
  485.                 close(fd[0]);
  486.                 close(fd[1]);
  487.                 return -1;
  488.            
  489.             default: /* Отец */
  490.                 last_out = fd[0]; /* Следующая читает отсюда */
  491.                
  492.                 child_table[curr_prog_num - 1] = son_pid;
  493.                 if (next_in != 1) close(next_in);
  494.                 break;
  495.         }
  496.        
  497.         if (end_flag) break;
  498.         else
  499.         {
  500.             word_num++;
  501.         }
  502.     }
  503.    
  504.     free_str_array_by_len(&command, word_num);
  505.    
  506.     return 0;
  507. }
  508.  
  509.  
  510. void free_str_array_by_len(char*** str_array_ptr, int len)
  511. {
  512.     int num = 0;
  513.     char** str_array;
  514.    
  515.     if (str_array_ptr == NULL) return;
  516.    
  517.     str_array = *str_array_ptr;
  518.    
  519.     while (num < len)
  520.     {
  521.         if (str_array[num] != NULL)
  522.             free(str_array[num]);
  523.         num++;
  524.     }
  525.    
  526.     free(str_array);
  527.     return;
  528. }
  529.  
  530.  
  531. /* Освобождает память массива строк до указателя NULL */
  532. void free_str_array(char*** str_array)
  533. {
  534.     char** ptr;
  535.     char** next_ptr;
  536.  
  537.     if (str_array == NULL || *str_array == NULL) return;
  538.  
  539.     ptr = *str_array;
  540.     while (*ptr != NULL)
  541.     {
  542.         next_ptr = ptr + 1;
  543.         free(*ptr);
  544.         ptr = next_ptr;
  545.     }
  546.  
  547.     free(*str_array);
  548. }
  549.  
  550.  
  551. /* По коду перенаправлений устанавливает перенаправления.
  552.  * "<", ">", ">>", "2>" : 1, 2, 3, 4
  553.  * Возвращает 0 в случае успеха, -1 в случае ошибки */
  554. int set_redirs(int redirs[MAX_REDIRS],
  555.                char* filenames[MAX_REDIRS], int in, int out)
  556. {
  557.     int file;
  558.     int i;
  559.    
  560.     if (in < 0 || out < 0) return -1;
  561.    
  562.     for (i = 0; i < MAX_REDIRS && redirs[i]; i++)
  563.     {
  564.         if (redirs[i] < 0 || filenames[i] == NULL) return -1;
  565.    
  566.         switch (redirs[i])
  567.         {
  568.             case 1:
  569.                 file = open(filenames[i], O_RDONLY, 0666);
  570.                 if (file == -1) return -1;
  571.                 close(in);
  572.                 dup2(file, 0);
  573.                 break;
  574.            
  575.             case 2:
  576.                 file = open(filenames[i], O_WRONLY | O_CREAT | O_TRUNC, 0666);
  577.                 if (file == -1) return -1;
  578.                 close(out);
  579.                 dup2(file, 1);
  580.                 break;
  581.            
  582.             case 3:
  583.                 file = open(filenames[i], O_WRONLY | O_APPEND | O_CREAT, 0666);
  584.                 if (file == -1) return -1;
  585.                 close(out);
  586.                 dup2(file, 1);
  587.                 break;
  588.            
  589.             case 4:
  590.                 file = open(filenames[i], O_WRONLY | O_CREAT, 0666);
  591.                 if (file == -1) return -1;
  592.                 close(2);
  593.                 dup2(file, 2);
  594.                 break;
  595.         }
  596.     }
  597.    
  598.     return 0;
  599. }
  600.  
  601.  
  602. /* Обрабатывает Ctrl+C, рассылает его детям */
  603. void inter_handler(int signum)
  604. {
  605.     size_t i;
  606.    
  607.     signal(SIGINT, inter_handler);
  608.    
  609.     if (child_table != NULL)
  610.     {
  611.         for (i = 0; i < child_num; i++)
  612.         {
  613.             kill(SIGINT, child_table[i]);
  614.         }
  615.     }
  616.    
  617.     return;
  618. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement