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

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