2 * AcessOS Shell Version 3
\r
4 #define USE_READLINE 1
\r
5 #include <acess/sys.h>
\r
12 # include "readline.h"
\r
19 // ==== PROTOTYPES ====
\r
20 char *ReadCommandLine(int *Length);
\r
21 void Parse_Args(char *str, char **dest);
\r
22 void CallCommand(char **Args);
\r
23 void Command_Logout(int argc, char **argv);
\r
24 void Command_Clear(int argc, char **argv);
\r
25 void Command_Help(int argc, char **argv);
\r
26 void Command_Cd(int argc, char **argv);
\r
27 void Command_Dir(int argc, char **argv);
\r
29 // ==== CONSTANT GLOBALS ====
\r
32 void (*fcn)(int argc, char **argv);
\r
34 {"exit", Command_Logout}, {"logout", Command_Logout},
\r
35 {"help", Command_Help}, {"clear", Command_Clear},
\r
36 {"cd", Command_Cd}, {"dir", Command_Dir}
\r
38 static char *cDEFAULT_PATH[] = {"/Acess/Bin"};
\r
39 #define BUILTIN_COUNT (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))
\r
41 // ==== LOCAL VARIABLES ====
\r
42 int giNumPathDirs = 1;
\r
43 char **gasPathDirs = cDEFAULT_PATH;
\r
44 char **gasEnvironment;
\r
45 char gsCommandBuffer[1024];
\r
46 char *gsCurrentDirectory = NULL;
\r
47 char **gasCommandHistory;
\r
48 int giLastCommand = 0;
\r
49 int giCommandSpace = 0;
\r
52 int main(int argc, char *argv[], char *envp[])
\r
55 char *saArgs[32] = {0};
\r
62 tReadline readline_state = {0};
\r
66 readline_state.UseHistory = 1;
\r
69 gasEnvironment = envp;
\r
71 Command_Clear(0, NULL);
\r
74 char *tmp = getenv("CWD");
\r
76 gsCurrentDirectory = malloc(strlen(tmp)+1);
\r
77 strcpy(gsCurrentDirectory, tmp);
\r
79 gsCurrentDirectory = malloc(2);
\r
80 strcpy(gsCurrentDirectory, "/");
\r
84 write(_stdout, 22, "Acess Shell Version 3\n");
\r
85 write(_stdout, 2, "\n");
\r
88 // Free last command & arguments
\r
89 if(saArgs[0]) free(saArgs);
\r
91 if(!bCached) free(sCommandStr);
\r
95 write(_stdout, strlen(gsCurrentDirectory), gsCurrentDirectory);
\r
96 write(_stdout, 2, "$ ");
\r
98 // Read Command line
\r
100 sCommandStr = Readline( &readline_state );
\r
101 length = strlen(sCommandStr);
\r
103 sCommandStr = ReadCommandLine( &length );
\r
106 write(_stdout, 25, "PANIC: Out of heap space\n");
\r
110 // Check if the command should be cached
\r
111 if(gasCommandHistory == NULL || strcmp(sCommandStr, gasCommandHistory[giLastCommand]) != 0)
\r
113 if(giLastCommand >= giCommandSpace) {
\r
114 giCommandSpace += 12;
\r
115 gasCommandHistory = realloc(gasCommandHistory, giCommandSpace*sizeof(char*));
\r
118 gasCommandHistory[ giLastCommand ] = sCommandStr;
\r
123 // Parse Command Line into arguments
\r
124 Parse_Args(sCommandStr, saArgs);
\r
128 while(saArgs[iArgCount]) iArgCount++;
\r
130 // Silently Ignore all empty commands
\r
131 if(saArgs[1][0] == '\0') continue;
\r
133 // Check Built-In Commands
\r
134 for( i = 0; i < BUILTIN_COUNT; i++ )
\r
136 if( strcmp(saArgs[1], cBUILTINS[i].name) == 0 )
\r
138 cBUILTINS[i].fcn(iArgCount-1, &saArgs[1]);
\r
142 if(i != BUILTIN_COUNT) continue;
\r
145 CallCommand( &saArgs[1] );
\r
148 free( sCommandStr );
\r
154 * \fn char *ReadCommandLine(int *Length)
\r
155 * \brief Read from the command line
\r
157 char *ReadCommandLine(int *Length)
\r
160 int len, pos, space = 1023;
\r
163 int scrollbackPos = giLastCommand;
\r
166 // Preset Variables
\r
167 ret = malloc( space+1 );
\r
168 if(!ret) return NULL;
\r
171 // Read In Command Line
\r
173 read(_stdin, 1, &ch); // Read Character from stdin (read is a blocking call)
\r
175 if(ch == '\n') break;
\r
179 // Control characters
\r
181 read(_stdin, 1, &ch); // Read control character
\r
184 //case 'D': if(pos) pos--; break;
\r
185 //case 'C': if(pos<len) pos++; break;
\r
187 read(_stdin, 1, &ch); // Read control character
\r
194 if( scrollbackPos > 0 ) break;
\r
197 ret = strdup( gasCommandHistory[--scrollbackPos] );
\r
200 while(pos--) write(_stdout, 3, "\x1B[D");
\r
201 write(_stdout, len, ret); pos = len;
\r
202 while(pos++ < oldLen) write(_stdout, 1, " ");
\r
208 if( scrollbackPos < giLastCommand-1 ) break;
\r
211 ret = strdup( gasCommandHistory[++scrollbackPos] );
\r
214 while(pos--) write(_stdout, 3, "\x1B[D");
\r
215 write(_stdout, len, ret); pos = len;
\r
216 while(pos++ < oldLen) write(_stdout, 1, " ");
\r
221 if(pos == 0) break;
\r
223 write(_stdout, 3, "\x1B[D");
\r
226 if(pos == len) break;
\r
228 write(_stdout, 3, "\x1B[C");
\r
236 if(len <= 0) break; // Protect against underflows
\r
237 write(_stdout, 1, &ch);
\r
238 if(pos == len) { // Simple case of end of string
\r
243 char buf[7] = "\x1B[000D";
\r
244 buf[2] += ((len-pos+1)/100) % 10;
\r
245 buf[3] += ((len-pos+1)/10) % 10;
\r
246 buf[4] += (len-pos+1) % 10;
\r
247 write(_stdout, len-pos, &ret[pos]); // Move Text
\r
248 ch = ' '; write(_stdout, 1, &ch); ch = '\b'; // Clear deleted character
\r
249 write(_stdout, 7, buf); // Update Cursor
\r
251 memmove(&ret[pos-1], &ret[pos], len-pos);
\r
259 //TODO: Implement Tab-Completion
\r
260 //Currently just ignore tabs
\r
265 if(len+1 > space) {
\r
267 ret = realloc(ret, space+1);
\r
268 if(!ret) return NULL;
\r
271 // Editing inside the buffer
\r
273 char buf[7] = "\x1B[000D";
\r
274 buf[2] += ((len-pos)/100) % 10;
\r
275 buf[3] += ((len-pos)/10) % 10;
\r
276 buf[4] += (len-pos) % 10;
\r
277 write(_stdout, 1, &ch); // Print new character
\r
278 write(_stdout, len-pos, &ret[pos]); // Move Text
\r
279 write(_stdout, 7, buf); // Update Cursor
\r
280 memmove( &ret[pos+1], &ret[pos], len-pos );
\r
283 write(_stdout, 1, &ch);
\r
289 } while(ch != '\n');
\r
296 if(Length) *Length = len;
\r
302 * \fn void Parse_Args(char *str, char **dest)
\r
303 * \brief Parse a string into an argument array
\r
305 void Parse_Args(char *str, char **dest)
\r
308 char *buf = malloc( strlen(str) + 1 );
\r
312 Print("Parse_Args: Out of heap space!\n");
\r
319 // Trim leading whitespace
\r
320 while(*buf == ' ' && *buf) buf++;
\r
324 dest[i] = buf; // Save start of string
\r
326 while(*buf && *buf != ' ')
\r
330 while(*buf && !(*buf == '"' && buf[-1] != '\\'))
\r
334 if(*buf == '\0') break;
\r
336 while(*++buf == ' ' && *buf);
\r
337 if(*buf == '\0') break;
\r
347 * \fn void CallCommand(char **Args)
\r
349 void CallCommand(char **Args)
\r
354 char sTmpBuffer[1024];
\r
355 char *exefile = Args[0];
\r
357 if(exefile[0] == '/'
\r
358 || (exefile[0] == '.' && exefile[1] == '/')
\r
359 || (exefile[0] == '.' && exefile[1] == '.' && exefile[2] == '/')
\r
362 GeneratePath(exefile, gsCurrentDirectory, sTmpBuffer);
\r
363 // Check file existence
\r
364 fd = open(sTmpBuffer, OPENFLAG_EXEC);
\r
366 Print("Unknown Command: `");Print(Args[0]);Print("'\n"); // Error Message
\r
370 // Get File info and close file
\r
371 finfo( fd, &info, 0 );
\r
374 // Check if the file is a directory
\r
375 if(info.flags & FILEFLAG_DIRECTORY) {
\r
376 Print("`");Print(sTmpBuffer); // Error Message
\r
377 Print("' is a directory.\n");
\r
385 // Check all components of $PATH
\r
386 for( i = 0; i < giNumPathDirs; i++ )
\r
388 GeneratePath(exefile, gasPathDirs[i], sTmpBuffer);
\r
389 fd = open(sTmpBuffer, OPENFLAG_EXEC);
\r
390 if(fd == -1) continue;
\r
391 finfo( fd, &info, 0 );
\r
393 if(info.flags & FILEFLAG_DIRECTORY) continue;
\r
394 // Woohoo! We found a valid command
\r
398 // Exhausted path directories
\r
399 if( i == giNumPathDirs ) {
\r
400 Print("Unknown Command: `");Print(exefile);Print("'\n");
\r
405 // Create new process
\r
406 pid = clone(CLONE_VM, 0);
\r
409 execve(sTmpBuffer, Args, gasEnvironment);
\r
411 Print("Unablt to create process: `");Print(sTmpBuffer);Print("'\n"); // Error Message
\r
415 waittid(pid, &status);
\r
420 * \fn void Command_Logout(int argc, char **argv)
\r
421 * \brief Exit the shell, logging the user out
\r
423 void Command_Logout(int argc, char **argv)
\r
429 * \fn void Command_Colour(int argc, char **argv)
\r
430 * \brief Displays the help screen
\r
432 void Command_Help(int argc, char **argv)
\r
434 Print("Acess 2 Command Line Interface\n");
\r
435 Print(" By John Hodge (thePowersGang / [TPG])\n");
\r
437 Print("Builtin Commands:\n");
\r
438 Print(" logout: Return to the login prompt\n");
\r
439 Print(" exit: Same\n");
\r
440 Print(" help: Display this message\n");
\r
441 Print(" clear: Clear the screen\n");
\r
442 Print(" cd: Change the current directory\n");
\r
443 Print(" dir: Print the contents of the current directory\n");
\r
449 * \fn void Command_Clear(int argc, char **argv)
\r
450 * \brief Clear the screen
\r
452 void Command_Clear(int argc, char **argv)
\r
454 write(_stdout, 4, "\x1B[2J"); //Clear Screen
\r
458 * \fn void Command_Cd(int argc, char **argv)
\r
459 * \brief Change directory
\r
461 void Command_Cd(int argc, char **argv)
\r
463 char tmpPath[1024];
\r
469 Print(gsCurrentDirectory);Print("\n");
\r
473 GeneratePath(argv[1], gsCurrentDirectory, tmpPath);
\r
475 fp = open(tmpPath, 0);
\r
477 write(_stdout, 26, "Directory does not exist\n");
\r
480 finfo(fp, &stats, 0);
\r
483 if( !(stats.flags & FILEFLAG_DIRECTORY) ) {
\r
484 write(_stdout, 17, "Not a Directory\n");
\r
488 free(gsCurrentDirectory);
\r
489 gsCurrentDirectory = malloc(strlen(tmpPath)+1);
\r
490 strcpy(gsCurrentDirectory, tmpPath);
\r
492 // Register change with kernel
\r
493 chdir( gsCurrentDirectory );
\r
497 * \fn void Command_Dir(int argc, char **argv)
\r
498 * \brief Print the contents of a directory
\r
500 void Command_Dir(int argc, char **argv)
\r
502 int dp, fp, dirLen;
\r
503 char modeStr[11] = "RWXrwxRWX ";
\r
504 char tmpPath[1024];
\r
509 // Generate Directory Path
\r
511 dirLen = GeneratePath(argv[1], gsCurrentDirectory, tmpPath);
\r
514 strcpy(tmpPath, gsCurrentDirectory);
\r
516 dirLen = strlen(tmpPath);
\r
519 dp = open(tmpPath, OPENFLAG_READ);
\r
520 // Check if file opened
\r
523 printf("Unable to open directory `%s', File cannot be found\n", tmpPath);
\r
527 if( finfo(dp, &info, 0) == -1 )
\r
530 write(_stdout, 34, "stat Failed, Bad File Descriptor\n");
\r
533 // Check if it's a directory
\r
534 if(!(info.flags & FILEFLAG_DIRECTORY))
\r
537 write(_stdout, 27, "Unable to open directory `");
\r
538 write(_stdout, strlen(tmpPath)+1, tmpPath);
\r
539 write(_stdout, 20, "', Not a directory\n");
\r
543 // Append Shash for file paths
\r
544 if(tmpPath[dirLen-1] != '/')
\r
546 tmpPath[dirLen++] = '/';
\r
547 tmpPath[dirLen] = '\0';
\r
550 fileName = (char*)(tmpPath+dirLen);
\r
551 // Read Directory Content
\r
552 while( (fp = readdir(dp, fileName)) )
\r
557 write(_stdout, 42, "Invalid Permissions to traverse directory\n");
\r
561 fp = open(tmpPath, 0);
\r
562 if(fp == -1) continue;
\r
564 finfo(fp, &info, 0);
\r
566 if(info.flags & FILEFLAG_DIRECTORY)
\r
567 write(_stdout, 1, "d");
\r
569 write(_stdout, 1, "-");
\r
573 acl.group = 0; acl.id = info.uid;
\r
574 _SysGetACL(fp, &acl);
\r
575 if(acl.perms & 1) modeStr[0] = 'r'; else modeStr[0] = '-';
\r
576 if(acl.perms & 2) modeStr[1] = 'w'; else modeStr[1] = '-';
\r
577 if(acl.perms & 8) modeStr[2] = 'x'; else modeStr[2] = '-';
\r
579 acl.group = 1; acl.id = info.gid;
\r
580 _SysGetACL(fp, &acl);
\r
581 if(acl.perms & 1) modeStr[3] = 'r'; else modeStr[3] = '-';
\r
582 if(acl.perms & 2) modeStr[4] = 'w'; else modeStr[4] = '-';
\r
583 if(acl.perms & 8) modeStr[5] = 'x'; else modeStr[5] = '-';
\r
585 acl.group = 1; acl.id = -1;
\r
586 _SysGetACL(fp, &acl);
\r
587 if(acl.perms & 1) modeStr[6] = 'r'; else modeStr[6] = '-';
\r
588 if(acl.perms & 2) modeStr[7] = 'w'; else modeStr[7] = '-';
\r
589 if(acl.perms & 8) modeStr[8] = 'x'; else modeStr[8] = '-';
\r
590 write(_stdout, 10, modeStr);
\r
594 if(info.flags & FILEFLAG_DIRECTORY) // Directory: Green
\r
595 write(_stdout, 6, "\x1B[32m");
\r
599 write(_stdout, strlen(fileName), fileName);
\r
601 // Print slash if applicable
\r
602 if(info.flags & FILEFLAG_DIRECTORY)
\r
603 write(_stdout, 1, "/");
\r
606 write(_stdout, 6, "\x1B[37m");
\r
609 write(_stdout, 1, "\n");
\r