8298c2a033d106d96e21b1dde918a6b328f05705
[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 #define BUILTIN_COUNT   (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))\r
32 \r
33 // ==== LOCAL VARIABLES ====\r
34 char    **gasEnvironment;\r
35 char    gsCommandBuffer[1024];\r
36 char    *gsCurrentDirectory = NULL;\r
37 char    gsTmpBuffer[1024];\r
38 char    **gasCommandHistory;\r
39  int    giLastCommand = 0;\r
40  int    giCommandSpace = 0;\r
41 \r
42 // ==== CODE ====\r
43 int main(int argc, char *argv[], char *envp[])\r
44 {\r
45         char    *sCommandStr;\r
46         char    *saArgs[32];\r
47          int    length = 0;\r
48          int    iArgCount = 0;\r
49          int    bCached = 1;\r
50         \r
51         gasEnvironment = envp;\r
52         \r
53         Command_Clear(0, NULL);\r
54         \r
55         {\r
56                 char    *tmp = getenv("CWD");\r
57                 if(tmp) {\r
58                         gsCurrentDirectory = malloc(strlen(tmp)+1);\r
59                         strcpy(gsCurrentDirectory, tmp);\r
60                 } else {\r
61                         gsCurrentDirectory = malloc(2);\r
62                         strcpy(gsCurrentDirectory, "/");\r
63                 }\r
64         }       \r
65         \r
66         write(_stdout, 22, "Acess Shell Version 3\n");\r
67         write(_stdout,  2, "\n");\r
68         for(;;)\r
69         {\r
70                 // Free last command & arguments\r
71                 if(saArgs[0])   free(saArgs);\r
72                 if(!bCached)    free(sCommandStr);\r
73                 bCached = 0;\r
74                 write(_stdout, strlen(gsCurrentDirectory), gsCurrentDirectory);\r
75                 write(_stdout, 3, "$ ");\r
76                 \r
77                 // Read Command line\r
78                 sCommandStr = ReadCommandLine( &length );\r
79                 \r
80                 if(!sCommandStr) {\r
81                         write(_stdout, 25, "PANIC: Out of heap space\n");\r
82                         return -1;\r
83                 }\r
84                 \r
85                 // Check if the command should be cached\r
86                 if(gasCommandHistory == NULL || strcmp(sCommandStr, gasCommandHistory[giLastCommand]) != 0)\r
87                 {\r
88                         if(giLastCommand >= giCommandSpace) {\r
89                                 giCommandSpace += 12;\r
90                                 gasCommandHistory = realloc(gasCommandHistory, giCommandSpace*sizeof(char*));\r
91                         }\r
92                         giLastCommand ++;\r
93                         gasCommandHistory[ giLastCommand ] = sCommandStr;\r
94                         bCached = 1;\r
95                 }\r
96                 \r
97                 // Parse Command Line into arguments\r
98                 Parse_Args(sCommandStr, saArgs);\r
99                 \r
100                 // Count Arguments\r
101                 iArgCount = 0;\r
102                 while(saArgs[iArgCount])        iArgCount++;\r
103                 \r
104                 // Silently Ignore all empty commands\r
105                 if(saArgs[1][0] == '\0')        continue;\r
106                 \r
107                 // Check Built-In Commands\r
108                 //  [HACK] Mem Usage - Use Length in place of `i'\r
109                 for(length=0;length<BUILTIN_COUNT;length++)\r
110                 {\r
111                         if(strcmp(saArgs[1], cBUILTINS[length].name) == 0)\r
112                         {\r
113                                 cBUILTINS[length].fcn(iArgCount-1, &saArgs[1]);\r
114                                 break;\r
115                         }\r
116                 }\r
117                 \r
118                 if(length != BUILTIN_COUNT)     continue;\r
119                 \r
120                 // Shall we?\r
121                 CallCommand( &saArgs[1] );\r
122         }\r
123 }\r
124 \r
125 /**\r
126  * \fn char *ReadCommandLine(int *Length)\r
127  * \brief Read from the command line\r
128  */\r
129 char *ReadCommandLine(int *Length)\r
130 {\r
131         char    *ret;\r
132          int    len, pos, space = 1023;\r
133         char    ch;\r
134          \r
135         // Preset Variables\r
136         ret = malloc( space+1 );\r
137         if(!ret)        return NULL;\r
138         len = 0;\r
139         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         \r
250         \r
251         // - Calling a file\r
252         GeneratePath(Args[0], gsCurrentDirectory, gsTmpBuffer);\r
253         \r
254         \r
255         // Check file existence\r
256         fd = open(gsTmpBuffer, 0);\r
257         if(fd == -1) {\r
258                 Print("Unknown Command: `");Print(Args[0]);Print("'\n");        // Error Message\r
259                 return ;\r
260         }\r
261         \r
262         // Check if the file is a directory\r
263         finfo( fd, &info, 0 );\r
264         close( fd );\r
265         if(info.flags & FILEFLAG_DIRECTORY) {\r
266                 Print("`");Print(gsTmpBuffer);  // Error Message\r
267                 Print("' is a directory.\n");\r
268                 return ;\r
269         }\r
270         \r
271         // Create new process\r
272         pid = clone(CLONE_VM, 0);\r
273         // Start Task\r
274         if(pid == 0)\r
275                 execve(gsTmpBuffer, Args, gasEnvironment);\r
276         if(pid <= 0) {\r
277                 Print("Unablt to create process: `");Print(gsTmpBuffer);Print("'\n");   // Error Message\r
278         }\r
279         else {\r
280                  int    status;\r
281                 waittid(pid, &status);\r
282         }\r
283 }\r
284 \r
285 /**\r
286  * \fn void Command_Colour(int argc, char **argv)\r
287  * \brief \r
288  */\r
289 void Command_Colour(int argc, char **argv)\r
290 {\r
291         int fg, bg;\r
292         char    clrStr[6] = "\x1B[37m";\r
293         \r
294         // Verify Arg Count\r
295         if(argc < 2)\r
296         {\r
297                 Print("Please specify a colour\n");\r
298                 goto usage;\r
299         }\r
300         \r
301         // Check Colour\r
302         for(fg=0;fg<8;fg++)\r
303                 if(strcmp(cCOLOUR_NAMES[fg], argv[1]) == 0)\r
304                         break;\r
305 \r
306         // Foreground a valid colour\r
307         if(fg == 8) {\r
308                 Print("Unknown Colour '");Print(argv[1]);Print("'\n");\r
309                 goto usage;\r
310         }\r
311         // Set Foreground\r
312         clrStr[3] = '0' + fg;\r
313         write(_stdout, 6, clrStr);\r
314         \r
315         // Need to Set Background?\r
316         if(argc > 2)\r
317         {\r
318                 for(bg=0;bg<8;bg++)\r
319                         if(strcmp(cCOLOUR_NAMES[bg], argv[2]) == 0)\r
320                                 break;\r
321         \r
322                 // Valid colour\r
323                 if(bg == 8)\r
324                 {\r
325                         Print("Unknown Colour '");Print(argv[2]);Print("'\n");\r
326                         goto usage;\r
327                 }\r
328         \r
329                 clrStr[2] = '4';\r
330                 clrStr[3] = '0' + bg;\r
331                 write(_stdout, 6, clrStr);\r
332         }\r
333         // Return\r
334         return;\r
335 \r
336         // Function Usage (Requested via a Goto (I know it's ugly))\r
337 usage:\r
338         Print("Valid Colours are ");\r
339         for(fg=0;fg<8;fg++)\r
340         {\r
341                 Print(cCOLOUR_NAMES[fg]);\r
342                 write(_stdout, 3, ", ");\r
343         }\r
344         write(_stdout, 4, "\b\b\n");\r
345         return;\r
346 }\r
347 \r
348 void Command_Clear(int argc, char **argv)\r
349 {\r
350         write(_stdout, 4, "\x1B[2J");   //Clear Screen\r
351 }\r
352 \r
353 /**\r
354  * \fn void Command_Cd(int argc, char **argv)\r
355  * \brief Change directory\r
356  */\r
357 void Command_Cd(int argc, char **argv)\r
358 {\r
359         char    tmpPath[1024];\r
360         int             fp;\r
361         t_sysFInfo      stats;\r
362         \r
363         if(argc < 2)\r
364         {\r
365                 Print(gsCurrentDirectory);Print("\n");\r
366                 return;\r
367         }\r
368         \r
369         GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
370         \r
371         fp = open(tmpPath, 0);\r
372         if(fp == -1) {\r
373                 write(_stdout, 26, "Directory does not exist\n");\r
374                 return;\r
375         }\r
376         finfo(fp, &stats, 0);\r
377         close(fp);\r
378         \r
379         if( !(stats.flags & FILEFLAG_DIRECTORY) ) {\r
380                 write(_stdout, 17, "Not a Directory\n");\r
381                 return;\r
382         }\r
383         \r
384         free(gsCurrentDirectory);\r
385         gsCurrentDirectory = malloc(strlen(tmpPath)+1);\r
386         strcpy(gsCurrentDirectory, tmpPath);\r
387         \r
388         // Register change with kernel\r
389         chdir( gsCurrentDirectory );\r
390 }\r
391 \r
392 /**\r
393  */\r
394 void Command_Dir(int argc, char **argv)\r
395 {\r
396          int    dp, fp, dirLen;\r
397         char    modeStr[11] = "RWXrwxRWX ";\r
398         char    tmpPath[1024];\r
399         char    *fileName;\r
400         t_sysFInfo      info;\r
401         t_sysACL        acl;\r
402         \r
403         // Generate Directory Path\r
404         if(argc > 1)\r
405                 dirLen = GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
406         else\r
407         {\r
408                 strcpy(tmpPath, gsCurrentDirectory);\r
409         }\r
410         dirLen = strlen(tmpPath);\r
411         \r
412         // Open Directory\r
413         dp = open(tmpPath, OPENFLAG_READ);\r
414         // Check if file opened\r
415         if(dp == -1)\r
416         {\r
417                 //printf("Unable to open directory `%s', File cannot be found\n", tmpPath);\r
418                 write(_stdout, 27, "Unable to open directory `");\r
419                 write(_stdout, strlen(tmpPath)+1, tmpPath);\r
420                 write(_stdout, 25, "', File cannot be found\n");\r
421                 return;\r
422         }\r
423         // Get File Stats\r
424         if( finfo(dp, &info, 0) == -1 )\r
425         {\r
426                 close(dp);\r
427                 write(_stdout, 34, "stat Failed, Bad File Descriptor\n");\r
428                 return;\r
429         }\r
430         // Check if it's a directory\r
431         if(!(info.flags & FILEFLAG_DIRECTORY))\r
432         {\r
433                 close(dp);\r
434                 write(_stdout, 27, "Unable to open directory `");\r
435                 write(_stdout, strlen(tmpPath)+1, tmpPath);\r
436                 write(_stdout, 20, "', Not a directory\n");\r
437                 return;\r
438         }\r
439         \r
440         // Append Shash for file paths\r
441         if(tmpPath[dirLen-1] != '/')\r
442         {\r
443                 tmpPath[dirLen++] = '/';\r
444                 tmpPath[dirLen] = '\0';\r
445         }\r
446         \r
447         fileName = (char*)(tmpPath+dirLen);\r
448         // Read Directory Content\r
449         while( (fp = readdir(dp, fileName)) )\r
450         {\r
451                 if(fp < 0)\r
452                 {\r
453                         if(fp == -3)\r
454                                 write(_stdout, 42, "Invalid Permissions to traverse directory\n");\r
455                         break;\r
456                 }\r
457                 // Open File\r
458                 fp = open(tmpPath, 0);\r
459                 if(fp == -1)    continue;\r
460                 // Get File Stats\r
461                 finfo(fp, &info, 0);\r
462                 \r
463                 if(info.flags & FILEFLAG_DIRECTORY)\r
464                         write(_stdout, 1, "d");\r
465                 else\r
466                         write(_stdout, 1, "-");\r
467                 \r
468                 // Print Mode\r
469                 acl.group = 0;  acl.id = info.uid;\r
470                 _SysGetACL(fp, &acl);\r
471                 if(acl.perms & 1)       modeStr[0] = 'r';       else    modeStr[0] = '-';\r
472                 if(acl.perms & 2)       modeStr[1] = 'w';       else    modeStr[1] = '-';\r
473                 if(acl.perms & 8)       modeStr[2] = 'x';       else    modeStr[2] = '-';\r
474                 acl.group = 1;  acl.id = info.gid;\r
475                 _SysGetACL(fp, &acl);\r
476                 if(acl.perms & 1)       modeStr[3] = 'r';       else    modeStr[3] = '-';\r
477                 if(acl.perms & 1)       modeStr[4] = 'w';       else    modeStr[4] = '-';\r
478                 if(acl.perms & 1)       modeStr[5] = 'x';       else    modeStr[5] = '-';\r
479                 acl.group = 1;  acl.id = -1;\r
480                 _SysGetACL(fp, &acl);\r
481                 if(acl.perms & 1)       modeStr[6] = 'r';       else    modeStr[6] = '-';\r
482                 if(acl.perms & 1)       modeStr[7] = 'w';       else    modeStr[7] = '-';\r
483                 if(acl.perms & 1)       modeStr[8] = 'x';       else    modeStr[8] = '-';\r
484                 write(_stdout, 10, modeStr);\r
485                 close(fp);\r
486                 \r
487                 // Colour Code\r
488                 if(info.flags & FILEFLAG_DIRECTORY)     // Directory: Green\r
489                         write(_stdout, 6, "\x1B[32m");\r
490                 // Default: White\r
491                 \r
492                 // Print Name\r
493                 write(_stdout, strlen(fileName), fileName);\r
494                 \r
495                 // Print slash if applicable\r
496                 if(info.flags & FILEFLAG_DIRECTORY)\r
497                         write(_stdout, 1, "/");\r
498                 \r
499                 // Revert Colour\r
500                 write(_stdout, 6, "\x1B[37m");\r
501                 \r
502                 // Newline!\r
503                 write(_stdout, 1, "\n");\r
504         }\r
505         // Close Directory\r
506         close(dp);\r
507 }\r

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