refactor: restructure the user space stuff.
[lunaix-os.git] / lunaix-os / usr / sh / sh.c
1 #include <errno.h>
2 #include <lunaix/ioctl.h>
3 #include <lunaix/lunaix.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 char pwd[512];
10 char cat_buf[1024];
11
12 /*
13     Simple shell - (actually this is not even a shell)
14     It just to make the testing more easy.
15 */
16
17 #define WS_CHAR(c)                                                             \
18     (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\v' || c == '\r')
19
20 void
21 strrtrim(char* str)
22 {
23     size_t l = strlen(str);
24     while (l < (size_t)-1) {
25         char c = str[l];
26         if (!c || WS_CHAR(c)) {
27             l--;
28             continue;
29         }
30         break;
31     }
32     str[l + 1] = '\0';
33 }
34
35 char*
36 strltrim_safe(char* str)
37 {
38     size_t l = 0;
39     char c = 0;
40     while ((c = str[l]) && WS_CHAR(c)) {
41         l++;
42     }
43
44     if (!l)
45         return str;
46     return strcpy(str, str + l);
47 }
48
49 void
50 parse_cmdline(char* line, char** cmd, char** arg_part)
51 {
52     strrtrim(line);
53     line = strltrim_safe(line);
54     int l = 0;
55     char c = 0;
56     while ((c = line[l])) {
57         if (c == ' ') {
58             line[l++] = 0;
59             break;
60         }
61         l++;
62     }
63     *cmd = line;
64     if (c) {
65         *arg_part = strltrim_safe(line + l);
66     } else {
67         *arg_part = NULL;
68     }
69 }
70
71 void
72 sh_printerr()
73 {
74     switch (errno) {
75         case 0:
76             break;
77         case ENOTDIR:
78             printf("Error: Not a directory\n");
79             break;
80         case ENOENT:
81             printf("Error: No such file or directory\n");
82             break;
83         case EINVAL:
84             printf("Error: Invalid parameter or operation\n");
85             break;
86         case ENOTSUP:
87             printf("Error: Not supported\n");
88             break;
89         case EROFS:
90             printf("Error: File system is read only\n");
91             break;
92         case ENOMEM:
93             printf("Error: Out of memory\n");
94             break;
95         case EISDIR:
96             printf("Error: This is a directory\n");
97             break;
98         default:
99             printf("Error: (%d)\n", errno);
100             break;
101     }
102 }
103
104 void
105 sigint_handle(int signum)
106 {
107     return;
108 }
109
110 void
111 sh_exec(const char* name, const char** argv)
112 {
113     if (!strcmp(name, "cd")) {
114         chdir(argv[0]);
115         sh_printerr();
116         return;
117     }
118
119     pid_t p;
120     if (!(p = fork())) {
121         if (execve(name, argv, NULL)) {
122             sh_printerr();
123         }
124         _exit(1);
125     }
126     setpgid(p, getpgid());
127     waitpid(p, NULL, 0);
128 }
129
130 void
131 sh_loop()
132 {
133     char buf[512];
134     char *cmd, *argpart;
135     signal(SIGINT, sigint_handle);
136
137     // set our shell as foreground process
138     // (unistd.h:tcsetpgrp is essentially a wrapper of this)
139     // stdout (by default, unless user did smth) is the tty we are currently at
140     ioctl(stdout, TIOCSPGRP, getpgid());
141
142     char* argv[] = { 0, 0 };
143
144     while (1) {
145         getcwd(pwd, 512);
146         printf("[\033[2m%s\033[39;49m]$ ", pwd);
147         int sz = read(stdin, buf, 511);
148
149         if (sz < 0) {
150             printf("fail to read user input (%d)\n", geterrno());
151             return;
152         }
153
154         buf[sz] = '\0';
155
156         // currently, this shell only support single argument
157         parse_cmdline(buf, &cmd, &argv[0]);
158
159         if (cmd[0] == 0) {
160             printf("\n");
161             goto cont;
162         }
163
164         // cmd=="exit"
165         if (*(unsigned int*)cmd == 0x74697865U) {
166             break;
167         }
168
169         sh_exec(cmd, (const char**)&argv);
170     cont:
171         printf("\n");
172     }
173 }
174
175 void
176 main()
177 {
178     printf("\n Simple shell. Use <PG_UP> or <PG_DOWN> to scroll.\n\n");
179     sh_loop();
180     _exit(0);
181 }