summaryrefslogtreecommitdiff
path: root/userspace/fsh.c
diff options
context:
space:
mode:
Diffstat (limited to 'userspace/fsh.c')
-rw-r--r--userspace/fsh.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/userspace/fsh.c b/userspace/fsh.c
new file mode 100644
index 0000000..4df4e4e
--- /dev/null
+++ b/userspace/fsh.c
@@ -0,0 +1,320 @@
+/**
+ * @file
+ *
+ * Fool's Shell
+ * =========
+ *
+ * A minimalsitic and naive shell developed along the Fool OS kernel.
+ * TODO: Free tokenizer / dynamic size!
+ * TODO: & with pipes
+ * TODO: > <
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include "interface/fs.h"
+
+#include "newcalls.h"
+
+extern char **environ;
+
+bool process(char *buf);
+bool metaprocess(char *buf);
+bool cd(char *path);
+
+void version()
+{
+ puts("Fool's Shell version git-commit:" GIT_REVISION "\ncompiled on " __DATE__ " at " __TIME__);
+}
+void help()
+{
+ puts( "\nfoolshell: supported built-in commands/functions:\n\n"
+
+ "'env' - show all environment variables\n"
+ "'getenv [var]' - show environment variable\n"
+ "'setenv [var] [val]' - set environemnet variable\n"
+
+ "'cd [dir]' - change directory (set $PWD)\n"
+ "'[binary] [params...]' - run a binary\n"
+ "'[binary] [params...] &' - run a binary in background\n"
+ " | - pipes\n"
+
+ "'help' - show this message\n"
+ "'exit' - exit running foolshell\n\n");
+
+}
+
+void prompt()
+{
+ printf("\033[36mfool\033[37m@\033[32mhill\033[33m:%s%s\033[37m",getenv("PWD"),getenv("PS1"));
+}
+
+int main(int argc, char **argv)
+{
+ for(int i=0;i<argc;i++)
+ {
+ if(!strcmp(argv[i],"--version"))
+ {
+ version();
+ return 0;
+ }
+
+ if(!strcmp(argv[i],"--help"))
+ {
+ help();
+ return 0;
+ }
+ }
+
+ char *buf=calloc(sizeof(char),256);
+
+ // input and output without buffering
+ setvbuf(stdin,NULL,_IONBF,0);
+ setvbuf(stdout,NULL,_IONBF,0);
+
+ printf("\033c"); // clear screen
+
+ while(1) // process commands until exit
+ {
+ prompt();
+ int bl=0;
+
+ while(1)
+ {
+ char c=fgetc(stdin);
+
+ if(c=='\b')
+ {
+ if(bl==0)continue;
+ buf[--bl]='\0';
+ putc(c,stdout);
+ }
+ else
+ {
+ putc(c,stdout);
+ if(c=='\n')break;
+ buf[bl]=c;
+ buf[++bl]='\0';
+ }
+ }
+
+ if(!metaprocess(buf))break; // process input and return if exit
+ }
+
+ return 0;
+}
+
+// break the input in tokens on spaces
+char **tokenize(char *buf,char chr)
+{
+ char **token;
+ token=malloc(10*sizeof(char*));
+ token=realloc(token,20*sizeof(char*));
+
+ int l=strlen(buf);
+
+ int i;
+ int c=0;
+
+ for(i=0;i<l;i++)
+ {
+ // init space for next token
+ token[c]=malloc(256);
+
+ //skip all the whitespace
+ while(buf[i]==chr&&i<l)i++;
+ if(i==l)break;
+
+ //get token
+ int t=0;
+
+ while(buf[i]!=chr&&i<l)
+ {
+ token[c][t]=buf[i];
+ t++;
+ i++;
+ }
+ token[c][t]=0;
+
+// printf("token %i : <%s>\n",c, token[c]);
+ c++;
+ token[c]=NULL;
+
+ }
+ return token;
+}
+
+bool metaprocess2(char *buf);
+bool metaprocess(char *buf)
+{
+ char **token=tokenize(buf,'|');
+ if(token[0]==0 || token[1]==0)return process(buf); // no pipes
+
+ int pid=_fork();
+
+ if(pid==0)
+ {
+ metaprocess2(buf);
+ exit(1);
+ }
+
+ _wait(pid);
+ return true;
+}
+
+bool metaprocess2(char *buf)
+{
+ char **token=tokenize(buf,'|');
+ if(token[0]==0 || token[1]==0)return process(buf); // no pipes
+
+ int fds[2];
+ _pipe(fds);
+
+ int pid=_fork();
+
+ if(pid==0)
+ {
+ // first child shall write to the pipe
+ _close(fds[0]);
+ _dup2(fds[1],1); // replace stdout with the write-end of our pipe
+ process(token[0]);
+ exit(1);
+ }
+
+ _close(fds[1]); // and we read from it
+ _dup2(fds[0],0);
+ metaprocess2(strchr(buf,'|')+1);
+
+ _wait(pid);
+
+ return true;
+}
+
+bool process(char *buf)
+{
+ char **token=tokenize(buf,' ');
+ char *command=token[0];
+
+ if(!strcmp(command,"help"))help();
+ else if(!strcmp(command,"cd"))cd(token[1]);
+ else if(!strcmp(command,"exit")) return false;
+ //else if(!strcmp(command,"getenv"))printf("(0x%08X) get: '%s' = '%s'(0x%08X) \n",environ,token[1],getenv(token[1]),getenv(token[1]));
+ else if(!strcmp(command,"getenv"))printf("%s\n",getenv(token[1]));
+ else if(!strcmp(command,"setenv"))
+ {
+ sprintf(buf,"%s=%s",token[1],token[2]);
+ putenv(buf);
+ //printf("(0x%08X) set: '%s' = '%s' \n",environ,token[1],getenv(token[1]));
+ }
+ else if(!strcmp(command,"env"))
+ {
+ int i=0;
+// printf("env: 0x%08X\n",environ);
+ while(environ[i]!=NULL)
+ {
+// printf("envvar %s (0x%08X)\n" ,environ[i],environ[i]);
+ printf("%s\n" ,environ[i]);
+ i++;
+ }
+ }
+ else // otherwise treat command as exectutable and send to execve
+ {
+ int pid=_fork();
+
+ if(pid==0)
+ {
+ if(token[0][0]=='/')sprintf(buf,"%s",token[0]);
+ else sprintf(buf,"%s/%s",getenv("PATH"),token[0]);
+ _execve(buf,token,environ);
+ printf("foolshell: %s (errno: %d)\n",strerror(errno),errno);
+ exit(1);
+ }
+
+ if(token[1]==NULL||strcmp(token[1],"&"))_wait(pid);
+
+ }
+ return true;
+}
+
+bool setpwd(char *path)
+{
+ if(!strcmp(path,"/"))
+ {
+ putenv("PWD=/");
+ return true;
+ }
+ char buf[255];
+ buf[0]=0;
+ strcat(buf,"PWD=");
+
+ char **t=tokenize(path,'/');
+
+ char *p[100];
+ int pp=0;
+
+ while(*t!=NULL)
+ {
+ if(!strcmp(*t,"..")){
+ pp--;
+ t++;
+ if(pp<0)pp=0;
+ continue;
+ }
+ if(!strcmp(*t,".")){t++; continue;}
+ if(**t!=0)
+ {
+ p[pp]=*t;
+ //printf("> %s\n",p[pp]);
+ pp++;
+ }
+ t++;
+ }
+
+ if(pp==0)
+ {
+ strcat(buf,"/");
+ }
+
+ for(int i=0;i<pp;i++)
+ {
+ strcat(buf,"/");
+ strcat(buf,p[i]);
+ }
+
+ putenv(buf);
+ return true; // TODO check if dir exists at all.
+}
+
+bool cd(char *path)
+{
+ char buf[256];
+ // home
+ if(path==NULL)
+ {
+ sprintf(buf,"%s",getenv("HOME"));
+ }
+ // absolute
+ else if(path[0]=='/')
+ {
+ sprintf(buf,"%s",path);
+ }
+ // relative
+ else
+ {
+ sprintf(buf,"%s/%s",getenv("PWD"),path);
+ }
+
+ // check if exists
+ fs_dirent dirs;
+ if(-1==_readdir(buf,&dirs,0))
+ {
+ printf("directory not found!\n");
+ return false;
+ }
+
+ return setpwd(buf);
+}