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 && cursor_pos > 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 187 /* only print printable caracters */ 188 if (isprint(c)) { 189 if (cursor_pos < line_len) { 190 memmove(s + cursor_pos + 1, s + cursor_pos, line_len - cursor_pos); 191 putchar(c); 192 s[cursor_pos++] = c; 193 line_len += 1; 194 195 buffer_print_slice(s, cursor_pos, line_len); 196 197 /* move left the amount buffer_print_slice moved the cursor */ 198 printf("\033[%ldD", line_len - cursor_pos); 199 } else { 200 putchar(c); 201 s[line_len++] = c; 202 cursor_pos++; 203 } 204 } 205 } 206 printf("\r\n"); 207 s[line_len] = '\0'; 208 return s; 209 } 210 211 bool is_builtin(const char *name) { 212 size_t i; 213 for(i = builtin_index = 0; i < TOTAL_BUILTINS; i++) 214 if(!strcmp(name, msh_builtins_name[i])) 215 return true; 216 217 return false; 218 } 219 220 void msh_cd(const char **path) { 221 if(path[1] == NULL) { 222 /* go ot user home */ 223 struct passwd *pw = getpwuid(getuid()); 224 const char *user_home_dir = pw->pw_dir; 225 if(chdir(user_home_dir) < 0) { 226 perror("cd"); 227 } 228 } 229 else { 230 if(chdir(path[1]) < 0) 231 fprintf(stderr, 232 "%s: no such file or directory: %s\n", 233 "cd", path[0]); 234 } 235 } 236 237 static void (*msh_builtin_handler[])(const char **argv) = { 238 &msh_cd, 239 }; 240 241 void msh_execute_builtin(char **argv) { 242 msh_builtin_handler[builtin_index]((const char **)argv); 243 } 244 245 void msh_execute(char **argv) { 246 int status; 247 pid_t pid; 248 249 pid = fork(); 250 if (pid == 0) { 251 /* child process */ 252 if(execvp(argv[0], argv) < 0) 253 fprintf(stderr, "%s: command not found\n", argv[0]); 254 255 exit(EXIT_FAILURE); 256 } 257 else if (pid < 0) { 258 perror("msh: could not fork"); 259 } 260 else { 261 do { 262 waitpid(pid, &status, WUNTRACED); 263 } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 264 } 265 } 266 267 void msh_loop(void) { 268 char *buffer, **tokens; 269 270 buffer_alloc = READLINE_BUFFER_INIT_SIZE; 271 if(!(buffer = calloc(buffer_alloc, sizeof(char)))) 272 exit(EXIT_FAILURE); 273 274 tokens_alloc = TOKENS_BUFFER_INIT_SIZE; 275 if(!(tokens = malloc(sizeof(char*) * tokens_alloc))) { 276 free(buffer); 277 exit(EXIT_FAILURE); 278 } 279 280 while (run) { 281 tui_set_input_mode(); 282 283 /* make cusor blinking vertical bar */ 284 printf("\r\033[5 q$ "); 285 buffer = editor_read_line(buffer); 286 287 tui_reset_input_mode(); 288 289 tokens = buffer_split(buffer, tokens); 290 291 /* skip execute if buffer is empty */ 292 if(strlen(buffer) > 0) { 293 if(is_builtin(tokens[0])) 294 msh_execute_builtin(tokens); 295 else 296 msh_execute(tokens); 297 } 298 299 buffer_clear(buffer); 300 } 301 free(tokens); 302 free(buffer); 303 } 304 305 int main (void) { 306 msh_loop(); 307 return 0; 308 }
