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

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