Second Extended Filesystem (ext2) and other improvements (#33)
[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 int
50 parse_cmdline(char* line, char** args)
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
64     args[0] = line;
65     if (c && l) {
66         args[1] = strltrim_safe(line + l);
67     } else {
68         args[1] = NULL;
69     }
70
71     return !!l;
72 }
73
74 void
75 sh_printerr()
76 {
77     switch (errno) {
78         case 0:
79             break;
80         case ENOTDIR:
81             printf("Error: Not a directory\n");
82             break;
83         case ENOENT:
84             printf("Error: No such file or directory\n");
85             break;
86         case EINVAL:
87             printf("Error: Invalid parameter or operation\n");
88             break;
89         case ENOTSUP:
90             printf("Error: Not supported\n");
91             break;
92         case EROFS:
93             printf("Error: File system is read only\n");
94             break;
95         case ENOMEM:
96             printf("Error: Out of memory\n");
97             break;
98         case EISDIR:
99             printf("Error: This is a directory\n");
100             break;
101         default:
102             printf("Error: (%d)\n", errno);
103             break;
104     }
105 }
106
107 void
108 sigint_handle(int signum)
109 {
110     return;
111 }
112
113 void
114 sh_exec(const char** argv)
115 {
116     static int prev_exit;
117     const char* envp[] = { 0 };
118     char* name = argv[0];
119     if (!strcmp(name, "cd")) {
120         chdir(argv[1] ? argv[1] : ".");
121         sh_printerr();
122         return;
123     }
124
125     if (!strcmp(name, "?")) {
126         printf("%d\n", prev_exit);
127         return;
128     }
129
130     char buffer[1024];
131     strcpy(buffer, "/usr/bin/");
132     strcpy(&buffer[9], name);
133
134     pid_t p;
135     int res;
136     if (!(p = fork())) {
137         if (execve(buffer, argv, envp)) {
138             sh_printerr();
139         }
140         _exit(1);
141     }
142     setpgid(p, getpgid());
143     waitpid(p, &res, 0);
144
145     prev_exit = WEXITSTATUS(res);
146 }
147
148 static char*
149 sanity_filter(char* buf)
150 {
151     int off = 0, i = 0;
152     char c;
153     do {
154         c = buf[i];
155         
156         if ((32 <= c && c <= 126) || !c) {
157             buf[i - off] = c;
158         }
159         else {
160             off++;
161         }
162
163         i++;
164     } while(c);
165
166     return buf;
167 }
168
169 void
170 sh_loop()
171 {
172     char buf[512];
173     char *cmd, *argpart;
174     signal(SIGINT, sigint_handle);
175
176     // set our shell as foreground process
177     // (unistd.h:tcsetpgrp is essentially a wrapper of this)
178     // stdout (by default, unless user did smth) is the tty we are currently at
179     ioctl(stdout, TIOCSPGRP, getpgid());
180
181     char* argv[] = {0, 0, 0};
182
183     while (1) {
184         getcwd(pwd, 512);
185         printf("[%s]$ ", pwd);
186         int sz = read(stdin, buf, 511);
187
188         if (sz < 0) {
189             printf("fail to read user input (%d)\n", geterrno());
190             return;
191         }
192
193         buf[sz] = '\0';
194         sanity_filter(buf);
195
196         // currently, this shell only support single argument
197         if (!parse_cmdline(buf, argv)) {
198             printf("\n");
199             goto cont;
200         }
201
202         // cmd=="exit"
203         if (*(unsigned int*)argv[0] == 0x74697865U) {
204             break;
205         }
206
207         sh_exec((const char**)argv);
208     cont:
209         printf("\n");
210     }
211 }
212
213 void
214 main()
215 {
216     sh_loop();
217     _exit(0);
218 }