/ shell.c
shell.c
1 #include <stdio.h> 2 #include <sys/wait.h> 3 #include <sys/stat.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <errno.h> 8 9 const int MAX_ARGS = 48; 10 const int MAX_PATH_ENTRIES = 48; 11 char DEFAULT_PATH[] = "/usr/bin:/run/current-system/sw/bin"; 12 const char PROMPT[] = "> "; 13 14 struct Cmd { 15 int argc; 16 char **argv; 17 }; 18 19 struct Cmd *line_to_cmd(char *line) { 20 char *p = strtok(line, " "); 21 int count = 0; 22 char **argv = malloc(MAX_ARGS * (sizeof(p))); 23 24 while (p != NULL) { 25 argv[count++] = p; 26 p = strtok(NULL, " "); 27 if (count == MAX_ARGS) { 28 fprintf(stderr, "Max args reached!\n"); 29 break; 30 } 31 } 32 33 if (count == 0) 34 return NULL; 35 36 argv[count] = NULL; 37 38 struct Cmd *cmd = malloc(sizeof(struct Cmd)); 39 cmd->argc = count; 40 cmd->argv = argv; 41 42 return cmd; 43 } 44 45 char **path_from_env(char *default_path) { 46 char *path_s = getenv("PATH"); 47 if (!path_s) { 48 fprintf(stderr, "PATH not set! Falling back to default %s\n", default_path); 49 path_s = default_path; 50 } 51 52 char **path = malloc(MAX_PATH_ENTRIES * sizeof(path_s)); 53 char *dir = strtok(path_s, ":"); 54 int i = 0; 55 while (dir != NULL) { 56 path[i++] = dir; 57 if (i == MAX_PATH_ENTRIES) { 58 fprintf(stderr, "MAX_PATH_ENTRIES reached!"); 59 break; 60 } 61 dir = strtok(NULL, ":"); 62 } 63 return path; 64 } 65 66 char *resolve_path(char *cmd, char **path) { 67 if (cmd == NULL) 68 return cmd; 69 70 struct stat sb; 71 int i = 0; 72 char *dir = path[i]; 73 while (dir != NULL) { 74 char *full_path = malloc(strlen(dir) + strlen(cmd) + 2); 75 sprintf(full_path, "%s/%s", dir, cmd); 76 if (lstat(full_path, &sb) == -1) { 77 free(full_path); 78 dir = path[i++]; 79 continue; 80 } 81 return full_path; 82 } 83 return cmd; 84 } 85 86 int eval(char *line, char **PATH) { 87 struct Cmd *cmd = line_to_cmd(line); 88 if (!cmd) 89 return 0; 90 91 char *path = resolve_path(cmd->argv[0], PATH); 92 if (!path) { 93 fprintf(stderr, "Command '%s' not found\n", line); 94 return -1; 95 } 96 97 int pid = fork(); 98 if (pid < 1) { 99 execve(path, cmd->argv, NULL); 100 perror("execve"); 101 } else { 102 free(path); 103 free(cmd->argv); 104 free(cmd); 105 return pid; 106 } 107 } 108 109 int readline(char *buf, size_t buf_size) { 110 int nbytes = getline(&buf, &buf_size, stdin); 111 if (nbytes > 0) { 112 nbytes = strcspn(buf, "\n"); 113 buf[nbytes] = '\0'; 114 } 115 return nbytes; 116 } 117 118 int main(int argc, char *argv[]) { 119 size_t INPUT_BUFFER_SIZE = 1024; 120 char *line = malloc(INPUT_BUFFER_SIZE); 121 if (!line) { 122 perror("malloc"); 123 exit(1); 124 } 125 126 char **path = path_from_env(DEFAULT_PATH); 127 128 while (1) { 129 printf("%s", PROMPT); 130 int res = readline(line, INPUT_BUFFER_SIZE); 131 if (res < 0) { 132 exit(1); 133 } else if (res == 0) { 134 continue; 135 }; 136 int pid = eval(line, path); 137 if (pid > 0) { 138 wait(&pid); 139 } 140 fflush(stdout); 141 } 142 143 free(line); 144 }