Advertisement
Guest User

gei.c

a guest
Dec 16th, 2017
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 5.46 KB | None | 0 0
  1. //------------------------------------------------------------------------
  2. // NAME: Vencislav Tashev
  3. // CLASS: XIa
  4. // NUMBER: 4
  5. // PROBLEM: #2
  6. // FILE NAME: shell.c
  7. // FILE PURPOSE:
  8. // A Unix-like shell command interpreter
  9. // written in C using processes and exec* functions
  10. //------------------------------------------------------------------------
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <unistd.h>
  15. #include <string.h>
  16. #include <sys/types.h>
  17. #include <sys/wait.h>
  18. #include <sys/stat.h>
  19.  
  20. #define LINE_PREFIX "$ "
  21. #define BUFF_SIZE 4096
  22. #define DELIMITER " "
  23.  
  24. void free_string_array(char** arr);
  25. size_t count(const char* haystack, const char needle);
  26. char** parse_cmdline(const char* cmdline);
  27. void run_command(char** command);
  28.  
  29. int main() {
  30.     char buffer[BUFF_SIZE];
  31.  
  32.     while (1) {
  33.         printf(LINE_PREFIX);
  34.  
  35.         // break out of the loop when we reach EOF
  36.         if (fgets(buffer, BUFF_SIZE, stdin) == NULL) {
  37.             exit(EXIT_SUCCESS);
  38.         }
  39.  
  40.         // fgets() puts a "\n" in the end of input
  41.         // strcspn() scans str1 for the first occurrence of any of the characters that are part of str2,
  42.         // returning the number of characters of str1 read before this first occurrence.
  43.         buffer[strcspn(buffer, "\n")] = '\0';
  44.  
  45.         // Running the already parsed command line
  46.         run_command(parse_cmdline(buffer));
  47.     }
  48. }
  49.  
  50. //------------------------------------------------------------------------
  51. // FUNCTION: free_string_array
  52. // Completely frees space for a dynamically allocated char** (array of strings)
  53. // PARAMETERS:
  54. // char** arr - The array to be freed
  55. //------------------------------------------------------------------------
  56. void free_string_array(char** arr) {
  57.     for (int i = 0; arr[i] != '\0'; i++) {
  58.         // free each previously allocated element in the array
  59.         free(arr[i]);
  60.     }
  61.     // free the whole array pointer
  62.     free(arr);
  63.     // set the array pointer to point to NULL
  64.     arr = NULL;
  65. }
  66.  
  67. //------------------------------------------------------------------------
  68. // FUNCTION: count
  69. // Counts a char encounters in a string
  70. // PARAMETERS:
  71. // const char* haystack - The string that we search in
  72. // const char needle    - The char that we search for
  73. //------------------------------------------------------------------------
  74. size_t count(const char* haystack, const char needle) {
  75.     size_t result = 0;
  76.     for (size_t i = 0; i < strlen(haystack); i++) {
  77.         if (haystack[i] == needle) {
  78.             result++;
  79.         }
  80.     }
  81.  
  82.     return result;
  83. }
  84.  
  85. //------------------------------------------------------------------------
  86. // FUNCTION: parse_cmdline
  87. // Parses a provided line from the input
  88. // by tokenizing it by space and
  89. // returning the tokens in an dynamically allocated array
  90. // PARAMETERS:
  91. // const char* cmdline - The line to be parsed
  92. //------------------------------------------------------------------------
  93. char** parse_cmdline(const char* cmdline) {
  94.     const size_t len = strlen(cmdline);
  95.  
  96.     // copy the provided constant cmdline
  97.     // in order to tokenize it with strtok()
  98.     char* line_copy = (char*) calloc(sizeof(char), len + 1);
  99.     strcpy(line_copy, cmdline);
  100.    
  101.     // counting the encounters of ' ' in the string in order to determine
  102.     // the size for the result array of strings
  103.     const size_t spaces_count = count(line_copy, ' ');
  104.     // the result array of strings
  105.     // we allocate spaces_count + 2 memory for the null byte
  106.     char** parsed = (char**) calloc(sizeof(char*), spaces_count + 2);
  107.  
  108.     // strtok() breaks provided string into copies by provided delimiter
  109.     char* token = strtok(line_copy, DELIMITER);
  110.     int i = 0;
  111.     for (; token != NULL; i++) {
  112.         // allocate space for the current element in the result array
  113.         // and copying the current token value into it
  114.         parsed[i] = (char*) calloc(sizeof(char), strlen(token) + 1);
  115.         strcpy(parsed[i], token);
  116.  
  117.         // go to the next token
  118.         token = strtok(NULL, DELIMITER);
  119.     }
  120.     // set a null byte in the end
  121.     parsed[i] = '\0';
  122.  
  123.     // free the line copy because we do not need it anymore
  124.     free(line_copy);
  125.  
  126.     return parsed;
  127. }
  128.  
  129. //------------------------------------------------------------------------
  130. // FUNCTION: run_command
  131. // The main functionality of the program
  132. // Runs a given command array of strings ( e.g. {"ls", "-la"} ) by:
  133. // -> creating a process ( fork() ),
  134. // -> executing the already parsed command in child process ( exec*() )
  135. // PARAMETERS:
  136. // char** command - The command line to be executed
  137. //------------------------------------------------------------------------
  138. void run_command(char** command) {
  139.     // create a new process using fork()
  140.     pid_t pid = fork();
  141.  
  142.     if (pid == 0) {
  143.         // Child process
  144.  
  145.         execvp(command[0], command);
  146.         // exec* will interrupt the child process
  147.         // so the error handling can be placed immediately after exec*
  148.         // when exec* fails, it will continue down the child process
  149.         // and the error handling will execute correctly
  150.  
  151.         perror(command[0]);
  152.         free_string_array(command);
  153.         _exit(EXIT_FAILURE);
  154.     } else if (pid > 0) {
  155.         // Parent process
  156.  
  157.         // Wait for the child to finish it's work
  158.         waitpid(pid, NULL, 0);
  159.     } else {
  160.         perror("fork");
  161.     }
  162.  
  163.     // Free the already executed command, we do not need it anymore
  164.     free_string_array(command);
  165. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement