main.c (5192B)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdbool.h> 4 #include <stdlib.h> 5 6 #ifdef _WIN32 7 #define WIN32_LEAN_AND_MEAN 8 #include <windows.h> 9 #define F_OK 0 10 #else 11 #include <sys/wait.h> 12 #include <unistd.h> 13 #endif 14 15 void parse_input(const char *input, char *cmd, char **args, int *argc); 16 bool find_executable(const char *cmd, char *result_path); 17 void run_executable(const char *cmd, char **args); 18 19 int main() { 20 21 // Process user input 22 bool running = true; 23 char input[100]; 24 char cmd[100]; 25 char *args[10]; 26 int argc; 27 28 while(running){ 29 // Flush after every printf 30 setbuf(stdout, NULL); 31 printf("$ "); 32 33 // Exit on error 34 if(fgets(input, sizeof(input), stdin) == NULL){ 35 running = false; 36 } 37 38 // remove \n 39 int n = strlen(input); 40 if (n > 0 && input[n - 1] == '\n'){ 41 input[n - 1] = '\0'; 42 } 43 44 // Parse input 45 parse_input(input, cmd, args, &argc); 46 47 // Evaluate commands: 48 if(!strcmp(cmd, "exit")){ 49 //printf("Exiting...\n"); 50 running = false; 51 } else if (!strcmp(cmd, "echo")){ 52 for (int i = 0; i < argc; i++){ 53 printf("%s ", args[i]); 54 } 55 printf("\n"); 56 } else if (!strcmp(cmd, "type")){ 57 if (argc > 0){ 58 char path[512]; 59 if (!strcmp(args[0], "echo") 60 || !strcmp(args[0], "exit") 61 || !strcmp(args[0], "type")){ 62 printf("%s is a shell builtin\n", args[0]); 63 } else if (find_executable(args[0], path)) { 64 printf("%s is %s\n", args[0], path); 65 } else { 66 printf("%s not found\n", args[0]); 67 } 68 } else { 69 printf("Usage: type [command]\n"); 70 } 71 } else { 72 char path[512]; 73 if (find_executable(cmd, path)){ 74 run_executable(path, args); 75 } else { 76 printf("%s: command not found\n", cmd); 77 } 78 } 79 80 //free mem 81 for (int i = 0; i < argc; i++){ 82 free(args[i]); 83 } 84 } 85 return 0; 86 } 87 88 89 void parse_input(const char *input, char *cmd, char **args, int *argc){ 90 91 char input_cpy[100]; 92 strncpy(input_cpy, input, sizeof(input_cpy) - 1); 93 input_cpy[sizeof(input_cpy) - 1] = '\0'; 94 95 char *token = strtok(input_cpy, " "); 96 if(token != NULL){ 97 strcpy(cmd, token); 98 } 99 100 *argc = 0; 101 while((token = strtok(NULL, " ")) != NULL){ 102 args[*argc] = malloc(strlen(token) + 1); 103 if (args[*argc] != NULL) { 104 strcpy(args[*argc], token); 105 (*argc)++; 106 } 107 } 108 109 args[*argc] = NULL; 110 } 111 112 bool find_executable(const char *cmd, char *result_path){ 113 114 // First, search in current folder 115 #ifdef _WIN32 116 const char *extensions[] = {".exe", NULL}; // Extension support list 117 // ------------ 118 if (strchr(cmd, '.')){ 119 snprintf(result_path, 512, ".\\%s", cmd); 120 if (access(result_path, F_OK) == 0){ 121 return true; 122 } 123 } else { 124 for (const char **ext = extensions; *ext != NULL; ext++){ 125 snprintf(result_path, 512, ".\\%s%s", cmd, *ext); 126 if (access(result_path, F_OK) == 0){ 127 return true; 128 } 129 } 130 131 #else 132 // In POSIX systems, simply search in current folder 133 snprintf(result_path, 512, "./%s", cmd); 134 if (access(result_path, F_OK) == 0){ 135 return true; 136 } 137 #endif 138 139 // Second, search in PATH 140 char *path_env = getenv("PATH"); 141 if(!path_env){ 142 return false; 143 } 144 145 char path_copy[1024]; 146 strncpy(path_copy, path_env, sizeof(path_copy) - 1); 147 path_copy[sizeof(path_copy) - 1] = '\0'; 148 149 char separator = ':'; 150 #ifdef _WIN32 151 separator = ';'; 152 #endif 153 154 char *dir = strtok(path_copy, &separator); 155 while (dir != NULL){ 156 if (strlen(dir) > 0) { 157 #ifdef _WIN32 158 if (strchr(cmd, '.')){ 159 snprintf(result_path, 512, "%s\\%s", dir, cmd); 160 if (access(result_path, F_OK) == 0){ 161 return true; 162 } 163 } else { 164 for (const char **ext = extensions; *ext != NULL; ext++){ 165 snprintf(result_path, 512, "%s\\%s%s", dir, cmd, *ext); 166 if (access(result_path, F_OK) == 0){ 167 return true; 168 } 169 } 170 } 171 #else 172 snprintf(result_path, 512, "%s/%s", dir, cmd); 173 if(access(result_path, X_OK) == 0){ 174 return true; 175 } 176 #endif 177 } 178 dir = strtok(NULL, &separator); 179 } 180 181 return false; 182 } 183 184 void run_executable(const char *cmd, char **args){ 185 #ifdef _WIN32 186 // 1. Build command line 187 char command_line[1024] = {0}; 188 snprintf(command_line, sizeof(command_line), "\"%s\"", cmd); 189 190 for (int i = 0; args[i] != NULL; i++){ 191 strncat(command_line, " ", sizeof(command_line) - strlen(command_line) - 1); 192 strncat(command_line, args[i], sizeof(command_line) - strlen(command_line) - 1); 193 } 194 195 // 2. Setup structures for 'CreateProcess' 196 STARTUPINFO si = {0}; 197 PROCESS_INFORMATION pi = {0}; 198 si.cb = sizeof(STARTUPINFO); 199 200 // 3. Try to create process 201 if (!CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ 202 fprintf(stderr, "Error: error executing command %s\n", cmd); 203 } else { 204 WaitForSingleObject(pi.hProcess, INFINITE); 205 CloseHandle(pi.hProcess); 206 CloseHandle(pi.hThread); 207 } 208 209 #else 210 // Use 'execvp' in POSIX systems 211 if (fork() == 0){ 212 execvp(cmd, args); 213 perror("Error executing command"); 214 exit(EXIT_FAILURE); 215 } else { 216 wait(NULL); 217 } 218 #endif 219 } 220 221