2 * AcessOS Shell Version 3
\r
4 #include <acess/sys.h>
\r
13 // ==== PROTOTYPES ====
\r
14 char *ReadCommandLine(int *Length);
\r
15 void Parse_Args(char *str, char **dest);
\r
16 void CallCommand(char **Args);
\r
17 void Command_Colour(int argc, char **argv);
\r
18 void Command_Clear(int argc, char **argv);
\r
19 void Command_Cd(int argc, char **argv);
\r
20 void Command_Dir(int argc, char **argv);
\r
22 // ==== CONSTANT GLOBALS ====
\r
23 char *cCOLOUR_NAMES[8] = {"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"};
\r
26 void (*fcn)(int argc, char **argv);
\r
28 {"colour", Command_Colour}, {"clear", Command_Clear},
\r
29 {"cd", Command_Cd}, {"dir", Command_Dir}
\r
31 static char *cDEFAULT_PATH[] = {"/Acess"};
\r
32 #define BUILTIN_COUNT (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))
\r
34 // ==== LOCAL VARIABLES ====
\r
35 int giNumPathDirs = 1;
\r
36 char **gasPathDirs = cDEFAULT_PATH;
\r
37 char **gasEnvironment;
\r
38 char gsCommandBuffer[1024];
\r
39 char *gsCurrentDirectory = NULL;
\r
40 char **gasCommandHistory;
\r
41 int giLastCommand = 0;
\r
42 int giCommandSpace = 0;
\r
45 int main(int argc, char *argv[], char *envp[])
\r
54 gasEnvironment = envp;
\r
56 Command_Clear(0, NULL);
\r
59 char *tmp = getenv("CWD");
\r
61 gsCurrentDirectory = malloc(strlen(tmp)+1);
\r
62 strcpy(gsCurrentDirectory, tmp);
\r
64 gsCurrentDirectory = malloc(2);
\r
65 strcpy(gsCurrentDirectory, "/");
\r
69 write(_stdout, 22, "Acess Shell Version 3\n");
\r
70 write(_stdout, 2, "\n");
\r
73 // Free last command & arguments
\r
74 if(saArgs[0]) free(saArgs);
\r
75 if(!bCached) free(sCommandStr);
\r
77 write(_stdout, strlen(gsCurrentDirectory), gsCurrentDirectory);
\r
78 write(_stdout, 3, "$ ");
\r
80 // Read Command line
\r
81 sCommandStr = ReadCommandLine( &length );
\r
84 write(_stdout, 25, "PANIC: Out of heap space\n");
\r
88 // Check if the command should be cached
\r
89 if(gasCommandHistory == NULL || strcmp(sCommandStr, gasCommandHistory[giLastCommand]) != 0)
\r
91 if(giLastCommand >= giCommandSpace) {
\r
92 giCommandSpace += 12;
\r
93 gasCommandHistory = realloc(gasCommandHistory, giCommandSpace*sizeof(char*));
\r
96 gasCommandHistory[ giLastCommand ] = sCommandStr;
\r
100 // Parse Command Line into arguments
\r
101 Parse_Args(sCommandStr, saArgs);
\r
105 while(saArgs[iArgCount]) iArgCount++;
\r
107 // Silently Ignore all empty commands
\r
108 if(saArgs[1][0] == '\0') continue;
\r
110 // Check Built-In Commands
\r
111 for( i = 0; i < BUILTIN_COUNT; i++ )
\r
113 if( strcmp(saArgs[1], cBUILTINS[i].name) == 0 )
\r
115 cBUILTINS[i].fcn(iArgCount-1, &saArgs[1]);
\r
119 if(i != BUILTIN_COUNT) continue;
\r
122 CallCommand( &saArgs[1] );
\r
127 * \fn char *ReadCommandLine(int *Length)
\r
128 * \brief Read from the command line
\r
130 char *ReadCommandLine(int *Length)
\r
133 int len, pos, space = 1023;
\r
136 // Preset Variables
\r
137 ret = malloc( space+1 );
\r
138 if(!ret) return NULL;
\r
141 // Read In Command Line
\r
143 read(_stdin, 1, &ch); // Read Character from stdin (read is a blocking call)
\r
144 // Ignore control characters
\r
146 read(_stdin, 1, &ch); // Read control character
\r
149 case 'D': if(pos) pos--; break;
\r
150 case 'C': if(pos<len) pos++; break;
\r
152 read(_stdin, 1, &ch); // Read control character
\r
155 case 'D': if(pos) pos--; break;
\r
156 case 'C': if(pos<len) pos++; break;
\r
163 if(len <= 0) continue; // Protect against underflows
\r
164 if(pos == len) { // Simple case of end of string
\r
167 memmove(&ret[pos-1], &ret[pos], len-pos);
\r
171 write(_stdout, 1, &ch);
\r
177 ret = realloc(ret, space+1);
\r
178 if(!ret) return NULL;
\r
181 write(_stdout, 1, &ch);
\r
184 } while(ch != '\n');
\r
191 if(Length) *Length = len;
\r
197 * \fn void Parse_Args(char *str, char **dest)
\r
198 * \brief Parse a string into an argument array
\r
200 void Parse_Args(char *str, char **dest)
\r
203 char *buf = malloc( strlen(str) + 1 );
\r
207 Print("Parse_Args: Out of heap space!\n");
\r
214 // Trim leading whitespace
\r
215 while(*buf == ' ' && *buf) buf++;
\r
219 dest[i] = buf; // Save start of string
\r
221 while(*buf && *buf != ' ')
\r
225 while(*buf && !(*buf == '"' && buf[-1] != '\\'))
\r
229 if(*buf == '\0') break;
\r
231 while(*++buf == ' ' && *buf);
\r
232 if(*buf == '\0') break;
\r
242 * \fn void CallCommand(char **Args)
\r
244 void CallCommand(char **Args)
\r
249 char sTmpBuffer[1024];
\r
250 char *exefile = Args[0];
\r
252 if(exefile[0] == '/'
\r
253 || (exefile[0] == '.' && exefile[1] == '/')
\r
254 || (exefile[0] == '.' && exefile[1] == '.' && exefile[2] == '/')
\r
257 GeneratePath(exefile, gsCurrentDirectory, sTmpBuffer);
\r
258 // Check file existence
\r
259 fd = open(sTmpBuffer, OPENFLAG_EXEC);
\r
261 Print("Unknown Command: `");Print(Args[0]);Print("'\n"); // Error Message
\r
265 // Get File info and close file
\r
266 finfo( fd, &info, 0 );
\r
269 // Check if the file is a directory
\r
270 if(info.flags & FILEFLAG_DIRECTORY) {
\r
271 Print("`");Print(sTmpBuffer); // Error Message
\r
272 Print("' is a directory.\n");
\r
280 // Check all components of $PATH
\r
281 for( i = 0; i < giNumPathDirs; i++ )
\r
283 GeneratePath(exefile, gasPathDirs[i], sTmpBuffer);
\r
284 fd = open(sTmpBuffer, OPENFLAG_EXEC);
\r
285 if(fd == -1) continue;
\r
286 finfo( fd, &info, 0 );
\r
288 if(info.flags & FILEFLAG_DIRECTORY) continue;
\r
289 // Woohoo! We found a valid command
\r
293 // Exhausted path directories
\r
294 if( i == giNumPathDirs ) {
\r
295 Print("Unknown Command: `");Print(exefile);Print("'\n");
\r
300 // Create new process
\r
301 pid = clone(CLONE_VM, 0);
\r
304 execve(sTmpBuffer, Args, gasEnvironment);
\r
306 Print("Unablt to create process: `");Print(sTmpBuffer);Print("'\n"); // Error Message
\r
310 waittid(pid, &status);
\r
315 * \fn void Command_Colour(int argc, char **argv)
\r
318 void Command_Colour(int argc, char **argv)
\r
321 char clrStr[6] = "\x1B[37m";
\r
323 // Verify Arg Count
\r
330 for(fg=0;fg<8;fg++)
\r
331 if(strcmp(cCOLOUR_NAMES[fg], argv[1]) == 0)
\r
334 // Foreground a valid colour
\r
336 Print("Unknown Colour '");Print(argv[1]);Print("'\n");
\r
340 clrStr[3] = '0' + fg;
\r
341 write(_stdout, 6, clrStr);
\r
343 // Need to Set Background?
\r
346 for(bg=0;bg<8;bg++)
\r
347 if(strcmp(cCOLOUR_NAMES[bg], argv[2]) == 0)
\r
353 Print("Unknown Colour '");Print(argv[2]);Print("'\n");
\r
358 clrStr[3] = '0' + bg;
\r
359 write(_stdout, 6, clrStr);
\r
364 // Function Usage (Requested via a Goto (I know it's ugly))
\r
366 Print("Usage: colour <foreground> [<background>]\n");
\r
367 Print("Valid Colours are ");
\r
368 for(fg=0;fg<8;fg++)
\r
370 Print(cCOLOUR_NAMES[fg]);
\r
371 write(_stdout, 3, ", ");
\r
373 write(_stdout, 4, "\b\b\n");
\r
378 * \fn void Command_Clear(int argc, char **argv)
\r
379 * \brief Clear the screen
\r
381 void Command_Clear(int argc, char **argv)
\r
383 write(_stdout, 4, "\x1B[2J"); //Clear Screen
\r
387 * \fn void Command_Cd(int argc, char **argv)
\r
388 * \brief Change directory
\r
390 void Command_Cd(int argc, char **argv)
\r
392 char tmpPath[1024];
\r
398 Print(gsCurrentDirectory);Print("\n");
\r
402 GeneratePath(argv[1], gsCurrentDirectory, tmpPath);
\r
404 fp = open(tmpPath, 0);
\r
406 write(_stdout, 26, "Directory does not exist\n");
\r
409 finfo(fp, &stats, 0);
\r
412 if( !(stats.flags & FILEFLAG_DIRECTORY) ) {
\r
413 write(_stdout, 17, "Not a Directory\n");
\r
417 free(gsCurrentDirectory);
\r
418 gsCurrentDirectory = malloc(strlen(tmpPath)+1);
\r
419 strcpy(gsCurrentDirectory, tmpPath);
\r
421 // Register change with kernel
\r
422 chdir( gsCurrentDirectory );
\r
427 void Command_Dir(int argc, char **argv)
\r
429 int dp, fp, dirLen;
\r
430 char modeStr[11] = "RWXrwxRWX ";
\r
431 char tmpPath[1024];
\r
436 // Generate Directory Path
\r
438 dirLen = GeneratePath(argv[1], gsCurrentDirectory, tmpPath);
\r
441 strcpy(tmpPath, gsCurrentDirectory);
\r
443 dirLen = strlen(tmpPath);
\r
446 dp = open(tmpPath, OPENFLAG_READ);
\r
447 // Check if file opened
\r
450 //printf("Unable to open directory `%s', File cannot be found\n", tmpPath);
\r
451 write(_stdout, 27, "Unable to open directory `");
\r
452 write(_stdout, strlen(tmpPath)+1, tmpPath);
\r
453 write(_stdout, 25, "', File cannot be found\n");
\r
457 if( finfo(dp, &info, 0) == -1 )
\r
460 write(_stdout, 34, "stat Failed, Bad File Descriptor\n");
\r
463 // Check if it's a directory
\r
464 if(!(info.flags & FILEFLAG_DIRECTORY))
\r
467 write(_stdout, 27, "Unable to open directory `");
\r
468 write(_stdout, strlen(tmpPath)+1, tmpPath);
\r
469 write(_stdout, 20, "', Not a directory\n");
\r
473 // Append Shash for file paths
\r
474 if(tmpPath[dirLen-1] != '/')
\r
476 tmpPath[dirLen++] = '/';
\r
477 tmpPath[dirLen] = '\0';
\r
480 fileName = (char*)(tmpPath+dirLen);
\r
481 // Read Directory Content
\r
482 while( (fp = readdir(dp, fileName)) )
\r
487 write(_stdout, 42, "Invalid Permissions to traverse directory\n");
\r
491 fp = open(tmpPath, 0);
\r
492 if(fp == -1) continue;
\r
494 finfo(fp, &info, 0);
\r
496 if(info.flags & FILEFLAG_DIRECTORY)
\r
497 write(_stdout, 1, "d");
\r
499 write(_stdout, 1, "-");
\r
502 acl.group = 0; acl.id = info.uid;
\r
503 _SysGetACL(fp, &acl);
\r
504 if(acl.perms & 1) modeStr[0] = 'r'; else modeStr[0] = '-';
\r
505 if(acl.perms & 2) modeStr[1] = 'w'; else modeStr[1] = '-';
\r
506 if(acl.perms & 8) modeStr[2] = 'x'; else modeStr[2] = '-';
\r
507 acl.group = 1; acl.id = info.gid;
\r
508 _SysGetACL(fp, &acl);
\r
509 if(acl.perms & 1) modeStr[3] = 'r'; else modeStr[3] = '-';
\r
510 if(acl.perms & 1) modeStr[4] = 'w'; else modeStr[4] = '-';
\r
511 if(acl.perms & 1) modeStr[5] = 'x'; else modeStr[5] = '-';
\r
512 acl.group = 1; acl.id = -1;
\r
513 _SysGetACL(fp, &acl);
\r
514 if(acl.perms & 1) modeStr[6] = 'r'; else modeStr[6] = '-';
\r
515 if(acl.perms & 1) modeStr[7] = 'w'; else modeStr[7] = '-';
\r
516 if(acl.perms & 1) modeStr[8] = 'x'; else modeStr[8] = '-';
\r
517 write(_stdout, 10, modeStr);
\r
521 if(info.flags & FILEFLAG_DIRECTORY) // Directory: Green
\r
522 write(_stdout, 6, "\x1B[32m");
\r
526 write(_stdout, strlen(fileName), fileName);
\r
528 // Print slash if applicable
\r
529 if(info.flags & FILEFLAG_DIRECTORY)
\r
530 write(_stdout, 1, "/");
\r
533 write(_stdout, 6, "\x1B[37m");
\r
536 write(_stdout, 1, "\n");
\r