msh

simple shell implementation
Index Commits Files Refs README LICENSE
msh.c (5729B)
   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 }