Added ProcDev/SysFS + Cleanup
[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_Help(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 struct  {\r
26         char    *name;\r
27         void    (*fcn)(int argc, char **argv);\r
28 }       cBUILTINS[] = {\r
29         {"exit", Command_Logout},       {"logout", Command_Logout},\r
30         {"help", Command_Help}, {"clear", Command_Clear},\r
31         {"cd", Command_Cd}, {"dir", Command_Dir}\r
32 };\r
33 static char     *cDEFAULT_PATH[] = {"/Acess/Bin"};\r
34 #define BUILTIN_COUNT   (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))\r
35 \r
36 // ==== LOCAL VARIABLES ====\r
37  int    giNumPathDirs = 1;\r
38 char    **gasPathDirs = cDEFAULT_PATH;\r
39 char    **gasEnvironment;\r
40 char    gsCommandBuffer[1024];\r
41 char    *gsCurrentDirectory = NULL;\r
42 char    **gasCommandHistory;\r
43  int    giLastCommand = 0;\r
44  int    giCommandSpace = 0;\r
45 \r
46 // ==== CODE ====\r
47 int main(int argc, char *argv[], char *envp[])\r
48 {\r
49         char    *sCommandStr;\r
50         char    *saArgs[32] = {0};\r
51          int    length = 0;\r
52          int    i;\r
53          int    iArgCount = 0;\r
54          int    bCached = 1;\r
55         \r
56         gasEnvironment = envp;\r
57         \r
58         Command_Clear(0, NULL);\r
59         \r
60         {\r
61                 char    *tmp = getenv("CWD");\r
62                 if(tmp) {\r
63                         gsCurrentDirectory = malloc(strlen(tmp)+1);\r
64                         strcpy(gsCurrentDirectory, tmp);\r
65                 } else {\r
66                         gsCurrentDirectory = malloc(2);\r
67                         strcpy(gsCurrentDirectory, "/");\r
68                 }\r
69         }       \r
70         \r
71         write(_stdout, 22, "Acess Shell Version 3\n");\r
72         write(_stdout,  2, "\n");\r
73         for(;;)\r
74         {\r
75                 // Free last command & arguments\r
76                 if(saArgs[0])   free(saArgs);\r
77                 if(!bCached)    free(sCommandStr);\r
78                 bCached = 0;\r
79                 \r
80                 write(_stdout, strlen(gsCurrentDirectory), gsCurrentDirectory);\r
81                 write(_stdout, 2, "$ ");\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         #if 0\r
139          int    scrollbackPos = giLastCommand;\r
140         #endif\r
141          \r
142         // Preset Variables\r
143         ret = malloc( space+1 );\r
144         if(!ret)        return NULL;\r
145         len = 0;        pos = 0;\r
146                 \r
147         // Read In Command Line\r
148         do {\r
149                 read(_stdin, 1, &ch);   // Read Character from stdin (read is a blocking call)\r
150                 \r
151                 if(ch == '\n')  break;\r
152                 \r
153                 switch(ch)\r
154                 {\r
155                 // Control characters\r
156                 case '\x1B':\r
157                         read(_stdin, 1, &ch);   // Read control character\r
158                         switch(ch)\r
159                         {\r
160                         //case 'D':     if(pos) pos--;  break;\r
161                         //case 'C':     if(pos<len)     pos++;  break;\r
162                         case '[':\r
163                                 read(_stdin, 1, &ch);   // Read control character\r
164                                 switch(ch)\r
165                                 {\r
166                                 #if 0\r
167                                 case 'A':       // Up\r
168                                         {\r
169                                                  int    oldLen = len;\r
170                                                 if( scrollbackPos > 0 ) break;\r
171                                                 \r
172                                                 free(ret);\r
173                                                 ret = strdup( gasCommandHistory[--scrollbackPos] );\r
174                                                 \r
175                                                 len = strlen(ret);\r
176                                                 while(pos--)    write(_stdout, 3, "\x1B[D");\r
177                                                 write(_stdout, len, ret);       pos = len;\r
178                                                 while(pos++ < oldLen)   write(_stdout, 1, " ");\r
179                                         }\r
180                                         break;\r
181                                 case 'B':       // Down\r
182                                         {\r
183                                                  int    oldLen = len;\r
184                                                 if( scrollbackPos < giLastCommand-1 )   break;\r
185                                                 \r
186                                                 free(ret);\r
187                                                 ret = strdup( gasCommandHistory[++scrollbackPos] );\r
188                                                 \r
189                                                 len = strlen(ret);\r
190                                                 while(pos--)    write(_stdout, 3, "\x1B[D");\r
191                                                 write(_stdout, len, ret);       pos = len;\r
192                                                 while(pos++ < oldLen)   write(_stdout, 1, " ");\r
193                                         }\r
194                                         break;\r
195                                 #endif\r
196                                 case 'D':       // Left\r
197                                         if(pos == 0)    break;\r
198                                         pos --;\r
199                                         write(_stdout, 3, "\x1B[D");\r
200                                         break;\r
201                                 case 'C':       // Right\r
202                                         if(pos == len)  break;\r
203                                         pos++;\r
204                                         write(_stdout, 3, "\x1B[C");\r
205                                         break;\r
206                                 }\r
207                         }\r
208                         break;\r
209                 \r
210                 // Backspace\r
211                 case '\b':\r
212                         if(len <= 0)            break;  // Protect against underflows\r
213                         write(_stdout, 1, &ch);\r
214                         if(pos == len) {        // Simple case of end of string\r
215                                 len --;\r
216                                 pos--;\r
217                         }\r
218                         else {\r
219                                 char    buf[7] = "\x1B[000D";\r
220                                 buf[2] += ((len-pos+1)/100) % 10;\r
221                                 buf[3] += ((len-pos+1)/10) % 10;\r
222                                 buf[4] += (len-pos+1) % 10;\r
223                                 write(_stdout, len-pos, &ret[pos]);     // Move Text\r
224                                 ch = ' ';       write(_stdout, 1, &ch); ch = '\b';      // Clear deleted character\r
225                                 write(_stdout, 7, buf); // Update Cursor\r
226                                 // Alter Buffer\r
227                                 memmove(&ret[pos-1], &ret[pos], len-pos);\r
228                                 pos --;\r
229                                 len --;\r
230                         }\r
231                         break;\r
232                 \r
233                 // Tab\r
234                 case '\t':\r
235                         //TODO: Implement Tab-Completion\r
236                         //Currently just ignore tabs\r
237                         break;\r
238                 \r
239                 default:                \r
240                         // Expand Buffer\r
241                         if(len+1 > space) {\r
242                                 space += 256;\r
243                                 ret = realloc(ret, space+1);\r
244                                 if(!ret)        return NULL;\r
245                         }\r
246                         \r
247                         // Editing inside the buffer\r
248                         if(pos != len) {\r
249                                 char    buf[7] = "\x1B[000D";\r
250                                 buf[2] += ((len-pos)/100) % 10;\r
251                                 buf[3] += ((len-pos)/10) % 10;\r
252                                 buf[4] += (len-pos) % 10;\r
253                                 write(_stdout, 1, &ch); // Print new character\r
254                                 write(_stdout, len-pos, &ret[pos]);     // Move Text\r
255                                 write(_stdout, 7, buf); // Update Cursor\r
256                                 memmove( &ret[pos+1], &ret[pos], len-pos );\r
257                         }\r
258                         else {\r
259                                 write(_stdout, 1, &ch);\r
260                         }\r
261                         ret[pos++] = ch;\r
262                         len ++;\r
263                         break;\r
264                 }\r
265         } while(ch != '\n');\r
266         \r
267         // Cap String\r
268         ret[len] = '\0';\r
269         printf("\n");\r
270         \r
271         // Return length\r
272         if(Length)      *Length = len;\r
273         \r
274         return ret;\r
275 }\r
276 \r
277 /**\r
278  * \fn void Parse_Args(char *str, char **dest)\r
279  * \brief Parse a string into an argument array\r
280  */\r
281 void Parse_Args(char *str, char **dest)\r
282 {\r
283          int    i = 1;\r
284         char    *buf = malloc( strlen(str) + 1 );\r
285         \r
286         if(buf == NULL) {\r
287                 dest[0] = NULL;\r
288                 Print("Parse_Args: Out of heap space!\n");\r
289                 return ;\r
290         }\r
291         \r
292         strcpy(buf, str);\r
293         dest[0] = buf;\r
294         \r
295         // Trim leading whitespace\r
296         while(*buf == ' ' && *buf)      buf++;\r
297         \r
298         for(;;)\r
299         {\r
300                 dest[i] = buf;  // Save start of string\r
301                 i++;\r
302                 while(*buf && *buf != ' ')\r
303                 {\r
304                         if(*buf++ == '"')\r
305                         {\r
306                                 while(*buf && !(*buf == '"' && buf[-1] != '\\'))\r
307                                         buf++;\r
308                         }\r
309                 }\r
310                 if(*buf == '\0')        break;\r
311                 *buf = '\0';\r
312                 while(*++buf == ' ' && *buf);\r
313                 if(*buf == '\0')        break;\r
314         }\r
315         dest[i] = NULL;\r
316         if(i == 1) {\r
317                 free(buf);\r
318                 dest[0] = NULL;\r
319         }\r
320 }\r
321 \r
322 /**\r
323  * \fn void CallCommand(char **Args)\r
324  */\r
325 void CallCommand(char **Args)\r
326 {\r
327         t_sysFInfo      info;\r
328          int    pid = -1;\r
329          int    fd = 0;\r
330         char    sTmpBuffer[1024];\r
331         char    *exefile = Args[0];\r
332         \r
333         if(exefile[0] == '/'\r
334         || (exefile[0] == '.' && exefile[1] == '/')\r
335         || (exefile[0] == '.' && exefile[1] == '.' && exefile[2] == '/')\r
336                 )\r
337         {\r
338                 GeneratePath(exefile, gsCurrentDirectory, sTmpBuffer);\r
339                 // Check file existence\r
340                 fd = open(sTmpBuffer, OPENFLAG_EXEC);\r
341                 if(fd == -1) {\r
342                         Print("Unknown Command: `");Print(Args[0]);Print("'\n");        // Error Message\r
343                         return ;\r
344                 }\r
345                 \r
346                 // Get File info and close file\r
347                 finfo( fd, &info, 0 );\r
348                 close( fd );\r
349                 \r
350                 // Check if the file is a directory\r
351                 if(info.flags & FILEFLAG_DIRECTORY) {\r
352                         Print("`");Print(sTmpBuffer);   // Error Message\r
353                         Print("' is a directory.\n");\r
354                         return ;\r
355                 }\r
356         }\r
357         else\r
358         {\r
359                  int    i;\r
360                 \r
361                 // Check all components of $PATH\r
362                 for( i = 0; i < giNumPathDirs; i++ )\r
363                 {\r
364                         GeneratePath(exefile, gasPathDirs[i], sTmpBuffer);\r
365                         fd = open(sTmpBuffer, OPENFLAG_EXEC);\r
366                         if(fd == -1)    continue;\r
367                         finfo( fd, &info, 0 );\r
368                         close( fd );\r
369                         if(info.flags & FILEFLAG_DIRECTORY)     continue;\r
370                         // Woohoo! We found a valid command\r
371                         break;\r
372                 }\r
373                 \r
374                 // Exhausted path directories\r
375                 if( i == giNumPathDirs ) {\r
376                         Print("Unknown Command: `");Print(exefile);Print("'\n");\r
377                         return ;\r
378                 }\r
379         }\r
380         \r
381         // Create new process\r
382         pid = clone(CLONE_VM, 0);\r
383         // Start Task\r
384         if(pid == 0)\r
385                 execve(sTmpBuffer, Args, gasEnvironment);\r
386         if(pid <= 0) {\r
387                 Print("Unablt to create process: `");Print(sTmpBuffer);Print("'\n");    // Error Message\r
388         }\r
389         else {\r
390                  int    status;\r
391                 waittid(pid, &status);\r
392         }\r
393 }\r
394 \r
395 /**\r
396  * \fn void Command_Logout(int argc, char **argv)\r
397  * \brief Exit the shell, logging the user out\r
398  */\r
399 void Command_Logout(int argc, char **argv)\r
400 {\r
401         exit(0);\r
402 }\r
403 \r
404 /**\r
405  * \fn void Command_Colour(int argc, char **argv)\r
406  * \brief Displays the help screen\r
407  */\r
408 void Command_Help(int argc, char **argv)\r
409 {\r
410         Print("Acess 2 Command Line Interface\n");\r
411         Print(" By John Hodge (thePowersGang / [TPG])\n");\r
412         Print("\n");\r
413         Print("Builtin Commands:\n");\r
414         Print(" logout: Return to the login prompt\n");\r
415         Print(" exit:   Same\n");\r
416         Print(" help:   Display this message\n");\r
417         Print(" clear:  Clear the screen\n");\r
418         Print(" cd:     Change the current directory\n");\r
419         Print(" dir:    Print the contents of the current directory\n");\r
420         //Print("\n");\r
421         return;\r
422 }\r
423 \r
424 /**\r
425  * \fn void Command_Clear(int argc, char **argv)\r
426  * \brief Clear the screen\r
427  */\r
428 void Command_Clear(int argc, char **argv)\r
429 {\r
430         write(_stdout, 4, "\x1B[2J");   //Clear Screen\r
431 }\r
432 \r
433 /**\r
434  * \fn void Command_Cd(int argc, char **argv)\r
435  * \brief Change directory\r
436  */\r
437 void Command_Cd(int argc, char **argv)\r
438 {\r
439         char    tmpPath[1024];\r
440         int             fp;\r
441         t_sysFInfo      stats;\r
442         \r
443         if(argc < 2)\r
444         {\r
445                 Print(gsCurrentDirectory);Print("\n");\r
446                 return;\r
447         }\r
448         \r
449         GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
450         \r
451         fp = open(tmpPath, 0);\r
452         if(fp == -1) {\r
453                 write(_stdout, 26, "Directory does not exist\n");\r
454                 return;\r
455         }\r
456         finfo(fp, &stats, 0);\r
457         close(fp);\r
458         \r
459         if( !(stats.flags & FILEFLAG_DIRECTORY) ) {\r
460                 write(_stdout, 17, "Not a Directory\n");\r
461                 return;\r
462         }\r
463         \r
464         free(gsCurrentDirectory);\r
465         gsCurrentDirectory = malloc(strlen(tmpPath)+1);\r
466         strcpy(gsCurrentDirectory, tmpPath);\r
467         \r
468         // Register change with kernel\r
469         chdir( gsCurrentDirectory );\r
470 }\r
471 \r
472 /**\r
473  * \fn void Command_Dir(int argc, char **argv)\r
474  * \brief Print the contents of a directory\r
475  */\r
476 void Command_Dir(int argc, char **argv)\r
477 {\r
478          int    dp, fp, dirLen;\r
479         char    modeStr[11] = "RWXrwxRWX ";\r
480         char    tmpPath[1024];\r
481         char    *fileName;\r
482         t_sysFInfo      info;\r
483         t_sysACL        acl;\r
484         \r
485         // Generate Directory Path\r
486         if(argc > 1)\r
487                 dirLen = GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
488         else\r
489         {\r
490                 strcpy(tmpPath, gsCurrentDirectory);\r
491         }\r
492         dirLen = strlen(tmpPath);\r
493         \r
494         // Open Directory\r
495         dp = open(tmpPath, OPENFLAG_READ);\r
496         // Check if file opened\r
497         if(dp == -1)\r
498         {\r
499                 printf("Unable to open directory `%s', File cannot be found\n", tmpPath);\r
500                 return;\r
501         }\r
502         // Get File Stats\r
503         if( finfo(dp, &info, 0) == -1 )\r
504         {\r
505                 close(dp);\r
506                 write(_stdout, 34, "stat Failed, Bad File Descriptor\n");\r
507                 return;\r
508         }\r
509         // Check if it's a directory\r
510         if(!(info.flags & FILEFLAG_DIRECTORY))\r
511         {\r
512                 close(dp);\r
513                 write(_stdout, 27, "Unable to open directory `");\r
514                 write(_stdout, strlen(tmpPath)+1, tmpPath);\r
515                 write(_stdout, 20, "', Not a directory\n");\r
516                 return;\r
517         }\r
518         \r
519         // Append Shash for file paths\r
520         if(tmpPath[dirLen-1] != '/')\r
521         {\r
522                 tmpPath[dirLen++] = '/';\r
523                 tmpPath[dirLen] = '\0';\r
524         }\r
525         \r
526         fileName = (char*)(tmpPath+dirLen);\r
527         // Read Directory Content\r
528         while( (fp = readdir(dp, fileName)) )\r
529         {\r
530                 if(fp < 0)\r
531                 {\r
532                         if(fp == -3)\r
533                                 write(_stdout, 42, "Invalid Permissions to traverse directory\n");\r
534                         break;\r
535                 }\r
536                 // Open File\r
537                 fp = open(tmpPath, 0);\r
538                 if(fp == -1)    continue;\r
539                 // Get File Stats\r
540                 finfo(fp, &info, 0);\r
541                 \r
542                 if(info.flags & FILEFLAG_DIRECTORY)\r
543                         write(_stdout, 1, "d");\r
544                 else\r
545                         write(_stdout, 1, "-");\r
546                 \r
547                 // Print Mode\r
548                 // - Owner\r
549                 acl.group = 0;  acl.id = info.uid;\r
550                 _SysGetACL(fp, &acl);\r
551                 if(acl.perms & 1)       modeStr[0] = 'r';       else    modeStr[0] = '-';\r
552                 if(acl.perms & 2)       modeStr[1] = 'w';       else    modeStr[1] = '-';\r
553                 if(acl.perms & 8)       modeStr[2] = 'x';       else    modeStr[2] = '-';\r
554                 // - Group\r
555                 acl.group = 1;  acl.id = info.gid;\r
556                 _SysGetACL(fp, &acl);\r
557                 if(acl.perms & 1)       modeStr[3] = 'r';       else    modeStr[3] = '-';\r
558                 if(acl.perms & 2)       modeStr[4] = 'w';       else    modeStr[4] = '-';\r
559                 if(acl.perms & 8)       modeStr[5] = 'x';       else    modeStr[5] = '-';\r
560                 // - World\r
561                 acl.group = 1;  acl.id = -1;\r
562                 _SysGetACL(fp, &acl);\r
563                 if(acl.perms & 1)       modeStr[6] = 'r';       else    modeStr[6] = '-';\r
564                 if(acl.perms & 2)       modeStr[7] = 'w';       else    modeStr[7] = '-';\r
565                 if(acl.perms & 8)       modeStr[8] = 'x';       else    modeStr[8] = '-';\r
566                 write(_stdout, 10, modeStr);\r
567                 close(fp);\r
568                 \r
569                 // Colour Code\r
570                 if(info.flags & FILEFLAG_DIRECTORY)     // Directory: Green\r
571                         write(_stdout, 6, "\x1B[32m");\r
572                 // Default: White\r
573                 \r
574                 // Print Name\r
575                 write(_stdout, strlen(fileName), fileName);\r
576                 \r
577                 // Print slash if applicable\r
578                 if(info.flags & FILEFLAG_DIRECTORY)\r
579                         write(_stdout, 1, "/");\r
580                 \r
581                 // Revert Colour\r
582                 write(_stdout, 6, "\x1B[37m");\r
583                 \r
584                 // Newline!\r
585                 write(_stdout, 1, "\n");\r
586         }\r
587         // Close Directory\r
588         close(dp);\r
589 }\r

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