Updated the CLI Shell to use the new path, adding login to git
[tpg/acess2.git] / Usermode / Applications / CLIShell_src / main.c
1 /*\r
2  * AcessOS Shell Version 3\r
3  */\r
4 #include <acess/sys.h>\r
5 #include <stdlib.h>\r
6 #include <stdio.h>\r
7 #include "header.h"\r
8 \r
9 #define _stdin  0\r
10 #define _stdout 1\r
11 #define _stderr 2\r
12 \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
21 \r
22 // ==== CONSTANT GLOBALS ====\r
23 char    *cCOLOUR_NAMES[8] = {"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"};\r
24 struct  {\r
25         char    *name;\r
26         void    (*fcn)(int argc, char **argv);\r
27 }       cBUILTINS[] = {\r
28         {"colour", Command_Colour}, {"clear", Command_Clear},\r
29         {"cd", Command_Cd}, {"dir", Command_Dir}\r
30 };\r
31 static char     *cDEFAULT_PATH[] = {"/Acess/Bin"};\r
32 #define BUILTIN_COUNT   (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))\r
33 \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
43 \r
44 // ==== CODE ====\r
45 int main(int argc, char *argv[], char *envp[])\r
46 {\r
47         char    *sCommandStr;\r
48         char    *saArgs[32];\r
49          int    length = 0;\r
50          int    i;\r
51          int    iArgCount = 0;\r
52          int    bCached = 1;\r
53         \r
54         gasEnvironment = envp;\r
55         \r
56         Command_Clear(0, NULL);\r
57         \r
58         {\r
59                 char    *tmp = getenv("CWD");\r
60                 if(tmp) {\r
61                         gsCurrentDirectory = malloc(strlen(tmp)+1);\r
62                         strcpy(gsCurrentDirectory, tmp);\r
63                 } else {\r
64                         gsCurrentDirectory = malloc(2);\r
65                         strcpy(gsCurrentDirectory, "/");\r
66                 }\r
67         }       \r
68         \r
69         write(_stdout, 22, "Acess Shell Version 3\n");\r
70         write(_stdout,  2, "\n");\r
71         for(;;)\r
72         {\r
73                 // Free last command & arguments\r
74                 if(saArgs[0])   free(saArgs);\r
75                 if(!bCached)    free(sCommandStr);\r
76                 bCached = 0;\r
77                 write(_stdout, strlen(gsCurrentDirectory), gsCurrentDirectory);\r
78                 write(_stdout, 3, "$ ");\r
79                 \r
80                 // Read Command line\r
81                 sCommandStr = ReadCommandLine( &length );\r
82                 \r
83                 if(!sCommandStr) {\r
84                         write(_stdout, 25, "PANIC: Out of heap space\n");\r
85                         return -1;\r
86                 }\r
87                 \r
88                 // Check if the command should be cached\r
89                 if(gasCommandHistory == NULL || strcmp(sCommandStr, gasCommandHistory[giLastCommand]) != 0)\r
90                 {\r
91                         if(giLastCommand >= giCommandSpace) {\r
92                                 giCommandSpace += 12;\r
93                                 gasCommandHistory = realloc(gasCommandHistory, giCommandSpace*sizeof(char*));\r
94                         }\r
95                         giLastCommand ++;\r
96                         gasCommandHistory[ giLastCommand ] = sCommandStr;\r
97                         bCached = 1;\r
98                 }\r
99                 \r
100                 // Parse Command Line into arguments\r
101                 Parse_Args(sCommandStr, saArgs);\r
102                 \r
103                 // Count Arguments\r
104                 iArgCount = 0;\r
105                 while(saArgs[iArgCount])        iArgCount++;\r
106                 \r
107                 // Silently Ignore all empty commands\r
108                 if(saArgs[1][0] == '\0')        continue;\r
109                 \r
110                 // Check Built-In Commands\r
111                 for( i = 0; i < BUILTIN_COUNT; i++ )\r
112                 {\r
113                         if( strcmp(saArgs[1], cBUILTINS[i].name) == 0 )\r
114                         {\r
115                                 cBUILTINS[i].fcn(iArgCount-1, &saArgs[1]);\r
116                                 break;\r
117                         }\r
118                 }\r
119                 if(i != BUILTIN_COUNT)  continue;\r
120                 \r
121                 // Shall we?\r
122                 CallCommand( &saArgs[1] );\r
123         }\r
124 }\r
125 \r
126 /**\r
127  * \fn char *ReadCommandLine(int *Length)\r
128  * \brief Read from the command line\r
129  */\r
130 char *ReadCommandLine(int *Length)\r
131 {\r
132         char    *ret;\r
133          int    len, pos, space = 1023;\r
134         char    ch;\r
135          \r
136         // Preset Variables\r
137         ret = malloc( space+1 );\r
138         if(!ret)        return NULL;\r
139         len = 0;        pos = 0;\r
140                 \r
141         // Read In Command Line\r
142         do {\r
143                 read(_stdin, 1, &ch);   // Read Character from stdin (read is a blocking call)\r
144                 // Ignore control characters\r
145                 if(ch == '\x1B') {\r
146                         read(_stdin, 1, &ch);   // Read control character\r
147                         switch(ch)\r
148                         {\r
149                         case 'D':       if(pos) pos--;  break;\r
150                         case 'C':       if(pos<len)     pos++;  break;\r
151                         case '[':\r
152                                 read(_stdin, 1, &ch);   // Read control character\r
153                                 switch(ch)\r
154                                 {\r
155                                 case 'D':       if(pos) pos--;  break;\r
156                                 case 'C':       if(pos<len)     pos++;  break;\r
157                                 }\r
158                         }\r
159                         continue;\r
160                 }\r
161                 // Backspace\r
162                 if(ch == '\b') {\r
163                         if(len <= 0)            continue;       // Protect against underflows\r
164                         if(pos == len) {        // Simple case of end of string\r
165                                 len --; pos--;\r
166                         } else {\r
167                                 memmove(&ret[pos-1], &ret[pos], len-pos);\r
168                                 pos --;\r
169                                 len --;\r
170                         }\r
171                         write(_stdout, 1, &ch);\r
172                         continue;\r
173                 }\r
174                 // Expand Buffer\r
175                 if(len > space) {\r
176                         space += 256;\r
177                         ret = realloc(ret, space+1);\r
178                         if(!ret)        return NULL;\r
179                 }\r
180                 \r
181                 write(_stdout, 1, &ch);\r
182                 ret[pos++] = ch;\r
183                 len ++;\r
184         } while(ch != '\n');\r
185         \r
186         // Remove newline\r
187         pos --;\r
188         ret[pos] = '\0';\r
189         \r
190         // Return length\r
191         if(Length)      *Length = len;\r
192         \r
193         return ret;\r
194 }\r
195 \r
196 /**\r
197  * \fn void Parse_Args(char *str, char **dest)\r
198  * \brief Parse a string into an argument array\r
199  */\r
200 void Parse_Args(char *str, char **dest)\r
201 {\r
202          int    i = 1;\r
203         char    *buf = malloc( strlen(str) + 1 );\r
204         \r
205         if(buf == NULL) {\r
206                 dest[0] = NULL;\r
207                 Print("Parse_Args: Out of heap space!\n");\r
208                 return ;\r
209         }\r
210         \r
211         strcpy(buf, str);\r
212         dest[0] = buf;\r
213         \r
214         // Trim leading whitespace\r
215         while(*buf == ' ' && *buf)      buf++;\r
216         \r
217         for(;;)\r
218         {\r
219                 dest[i] = buf;  // Save start of string\r
220                 i++;\r
221                 while(*buf && *buf != ' ')\r
222                 {\r
223                         if(*buf++ == '"')\r
224                         {\r
225                                 while(*buf && !(*buf == '"' && buf[-1] != '\\'))\r
226                                         buf++;\r
227                         }\r
228                 }\r
229                 if(*buf == '\0')        break;\r
230                 *buf = '\0';\r
231                 while(*++buf == ' ' && *buf);\r
232                 if(*buf == '\0')        break;\r
233         }\r
234         dest[i] = NULL;\r
235         if(i == 1) {\r
236                 free(buf);\r
237                 dest[0] = NULL;\r
238         }\r
239 }\r
240 \r
241 /**\r
242  * \fn void CallCommand(char **Args)\r
243  */\r
244 void CallCommand(char **Args)\r
245 {\r
246         t_sysFInfo      info;\r
247          int    pid = -1;\r
248          int    fd = 0;\r
249         char    sTmpBuffer[1024];\r
250         char    *exefile = Args[0];\r
251         \r
252         if(exefile[0] == '/'\r
253         || (exefile[0] == '.' && exefile[1] == '/')\r
254         || (exefile[0] == '.' && exefile[1] == '.' && exefile[2] == '/')\r
255                 )\r
256         {\r
257                 GeneratePath(exefile, gsCurrentDirectory, sTmpBuffer);\r
258                 // Check file existence\r
259                 fd = open(sTmpBuffer, OPENFLAG_EXEC);\r
260                 if(fd == -1) {\r
261                         Print("Unknown Command: `");Print(Args[0]);Print("'\n");        // Error Message\r
262                         return ;\r
263                 }\r
264                 \r
265                 // Get File info and close file\r
266                 finfo( fd, &info, 0 );\r
267                 close( fd );\r
268                 \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
273                         return ;\r
274                 }\r
275         }\r
276         else\r
277         {\r
278                  int    i;\r
279                 \r
280                 // Check all components of $PATH\r
281                 for( i = 0; i < giNumPathDirs; i++ )\r
282                 {\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
287                         close( fd );\r
288                         if(info.flags & FILEFLAG_DIRECTORY)     continue;\r
289                         // Woohoo! We found a valid command\r
290                         break;\r
291                 }\r
292                 \r
293                 // Exhausted path directories\r
294                 if( i == giNumPathDirs ) {\r
295                         Print("Unknown Command: `");Print(exefile);Print("'\n");\r
296                         return ;\r
297                 }\r
298         }\r
299         \r
300         // Create new process\r
301         pid = clone(CLONE_VM, 0);\r
302         // Start Task\r
303         if(pid == 0)\r
304                 execve(sTmpBuffer, Args, gasEnvironment);\r
305         if(pid <= 0) {\r
306                 Print("Unablt to create process: `");Print(sTmpBuffer);Print("'\n");    // Error Message\r
307         }\r
308         else {\r
309                  int    status;\r
310                 waittid(pid, &status);\r
311         }\r
312 }\r
313 \r
314 /**\r
315  * \fn void Command_Colour(int argc, char **argv)\r
316  * \brief \r
317  */\r
318 void Command_Colour(int argc, char **argv)\r
319 {\r
320         int fg, bg;\r
321         char    clrStr[6] = "\x1B[37m";\r
322         \r
323         // Verify Arg Count\r
324         if(argc < 2)\r
325         {\r
326                 goto usage;\r
327         }\r
328         \r
329         // Check Colour\r
330         for(fg=0;fg<8;fg++)\r
331                 if(strcmp(cCOLOUR_NAMES[fg], argv[1]) == 0)\r
332                         break;\r
333 \r
334         // Foreground a valid colour\r
335         if(fg == 8) {\r
336                 Print("Unknown Colour '");Print(argv[1]);Print("'\n");\r
337                 goto usage;\r
338         }\r
339         // Set Foreground\r
340         clrStr[3] = '0' + fg;\r
341         write(_stdout, 6, clrStr);\r
342         \r
343         // Need to Set Background?\r
344         if(argc > 2)\r
345         {\r
346                 for(bg=0;bg<8;bg++)\r
347                         if(strcmp(cCOLOUR_NAMES[bg], argv[2]) == 0)\r
348                                 break;\r
349         \r
350                 // Valid colour\r
351                 if(bg == 8)\r
352                 {\r
353                         Print("Unknown Colour '");Print(argv[2]);Print("'\n");\r
354                         goto usage;\r
355                 }\r
356         \r
357                 clrStr[2] = '4';\r
358                 clrStr[3] = '0' + bg;\r
359                 write(_stdout, 6, clrStr);\r
360         }\r
361         // Return\r
362         return;\r
363 \r
364         // Function Usage (Requested via a Goto (I know it's ugly))\r
365 usage:\r
366         Print("Usage: colour <foreground> [<background>]\n");\r
367         Print("Valid Colours are ");\r
368         for(fg=0;fg<8;fg++)\r
369         {\r
370                 Print(cCOLOUR_NAMES[fg]);\r
371                 write(_stdout, 3, ", ");\r
372         }\r
373         write(_stdout, 4, "\b\b\n");\r
374         return;\r
375 }\r
376 \r
377 /**\r
378  * \fn void Command_Clear(int argc, char **argv)\r
379  * \brief Clear the screen\r
380  */\r
381 void Command_Clear(int argc, char **argv)\r
382 {\r
383         write(_stdout, 4, "\x1B[2J");   //Clear Screen\r
384 }\r
385 \r
386 /**\r
387  * \fn void Command_Cd(int argc, char **argv)\r
388  * \brief Change directory\r
389  */\r
390 void Command_Cd(int argc, char **argv)\r
391 {\r
392         char    tmpPath[1024];\r
393         int             fp;\r
394         t_sysFInfo      stats;\r
395         \r
396         if(argc < 2)\r
397         {\r
398                 Print(gsCurrentDirectory);Print("\n");\r
399                 return;\r
400         }\r
401         \r
402         GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
403         \r
404         fp = open(tmpPath, 0);\r
405         if(fp == -1) {\r
406                 write(_stdout, 26, "Directory does not exist\n");\r
407                 return;\r
408         }\r
409         finfo(fp, &stats, 0);\r
410         close(fp);\r
411         \r
412         if( !(stats.flags & FILEFLAG_DIRECTORY) ) {\r
413                 write(_stdout, 17, "Not a Directory\n");\r
414                 return;\r
415         }\r
416         \r
417         free(gsCurrentDirectory);\r
418         gsCurrentDirectory = malloc(strlen(tmpPath)+1);\r
419         strcpy(gsCurrentDirectory, tmpPath);\r
420         \r
421         // Register change with kernel\r
422         chdir( gsCurrentDirectory );\r
423 }\r
424 \r
425 /**\r
426  */\r
427 void Command_Dir(int argc, char **argv)\r
428 {\r
429          int    dp, fp, dirLen;\r
430         char    modeStr[11] = "RWXrwxRWX ";\r
431         char    tmpPath[1024];\r
432         char    *fileName;\r
433         t_sysFInfo      info;\r
434         t_sysACL        acl;\r
435         \r
436         // Generate Directory Path\r
437         if(argc > 1)\r
438                 dirLen = GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
439         else\r
440         {\r
441                 strcpy(tmpPath, gsCurrentDirectory);\r
442         }\r
443         dirLen = strlen(tmpPath);\r
444         \r
445         // Open Directory\r
446         dp = open(tmpPath, OPENFLAG_READ);\r
447         // Check if file opened\r
448         if(dp == -1)\r
449         {\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
454                 return;\r
455         }\r
456         // Get File Stats\r
457         if( finfo(dp, &info, 0) == -1 )\r
458         {\r
459                 close(dp);\r
460                 write(_stdout, 34, "stat Failed, Bad File Descriptor\n");\r
461                 return;\r
462         }\r
463         // Check if it's a directory\r
464         if(!(info.flags & FILEFLAG_DIRECTORY))\r
465         {\r
466                 close(dp);\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
470                 return;\r
471         }\r
472         \r
473         // Append Shash for file paths\r
474         if(tmpPath[dirLen-1] != '/')\r
475         {\r
476                 tmpPath[dirLen++] = '/';\r
477                 tmpPath[dirLen] = '\0';\r
478         }\r
479         \r
480         fileName = (char*)(tmpPath+dirLen);\r
481         // Read Directory Content\r
482         while( (fp = readdir(dp, fileName)) )\r
483         {\r
484                 if(fp < 0)\r
485                 {\r
486                         if(fp == -3)\r
487                                 write(_stdout, 42, "Invalid Permissions to traverse directory\n");\r
488                         break;\r
489                 }\r
490                 // Open File\r
491                 fp = open(tmpPath, 0);\r
492                 if(fp == -1)    continue;\r
493                 // Get File Stats\r
494                 finfo(fp, &info, 0);\r
495                 \r
496                 if(info.flags & FILEFLAG_DIRECTORY)\r
497                         write(_stdout, 1, "d");\r
498                 else\r
499                         write(_stdout, 1, "-");\r
500                 \r
501                 // Print Mode\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
518                 close(fp);\r
519                 \r
520                 // Colour Code\r
521                 if(info.flags & FILEFLAG_DIRECTORY)     // Directory: Green\r
522                         write(_stdout, 6, "\x1B[32m");\r
523                 // Default: White\r
524                 \r
525                 // Print Name\r
526                 write(_stdout, strlen(fileName), fileName);\r
527                 \r
528                 // Print slash if applicable\r
529                 if(info.flags & FILEFLAG_DIRECTORY)\r
530                         write(_stdout, 1, "/");\r
531                 \r
532                 // Revert Colour\r
533                 write(_stdout, 6, "\x1B[37m");\r
534                 \r
535                 // Newline!\r
536                 write(_stdout, 1, "\n");\r
537         }\r
538         // Close Directory\r
539         close(dp);\r
540 }\r

UCC git Repository :: git.ucc.asn.au