shell: basic functions works (not stable yet)
[meinos.git] / apps / sh / main.c
blob6cab5161f80321a7d8ebb2003124b1073ad45798
1 /*
2 meinOS - A unix-like x86 microkernel operating system
3 Copyright (C) 2008 Janosch Gräf <janosch.graef@gmx.net>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <sys/utsname.h>
26 #include <pwd.h>
27 #include <errno.h>
28 #include <misc.h>
30 #include <readline/readline.h>
31 #include <readline/history.h>
33 #define TERMINAL_DEVICE "/dev/console"
35 typedef struct {
36 const char *cmd;
37 int (*func)(char **argv);
38 } shell_builtin_cmd_t;
40 typedef struct {
41 pid_t pid;
42 char *path;
43 char **argv;
44 int stdin;
45 int stdout;
46 int stderr;
47 } shell_proc_t;
49 struct utsname utsname;
50 struct passwd *passwd;
52 static void usage(char *cmd,int err) {
53 FILE *out = err?stderr:stdout;
54 fprintf(out,"Usage: %s\n");
57 /**
58 * Gets command from shell
59 * @return Command line
61 static char *shell_get_command() {
62 char *cwd = getcwd(NULL,0);
63 char *prompt;
64 char *input;
66 asprintf(&prompt,"%s@%s:%s> ",passwd!=NULL?passwd->pw_name:"nobody",utsname.nodename,cwd);
67 input = readline(prompt);
68 if (input && input[0]!=0) add_history(input);
69 else return NULL;
71 free(prompt);
72 free(cwd);
73 return input;
76 /**
77 * Converts a string with an octal number to an integer
78 * @param string String holding octal number
79 * @return String as integer
81 static int octal2num(char *str) {
82 char buf[4];
83 memcpy(buf,str,3);
84 buf[3] = 0;
85 int num = strtoul(buf,NULL,8);
86 return num;
89 /**
90 * Parses an command line argument
91 * @param arg Command line argument
92 * @param len Length of argument
93 * @return Parse arguments
95 static char *shell_parse_arg(char *arg,size_t len) {
96 char *new = malloc(len+1);
97 size_t i,j;
99 for (i=0,j=0;i<len;i++,j++) {
100 if (arg[i]=='\\' && i+1<len) { // escape code
101 i++;
102 if (arg[i]=='\\') new[j] = '\\'; // backslash
103 else if (arg[i]=='\"') new[j] = '\"'; // quotation mark
104 else if (arg[i]=='a') new[j] = '\a'; // alert
105 else if (arg[i]=='b') new[j] = '\b'; // backspace
106 else if (arg[i]=='f') new[j] = '\f'; // form feed
107 else if (arg[i]=='n') new[j] = '\n'; // new line
108 else if (arg[i]=='r') new[j] = '\r'; // carriage return
109 else if (arg[i]=='t') new[j] = '\t'; // horizontal tab
110 else if (arg[i]=='v') new[j] = '\v'; // vertical tab
111 else if (arg[i]=='0' && arg[i+1]!=0 && arg[i+2]!=0 && arg[i+3]!=0) {
112 // character in octal
113 new[j] = octal2num(arg+i+1);
116 else new[j] = arg[i];
119 new[j] = 0;
121 return new;
125 * Parses a command line string
126 * @param cmd Command line string
127 * @return Argument vector
129 static char **shell_parse_cmd(char *cmd) {
130 int quote = 0;
131 char *cur = cmd;
132 char *next;
133 char **argv = NULL;
134 int argc = 0;
136 // skip beginning spaces
137 while (*cur==' ') cur++;
139 do {
140 // find next space (or quotation mark)
141 next = strchr(cur,quote?'\"':' ');
142 if (next==NULL) next = cmd+strlen(cmd);
143 // if next character is a quotation mark, activate qoute mode
144 //if (*next=='\"') mode = 1;
146 // skip spaces
147 //while (*next==' ') next++;
149 // add current to argv
150 argv = realloc(argv,(argc+1)*sizeof(char*));
151 argv[argc++] = shell_parse_arg(cur,next-cur);
152 cur = next+1;
153 } while (*next!=0);
155 argv = realloc(argv,(argc+1)*sizeof(char*));
156 argv[argc] = NULL;
158 return argv;
161 static int shell_builtin_exit(char **argv) {
162 return 1;
165 static int shell_builtin_cd(char **argv) {
166 if (argv[1]!=NULL) {
167 if (chdir(argv[1])==-1) fprintf(stderr,"sh: cd: %s: %s\n",argv[1],strerror(errno));
169 return 0;
172 static int shell_builtin_help(char **argv) {
173 printf("Built-in commands:\n"
174 " exit Exit shell\n"
175 " cd DIR Change working directory to DIR\n"
176 " help Show this help dialog\n"
177 " version Show version\n");
178 return 0;
181 static int shell_builtin_version(char **argv) {
182 printf("%s %s\n",utsname.sysname,utsname.version);
183 return 0;
186 static int shell_run_builtin(char **argv) {
187 shell_builtin_cmd_t shell_builtin_cmds[] = {
188 { .cmd = "exit", .func = shell_builtin_exit },
189 { .cmd = "cd", .func = shell_builtin_cd },
190 { .cmd = "help", .func = shell_builtin_help },
191 { .cmd = "version", .func = shell_builtin_version }
193 size_t i;
195 for (i=0;i<sizeof(shell_builtin_cmds)/sizeof(shell_builtin_cmd_t);i++) {
196 if (strcmp(argv[0],shell_builtin_cmds[i].cmd)==0) return shell_builtin_cmds[i].func(argv);
199 return -1;
202 static char *shell_find_path(char *cmd) {
203 char *path;
204 /// @todo read from PATH enviroment variable
205 asprintf(&path,"/boot/bin/%s",cmd);
206 return path;
209 static int shell_run_binary(char **argv,int background) {
210 shell_proc_t *proc = malloc(sizeof(shell_proc_t));
211 proc->path = shell_find_path(argv[0]);
212 proc->argv = argv;
213 proc->pid = execute(proc->path,argv,&(proc->stdin),&(proc->stdout),&(proc->stderr));
215 if (proc->pid==-1) {
216 free(proc->path);
217 free(proc);
218 return -1;
221 if (background) {
224 else {
225 int status = 0;
226 waitpid(proc->pid,&status,0);
228 close(proc->stdin);
229 close(proc->stdout);
230 close(proc->stderr);
231 free(proc->path);
232 free(proc);
234 if (status!=0) fprintf(stderr,"sh: %s: returned with %d\n",argv[0],status);
237 return 0;
241 * Runs shell interactive
243 static void shell_interactive() {
244 int status = 0;
246 while (status!=1) {
247 size_t i;
248 char **argv;
249 char *cmd = shell_get_command();
251 if (cmd==NULL) break;
252 argv = shell_parse_cmd(cmd);
254 if ((status = shell_run_builtin(argv))==-1) {
255 if (shell_run_binary(argv,0)==-1) fprintf(stderr,"sh: %s: command not found\n",argv[0]);
258 free(cmd);
259 for (i=0;argv[i];i++) free(argv[i]);
260 free(argv);
264 int main(int argc,char *argv[]) {
265 int c;
267 FILE *terminal = fopen(TERMINAL_DEVICE,"r+");
268 if (terminal==NULL) return 1;
269 FILE *stdin_bak = stdin;
270 FILE *stdout_bak = stdout;
271 FILE *stderr_bak = stderr;
272 stdin = terminal;
273 stdout = terminal;
274 stderr = terminal;
276 while ((c = getopt(argc,argv,":hv"))!=-1) {
277 switch(c) {
278 case 'h':
279 usage(argv[0],0);
280 break;
281 case 'v':
282 printf("sh v0.1\n(c) 2008 Janosch Graef\n");
283 return 0;
284 break;
285 case '?':
286 fprintf(stderr,"Unrecognized option: -%c\n", optopt);
287 usage(argv[0],1);
288 break;
292 uname(&utsname);
293 passwd = getpwuid(getuid());
294 shell_interactive();
296 stdin = stdin_bak;
297 stdout = stdout_bak;
298 stderr = stderr_bak;
299 fclose(terminal);
301 return 0;