CLIShell - PTY signal support (and kernel fixes)
[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 #include <readline.h>\r
11 #include <errno.h>\r
12 #include <acess/devices/pty.h>\r
13 \r
14 #define _stdin  0\r
15 #define _stdout 1\r
16 #define _stderr 2\r
17 \r
18 // ==== PROTOTYPES ====\r
19  int    Parse_Args(const char *str, char **dest);\r
20 void    CallCommand(char **Args);\r
21 void    Command_Logout(int argc, char **argv);\r
22 void    Command_Clear(int argc, char **argv);\r
23 void    Command_Help(int argc, char **argv);\r
24 void    Command_Cd(int argc, char **argv);\r
25 void    Command_Dir(int argc, char **argv);\r
26 \r
27 // ==== CONSTANT GLOBALS ====\r
28 struct  {\r
29         char    *name;\r
30         void    (*fcn)(int argc, char **argv);\r
31 }       cBUILTINS[] = {\r
32         {"exit", Command_Logout},       {"logout", Command_Logout},\r
33         {"help", Command_Help}, {"clear", Command_Clear},\r
34         {"cd", Command_Cd}, {"dir", Command_Dir}\r
35 };\r
36 static char     *cDEFAULT_PATH[] = {"/Acess/Bin","/Acess/SBin"};\r
37 #define BUILTIN_COUNT   (sizeof(cBUILTINS)/sizeof(cBUILTINS[0]))\r
38 \r
39 // ==== LOCAL VARIABLES ====\r
40  int    giNumPathDirs = 2;\r
41 char    **gasPathDirs = cDEFAULT_PATH;\r
42 char    **gasEnvironment;\r
43 char    gsCommandBuffer[1024];\r
44 char    *gsCurrentDirectory = NULL;\r
45 char    **gasCommandHistory;\r
46  int    giLastCommand = 0;\r
47  int    giCommandSpace = 0;\r
48 \r
49 // ==== CODE ====\r
50 int main(int argc, char *argv[], char **envp)\r
51 {\r
52         char    *sCommandStr;\r
53         char    *saArgs[32] = {0};\r
54          int    i;\r
55          int    iArgCount = 0;\r
56         tReadline       *readline_state = Readline_Init(1);\r
57         \r
58         gasEnvironment = envp;\r
59         \r
60 //      Command_Clear(0, NULL);\r
61         \r
62         {\r
63                 char    *tmp = getenv("CWD");\r
64                 if(tmp) {\r
65                         gsCurrentDirectory = malloc(strlen(tmp)+1);\r
66                         strcpy(gsCurrentDirectory, tmp);\r
67                 } else {\r
68                         gsCurrentDirectory = malloc(2);\r
69                         strcpy(gsCurrentDirectory, "/");\r
70                 }\r
71         }       \r
72         \r
73         printf("Acess Shell Version 3\n\n");\r
74         for(;;)\r
75         {\r
76                 // Free last command & arguments\r
77                 if(saArgs[0])   free(saArgs[0]);\r
78                 \r
79                 printf("%s$ ", gsCurrentDirectory);\r
80                 fflush(stdout);\r
81                 \r
82                 // Read Command line\r
83                 sCommandStr = Readline( readline_state );\r
84                 printf("\n");\r
85                 if( !sCommandStr ) {\r
86                         perror("Readline");\r
87                         return 1;\r
88                 }\r
89                 \r
90                 // Parse Command Line into arguments\r
91                 iArgCount = Parse_Args(sCommandStr, saArgs);\r
92                 \r
93                 // Silently Ignore all empty commands\r
94                 if(saArgs[1][0] == '\0')        continue;\r
95                 \r
96                 // Check Built-In Commands\r
97                 for( i = 0; i < BUILTIN_COUNT; i++ )\r
98                 {\r
99                         if( strcmp(saArgs[1], cBUILTINS[i].name) == 0 )\r
100                         {\r
101                                 cBUILTINS[i].fcn(iArgCount-1, &saArgs[1]);\r
102                                 break;\r
103                         }\r
104                 }\r
105                 if(i != BUILTIN_COUNT)  continue;\r
106                 \r
107                 // Shall we?\r
108                 CallCommand( &saArgs[1] );\r
109                 \r
110                 free( sCommandStr );\r
111         }\r
112 }\r
113 \r
114 /**\r
115  * \fn int Parse_Args(const char *str, char **dest)\r
116  * \brief Parse a string into an argument array\r
117  */\r
118 int Parse_Args(const char *str, char **dest)\r
119 {\r
120          int    i = 1;\r
121         char    *buf = malloc( strlen(str) + 1 );\r
122         \r
123         if(buf == NULL) {\r
124                 dest[0] = NULL;\r
125                 printf("Parse_Args: Out of heap space!\n");\r
126                 return 0;\r
127         }\r
128         \r
129         strcpy(buf, str);\r
130         dest[0] = buf;\r
131         \r
132         // Trim leading whitespace\r
133         while(*buf == ' ' && *buf)      buf++;\r
134         \r
135         for(;;)\r
136         {\r
137                 dest[i] = buf;  // Save start of string\r
138                 i++;\r
139                 while(*buf && *buf != ' ')\r
140                 {\r
141                         if(*buf++ == '"')\r
142                         {\r
143                                 while(*buf && !(*buf == '"' && buf[-1] != '\\'))\r
144                                         buf++;\r
145                         }\r
146                 }\r
147                 if(*buf == '\0')        break;\r
148                 *buf = '\0';\r
149                 while(*++buf == ' ' && *buf);\r
150                 if(*buf == '\0')        break;\r
151         }\r
152         dest[i] = NULL;\r
153         \r
154         return i;\r
155 }\r
156 \r
157 /**\r
158  * \fn void CallCommand(char **Args)\r
159  */\r
160 void CallCommand(char **Args)\r
161 {\r
162         t_sysFInfo      info;\r
163          int    pid = -1;\r
164          int    fd = 0;\r
165         char    sTmpBuffer[1024];\r
166         char    *exefile = Args[0];\r
167         \r
168         if(exefile[0] == '/'\r
169         || (exefile[0] == '.' && exefile[1] == '/')\r
170         || (exefile[0] == '.' && exefile[1] == '.' && exefile[2] == '/')\r
171                 )\r
172         {\r
173                 GeneratePath(exefile, gsCurrentDirectory, sTmpBuffer);\r
174                 // Check file existence\r
175                 fd = _SysOpen(sTmpBuffer, OPENFLAG_EXEC);\r
176                 if(fd == -1) {\r
177                         printf("Unknown Command: `%s'\n", Args[0]);     // Error Message\r
178                         return ;\r
179                 }\r
180                 \r
181                 // Get File info and close file\r
182                 _SysFInfo( fd, &info, 0 );\r
183                 _SysClose( fd );\r
184                 \r
185                 // Check if the file is a directory\r
186                 if(info.flags & FILEFLAG_DIRECTORY) {\r
187                         printf("`%s' is a directory.\n", sTmpBuffer);\r
188                         return ;\r
189                 }\r
190         }\r
191         else\r
192         {\r
193                  int    i;\r
194                 \r
195                 // Check all components of $PATH\r
196                 for( i = 0; i < giNumPathDirs; i++ )\r
197                 {\r
198                         GeneratePath(exefile, gasPathDirs[i], sTmpBuffer);\r
199                         fd = _SysOpen(sTmpBuffer, OPENFLAG_EXEC);\r
200                         if(fd == -1)    continue;\r
201                         _SysFInfo( fd, &info, 0 );\r
202                         _SysClose( fd );\r
203                         if(info.flags & FILEFLAG_DIRECTORY)     continue;\r
204                         // Woohoo! We found a valid command\r
205                         break;\r
206                 }\r
207                 \r
208                 // Exhausted path directories\r
209                 if( i == giNumPathDirs ) {\r
210                         printf("Unknown Command: `%s'\n", exefile);\r
211                         return ;\r
212                 }\r
213         }\r
214         \r
215         // Create new process\r
216         int fds[] = {0, 1, 2};\r
217          int    status;\r
218         pid = _SysSpawn(sTmpBuffer, (const char **)Args, (const char **)gasEnvironment, 3, fds, NULL);\r
219         if(pid <= 0) {\r
220                 printf("Unable to create process: `%s'\n", sTmpBuffer); // Error Message\r
221                 status = 0;\r
222         }\r
223         else {\r
224                 _SysIOCtl(0, PTY_IOCTL_SETPGRP, &pid);\r
225                 _SysWaitTID(pid, &status);\r
226         }\r
227         \r
228         // Return terminal to a sane state\r
229         {\r
230                 int zero = 0;\r
231                 _SysIOCtl(0, PTY_IOCTL_SETPGRP, &zero);\r
232                 printf("\x1b[0m");\r
233         }\r
234         // Print a status message if termination was non-clean\r
235         if( status )\r
236         {\r
237                 printf("[%i] exited %i\n", pid, status);\r
238         }\r
239 }\r
240 \r
241 /**\r
242  * \fn void Command_Logout(int argc, char **argv)\r
243  * \brief Exit the shell, logging the user out\r
244  */\r
245 void Command_Logout(int argc, char **argv)\r
246 {\r
247         exit(0);\r
248 }\r
249 \r
250 /**\r
251  * \fn void Command_Colour(int argc, char **argv)\r
252  * \brief Displays the help screen\r
253  */\r
254 void Command_Help(int argc, char **argv)\r
255 {\r
256         printf( "Acess 2 Command Line Interface\n"\r
257                 " By John Hodge (thePowersGang / [TPG])\n"\r
258                 "\n"\r
259                 "Builtin Commands:\n"\r
260                 " logout: Return to the login prompt\n"\r
261                 " exit:   Same\n"\r
262                 " help:   Display this message\n"\r
263                 " clear:  Clear the screen\n"\r
264                 " cd:     Change the current directory\n"\r
265                 " dir:    Print the contents of the current directory\n");\r
266         return;\r
267 }\r
268 \r
269 /**\r
270  * \fn void Command_Clear(int argc, char **argv)\r
271  * \brief Clear the screen\r
272  */\r
273 void Command_Clear(int argc, char **argv)\r
274 {\r
275         _SysWrite(_stdout, "\x1B[2J", 4);       //Clear Screen\r
276 }\r
277 \r
278 /**\r
279  * \fn void Command_Cd(int argc, char **argv)\r
280  * \brief Change directory\r
281  */\r
282 void Command_Cd(int argc, char **argv)\r
283 {\r
284         char    tmpPath[1024];\r
285         int             fp;\r
286         t_sysFInfo      stats;\r
287         \r
288         if(argc < 2)\r
289         {\r
290                 printf("%s\n", gsCurrentDirectory);\r
291                 return;\r
292         }\r
293         \r
294         GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
295         \r
296         fp = _SysOpen(tmpPath, 0);\r
297         if(fp == -1) {\r
298                 printf("Directory does not exist\n");\r
299                 return;\r
300         }\r
301         _SysFInfo(fp, &stats, 0);\r
302         _SysClose(fp);\r
303         \r
304         if( !(stats.flags & FILEFLAG_DIRECTORY) ) {\r
305                 printf("Not a Directory\n");\r
306                 return;\r
307         }\r
308         \r
309         free(gsCurrentDirectory);\r
310         gsCurrentDirectory = malloc(strlen(tmpPath)+1);\r
311         strcpy(gsCurrentDirectory, tmpPath);\r
312         \r
313         // Register change with kernel\r
314         _SysChdir( gsCurrentDirectory );\r
315 }\r
316 \r
317 /**\r
318  * \fn void Command_Dir(int argc, char **argv)\r
319  * \brief Print the contents of a directory\r
320  */\r
321 void Command_Dir(int argc, char **argv)\r
322 {\r
323          int    dp, fp;\r
324         char    modeStr[11] = "RWXrwxRWX ";\r
325         char    fileName[256];\r
326         t_sysFInfo      info;\r
327         t_sysACL        acl;\r
328         \r
329 \r
330         // -- Generate and open directory --\r
331         // Generate Directory Path\r
332         char    tmpPath[1024];\r
333         if(argc > 1)\r
334                 GeneratePath(argv[1], gsCurrentDirectory, tmpPath);\r
335         else\r
336                 strcpy(tmpPath, gsCurrentDirectory);\r
337         // Open Directory\r
338         dp = _SysOpen(tmpPath, OPENFLAG_READ);\r
339         if(dp == -1) {\r
340                 printf("Unable to open directory `%s', File cannot be found\n", tmpPath);\r
341                 return;\r
342         }\r
343         // Get File Stats\r
344         if( _SysFInfo(dp, &info, 0) == -1 )\r
345         {\r
346                 _SysClose(dp);\r
347                 printf("stat Failed, Bad File Descriptor\n");\r
348                 return;\r
349         }\r
350         // Check if it's a directory\r
351         if(!(info.flags & FILEFLAG_DIRECTORY))\r
352         {\r
353                 _SysClose(dp);\r
354                 printf("Unable to open directory `%s', Not a directory\n", tmpPath);\r
355                 return;\r
356         }\r
357         \r
358         // -- Read Directory Contents --\r
359         while( (fp = _SysReadDir(dp, fileName)) )\r
360         {\r
361                 if(fp < 0)\r
362                 {\r
363                         if(fp == -3)\r
364                                 printf("Invalid Permissions to traverse directory\n");\r
365                         break;\r
366                 }\r
367                 // Open File\r
368                 fp = _SysOpenChild(dp, fileName, 0);\r
369                 if(fp == -1)    continue;\r
370                 // Get File Stats\r
371                 _SysFInfo(fp, &info, 0);\r
372                 \r
373                 if(info.flags & FILEFLAG_DIRECTORY)\r
374                         printf("d");\r
375                 else\r
376                         printf("-");\r
377                 \r
378                 // Print Mode\r
379                 // - Owner\r
380                 acl.object = info.uid;\r
381                 _SysGetACL(fp, &acl);\r
382                 if(acl.perms & 1)       modeStr[0] = 'r';       else    modeStr[0] = '-';\r
383                 if(acl.perms & 2)       modeStr[1] = 'w';       else    modeStr[1] = '-';\r
384                 if(acl.perms & 8)       modeStr[2] = 'x';       else    modeStr[2] = '-';\r
385                 // - Group\r
386                 acl.object = info.gid | 0x80000000;\r
387                 _SysGetACL(fp, &acl);\r
388                 if(acl.perms & 1)       modeStr[3] = 'r';       else    modeStr[3] = '-';\r
389                 if(acl.perms & 2)       modeStr[4] = 'w';       else    modeStr[4] = '-';\r
390                 if(acl.perms & 8)       modeStr[5] = 'x';       else    modeStr[5] = '-';\r
391                 // - World\r
392                 acl.object = 0xFFFFFFFF;\r
393                 _SysGetACL(fp, &acl);\r
394                 if(acl.perms & 1)       modeStr[6] = 'r';       else    modeStr[6] = '-';\r
395                 if(acl.perms & 2)       modeStr[7] = 'w';       else    modeStr[7] = '-';\r
396                 if(acl.perms & 8)       modeStr[8] = 'x';       else    modeStr[8] = '-';\r
397                 printf(modeStr);\r
398                 _SysClose(fp);\r
399                 \r
400                 // Colour Code\r
401                 if(info.flags & FILEFLAG_DIRECTORY)     // Directory: Green\r
402                         printf("\x1B[32m");\r
403                 // Default: White\r
404                 \r
405                 // Print Name\r
406                 printf("%s", fileName);\r
407                 \r
408                 // Print slash if applicable\r
409                 if(info.flags & FILEFLAG_DIRECTORY)\r
410                         printf("/");\r
411                 \r
412                 // Revert Colour\r
413                 printf("\x1B[37m");\r
414                 \r
415                 // Newline!\r
416                 printf("\n");\r
417         }\r
418         // Close Directory\r
419         _SysClose(dp);\r
420 }\r

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