C   24

shell c

Guest on 24th January 2023 02:08:22 PM


  1. /*
  2.  * arch-tag: main
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include "shell.h"
  9.  
  10. struct sh_command_t {
  11.         char* name;
  12.         int argc;
  13.         char* argv[SH_MAXARGS];
  14.         short flags;
  15.         char* stdin_filename;
  16.         char* stdout_filename;
  17. };
  18.  
  19. void sh_print_prompt(void)
  20. {
  21.         printf(">> ");
  22. }
  23.  
  24. void sh_readline(char* buf)
  25. {
  26.         fgets(buf, SH_MAXLINELEN, stdin);
  27. }
  28.  
  29. int sh_parseline(char* line, struct sh_command_t* command)
  30. {
  31.         char** p;
  32.         int i, rdr_in_pos, rdr_out_pos;
  33.  
  34.         command->flags = 0;
  35.  
  36.         for(p = command->argv, command->argc = 0; (*p = (char*) strsep(&line, " \n\t")) != NULL; )
  37.                 if (**p != '\0') { // Skip repeated spaces
  38.                         // Don't process more than SH_MAXARGS args
  39.                         if (p + 1 >= &(command->argv[SH_MAXARGS]))
  40.                                 break;
  41.                         command->argc++;
  42.                         p++;
  43.                 }
  44.  
  45.         for (i = 0, rdr_in_pos = 0, rdr_out_pos = 0;
  46.              i < command->argc;
  47.              i++) {
  48.                 if (*(command->argv[i]) == '&') {
  49.                         //printf("%d %s\n", i, *(command->argv + i));
  50.                         if (!rdr_in_pos && !rdr_out_pos)
  51.                                 p = command->argv + i;
  52.  
  53.                         command->flags |= SH_CMD_BACKGROUND;
  54.                 } else if (*(command->argv[i]) == '>') {
  55.                         //printf("%d %s\n", i, *(command->argv + i));
  56.                         if (!rdr_in_pos)
  57.                                 p = command->argv + i;
  58.  
  59.                         rdr_out_pos = i;
  60.                         command->flags |= SH_CMD_REDIRECT_OUT;
  61.                         command->stdout_filename = *(command->argv + i + 1);
  62.                 } else if (*(command->argv[i]) == '<') {
  63.                         //printf("%d %s\n", i, *(command->argv + i));
  64.                         if (!rdr_out_pos)
  65.                                 p = command->argv + i;
  66.  
  67.                         rdr_in_pos = i;
  68.                         command->flags |= SH_CMD_REDIRECT_IN;
  69.                         command->stdin_filename = *(command->argv + i + 1);
  70.                 }
  71.         }
  72.  
  73.         *p = NULL;
  74.  
  75.         command->argc = p - command->argv;
  76.        
  77.         return command->argc;
  78. }
  79.  
  80. int sh_getpathv(char** pathv)
  81. {
  82.         int pathc;
  83.         char** p, *path;
  84.        
  85.         // TODO: make a copy?
  86.         path = (char*) getenv("PATH");
  87.  
  88.         for(p = pathv, pathc = 0; (*p = (char*) strsep(&path, ":")) != NULL; )
  89.                 if (**p != '\0') {
  90.                         if (p + 1 >= &pathv[SH_MAXPATHS])
  91.                                 break;
  92.                         pathc++;
  93.                         p++;
  94.                 }
  95.  
  96.         return pathc;
  97. }
  98.  
  99. int sh_check_exec(const char* fullpath)
  100. {
  101.         // XXX possible race condition
  102.         return access(fullpath, X_OK);
  103. }
  104.  
  105. char* sh_lookup_path(char* name, int pathc, char** pathv)
  106. {
  107.         int i, fp_size, fd, path_len, last_sl_pos;
  108.         char* fullpath, *curr_dir, *ex_dir;
  109.  
  110.         // TODO: check for absolute and relative paths
  111.         if (*name == '/') {
  112.                 if (sh_check_exec(name) == 0)
  113.                         return name;
  114.                 else
  115.                         return NULL;
  116.         }
  117.        
  118.         // TODO: use realpath(3) instead.
  119.         if (*name == '.') { // Hack to do relative pathnames
  120.                 if ((curr_dir = getcwd(NULL, 0)) == NULL) {
  121.                         perror("getcwd()");
  122.                         exit(1);
  123.                 }
  124.  
  125.                 if ((ex_dir = (char*) malloc(strlen(curr_dir) + 1)) == NULL) {
  126.                         perror("malloc()");
  127.                         exit(1);
  128.                 }
  129.  
  130.                 strncpy(ex_dir, name, strlen(name) + 1);
  131.  
  132.                 // locate the last '/'
  133.                 for (i = 0, path_len = strlen(ex_dir);
  134.                      i < path_len;
  135.                      i++) {
  136.                         // printf("%c\n", ex_dir[i]);
  137.                         if (ex_dir[i] == '/') {
  138.                                 last_sl_pos = i;
  139.                                 // printf("found '/' %d\n", i);
  140.                         }
  141.                 }
  142.  
  143.                 ex_dir[last_sl_pos] = 0;
  144.                 name = name + last_sl_pos + 1;
  145.  
  146.                 if ((fd = open(ex_dir, O_RDONLY)) < 0) {
  147.                         perror("open()");
  148.                         exit(1);
  149.                 }
  150.  
  151.                 //printf("%d %s %s\n", last_sl_pos, ex_dir, name);
  152.  
  153.                 if (fchdir(fd) < 0) {
  154.                         perror("fchdir()");
  155.                         exit(1);
  156.                 }
  157.  
  158.                 free(ex_dir);
  159.  
  160.                 if ((ex_dir = getcwd(NULL, 0)) == NULL) {
  161.                         perror("getcwd()");
  162.                         exit(1);
  163.                 }
  164.  
  165.                 if (chdir(curr_dir) < 0) {
  166.                         perror("chdir()");
  167.                         exit(1);
  168.                 }
  169.  
  170.                 if (asprintf(&fullpath, "%s/%s", ex_dir, name) < 0) {
  171.                         perror("asprintf()");
  172.                         exit(1);
  173.                 }
  174.  
  175.                 free(ex_dir);
  176.                 free(curr_dir);
  177.  
  178.                 // printf("%s\n", fullpath);
  179.  
  180.                 if (sh_check_exec(fullpath) == 0)
  181.                         return fullpath;
  182.                 else {
  183.                         free(fullpath);
  184.                         return NULL;
  185.                 }
  186.         }
  187.  
  188.         for (i = 0; i < pathc; i++) {
  189.                 fp_size = strlen(name) + strlen(pathv[i]) + 2;
  190.        
  191.                 if ((fullpath = (char*) malloc(fp_size)) == NULL) {
  192.                         perror("malloc()");
  193.                         exit(1);
  194.                 }
  195.                
  196.                 snprintf(fullpath, fp_size, "%s/%s", pathv[i], name);
  197.                
  198.                 //printf("%s\n", fullpath);
  199.                
  200.                 if (sh_check_exec(fullpath) == 0)
  201.                         return fullpath;
  202.                
  203.                 free(fullpath);
  204.         }
  205.  
  206.         return NULL;
  207. }
  208.  
  209. void sh_exec_child(struct sh_command_t* command)
  210. {
  211.         int in_fd, out_fd;
  212.  
  213.         if (command->flags & SH_CMD_REDIRECT_IN) {
  214.                 // printf("redirecting stdin to %s\n", command->stdin_filename);
  215.  
  216.                 close(0);
  217.  
  218.                 if ((in_fd = open(command->stdin_filename, O_RDONLY)) < 0) {
  219.                         perror("open()");
  220.                         exit(1);
  221.                 }
  222.         }
  223.        
  224.         if (command->flags & SH_CMD_REDIRECT_OUT) {
  225.                 // printf("redirecting stdout to %s\n", command->stdout_filename);
  226.  
  227.                 close(1);
  228.  
  229.                 if ((out_fd = open(command->stdout_filename, O_WRONLY | O_CREAT)) < 0) {
  230.                         perror("open()");
  231.                         exit(1);
  232.                 }
  233.  
  234.                 if (fchmod(out_fd, 400) < 0) {
  235.                         perror("fchmod()");
  236.                         exit(1);
  237.                 }
  238.         }
  239.        
  240.         execv(command->name, command->argv);
  241. }
  242.  
  243. int main(int argc, char** argv)
  244. {
  245.         char* buf;
  246.         struct sh_command_t command;
  247.         char* pathv[SH_MAXPATHS];
  248.         int pathc;
  249.         pid_t pid;
  250.  
  251.         pathc = sh_getpathv(pathv);
  252.  
  253.         while(1) {
  254.                 if ((buf = (char*) malloc(SH_MAXLINELEN)) == NULL) {
  255.                         perror("malloc()");
  256.                         exit(1);
  257.                 }
  258.  
  259.                 sh_print_prompt();
  260.  
  261.                 sh_readline(buf);
  262.  
  263.                 if (sh_parseline(buf, &command) == 0)
  264.                         goto done;
  265.  
  266.                 if ((command.name = sh_lookup_path(command.argv[0], pathc, pathv)) == NULL) {
  267.                         printf("Command not found: %s\n", command.argv[0]);
  268.                         goto done;
  269.                 }
  270.  
  271.                 printf("%s\n", command.name);
  272.  
  273.                 if ((pid = fork()) == 0) {
  274.                         sh_exec_child(&command);
  275.                 }
  276.  
  277.                 if (!(command.flags & SH_CMD_BACKGROUND))
  278.                         waitpid(pid, NULL, 0);
  279.                 else
  280.                         printf("pid: %d\n", pid);
  281.  
  282.                 free(command.name);
  283.  
  284.                 done:
  285.                 free(buf);
  286.         }
  287. }

Raw Paste


Login or Register to edit or fork this paste. It's free.

">