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