1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <sys/types.h> 7 #include <sys/wait.h> 8 #include <pwd.h> 9 10 #include <ctype.h> 11 12 #include "tui.h" 13 14 #define TAB_SIZE 4 15 16 #define READLINE_BUFFER_INIT_SIZE 128 17 #define TOKENS_BUFFER_INIT_SIZE 8 18 #define TOKENS_DELIM " |" 19 20 static bool run = true; 21 static size_t buffer_alloc; 22 static size_t tokens_alloc; 23 static size_t builtin_index; 24 25 static const char *msh_builtins_name[] = { 26 "cd" 27 }; 28 29 #define TOTAL_BUILTINS sizeof(msh_builtins_name)/sizeof(msh_builtins_name[0]) 30 31 void buffer_print_slice(const char *buf, size_t from, size_t to) { 32 for(size_t i = from; i < to; i++) 33 printf("%c", buf[i]); 34 } 35 36 void buffer_clear(char *buf) { 37 size_t len = strlen(buf); 38 for (size_t i = 0; i < len; i++) 39 buf[i] = '\0'; 40 } 41 42 char *buffer_read_line(char *s) { 43 int c; 44 char *aux; 45 size_t used_size; 46 47 used_size = 0; 48 while (1) { 49 c = getchar(); 50 if((c == '\n') || (c == EOF)) 51 break; 52 53 if(used_size == (buffer_alloc - 1)) { 54 if(!(aux = realloc(s, buffer_alloc += buffer_alloc))) { 55 perror("msh"); 56 free(s); 57 return NULL; 58 } 59 s = aux; 60 } 61 s[used_size++] = c; 62 } 63 64 s[used_size] = '\0'; 65 if (c == EOF) run = false; 66 return s; 67 } 68 69 char **buffer_split(char *b, char **t) { 70 char *p, **aux; 71 size_t tokens_count; 72 73 tokens_count = 0; 74 for(p = b; (p = strtok(p, TOKENS_DELIM)); p = NULL) { 75 if((tokens_count + 1) == tokens_alloc) { 76 if(!(aux = realloc(t, sizeof(char*) * (tokens_alloc *= 2)))) { 77 perror("msh"); 78 for(size_t i = 0; i < tokens_count; i++) 79 free(t[i]); 80 free(t); 81 return NULL; 82 } 83 t = aux; 84 } 85 t[tokens_count++] = p; 86 } 87 t[tokens_count] = NULL; 88 return t; 89 } 90 91 char *editor_read_line(char *s) { 92 int c; 93 char *aux; 94 size_t line_len, cursor_pos; 95 96 line_len = cursor_pos = 0; 97 while(1) { 98 c = getchar(); 99 100 /* Ctrl-D */ 101 if((c == 4) || (c == EOF)) { 102 run = false; 103 buffer_clear(s); 104 break; 105 } 106 107 if(c == 3) { 108 buffer_clear(s); 109 break; 110 } 111 112 if((c == '\r') || (c == '\n')) 113 break; 114 115 /* ESC */ 116 if(c == 27) { 117 getchar(); /* skip '[' */ 118 switch (getchar()) { 119 case 'A': /* up */ 120 break; 121 case 'B': /* down */ 122 break; 123 case 'D': /* left */ 124 if(cursor_pos > 0) { 125 printf("\033[1D"); 126 cursor_pos--; 127 } 128 break; 129 case 'C': /* right */ 130 if(cursor_pos < line_len) { 131 printf("\033[1C"); 132 cursor_pos++; 133 } 134 break; 135 default: 136 break; 137 } 138 continue; 139 } 140 141 /* Backspace */ 142 if(c == 0x7f) { 143 if (line_len > 0) { 144 line_len -= 1; 145 cursor_pos -= 1; 146 printf("\b \b"); 147 if (cursor_pos < line_len) { 148 memmove(s + cursor_pos, s + cursor_pos + 1, line_len - cursor_pos + 1); 149 buffer_print_slice(s, cursor_pos, line_len); 150 putchar(' '); 151 printf("\033[%ldD", line_len - cursor_pos + 1); 152 } 153 } 154 continue; 155 } 156 157 /* Delete */ 158 if(c == 0x7e) { 159 if ((line_len > 0) && (cursor_pos < line_len)) { 160 line_len -= 1; 161 memmove(s + cursor_pos, s + cursor_pos + 1, line_len - cursor_pos); 162 buffer_print_slice(s, cursor_pos, line_len); 163 putchar(' '); 164 printf("\033[%ldD", line_len - cursor_pos + 1); 165 } 166 continue; 167 } 168 169 170 if(c == '\t') { 171 for(size_t i = 0; i < TAB_SIZE; i++) 172 putchar(' '); 173 174 line_len += TAB_SIZE; 175 continue; 176 } 177 178 if(line_len == (buffer_alloc - 1)) { 179 if(!(aux = realloc(s, buffer_alloc += buffer_alloc))) { 180 perror("msh"); 181 free(s); 182 return NULL; 183 } 184 s = aux; 185 } 186 if (cursor_pos < line_len) { 187 memmove(s + cursor_pos + 1, s + cursor_pos, line_len - cursor_pos); 188 189 putchar(c); 190 s[cursor_pos++] = c; 191 line_len += 1; 192 193 buffer_print_slice(s, cursor_pos, line_len); 194 195 /* move left the amount buffer_print_slice moved the cursor */ 196 printf("\033[%ldD", line_len - cursor_pos); 197 } else { 198 putchar(c); 199 s[line_len++] = c; 200 cursor_pos++; 201 } 202 } 203 printf("\r\n"); 204 s[line_len] = '\0'; 205 return s; 206 } 207 208 bool is_builtin(const char *name) { 209 size_t i; 210 for(i = builtin_index = 0; i < TOTAL_BUILTINS; i++) 211 if(!strcmp(name, msh_builtins_name[i])) 212 return true; 213 214 return false; 215 } 216 217 void msh_cd(const char **path) { 218 if(path[1] == NULL) { 219 /* go ot user home */ 220 struct passwd *pw = getpwuid(getuid()); 221 const char *user_home_dir = pw->pw_dir; 222 if(chdir(user_home_dir) < 0) { 223 perror("cd"); 224 } 225 } 226 else { 227 if(chdir(path[1]) < 0) 228 fprintf(stderr, 229 "%s: No usch file or directory: %s\n", 230 "cd", path[0]); 231 } 232 } 233 234 static void (*msh_builtin_handler[])(const char **argv) = { 235 &msh_cd, 236 }; 237 238 void msh_execute_builtin(char **argv) { 239 msh_builtin_handler[builtin_index]((const char **)argv); 240 } 241 242 void msh_execute(char **argv) { 243 int status; 244 pid_t pid; 245 246 pid = fork(); 247 if (pid == 0) { 248 /* child process */ 249 if(execvp(argv[0], argv) < 0) 250 perror(argv[0]); 251 252 exit(EXIT_FAILURE); 253 } 254 else if (pid < 0) { 255 perror("msh: could not fork"); 256 } 257 else { 258 do { 259 waitpid(pid, &status, WUNTRACED); 260 } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 261 } 262 } 263 264 void msh_loop(void) { 265 char *buffer, **tokens; 266 267 buffer_alloc = READLINE_BUFFER_INIT_SIZE; 268 if(!(buffer = calloc(buffer_alloc, sizeof(char)))) 269 exit(EXIT_FAILURE); 270 271 tokens_alloc = TOKENS_BUFFER_INIT_SIZE; 272 if(!(tokens = malloc(sizeof(char*) * tokens_alloc))) { 273 free(buffer); 274 exit(EXIT_FAILURE); 275 } 276 277 while (run) { 278 tui_set_input_mode(); 279 280 /* make cusor blinking vertical bar */ 281 printf("\r\033[5 q$ "); 282 buffer = editor_read_line(buffer); 283 284 tui_reset_input_mode(); 285 286 tokens = buffer_split(buffer, tokens); 287 288 /* skip execute if buffer is empty */ 289 if(strlen(buffer) > 0) { 290 if(is_builtin(tokens[0])) 291 msh_execute_builtin(tokens); 292 else 293 msh_execute(tokens); 294 } 295 296 buffer_clear(buffer); 297 } 298 free(tokens); 299 free(buffer); 300 } 301 302 int main (void) { 303 msh_loop(); 304 return 0; 305 }