added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / workbench / c / shellcommands / Shell.c
blob39183e561f28c826613ded00f00fc0c06f874b4b
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id$
5 The shell program.
6 */
8 /******************************************************************************
10 NAME
12 Shell
14 SYNOPSIS
16 COMMAND/K/F,FROM
18 LOCATION
20 Workbench:C
22 FUNCTION
24 Start a shell (interactive or background).
26 INPUTS
28 COMMAND -- command line to execute
30 FROM -- script to invoke before user interaction
33 RESULT
35 NOTES
37 The script file is not a script in execute sense (as you may not use any
38 .key, .bra or .ket and similar things).
40 EXAMPLE
42 shell FROM S:Startup-Sequence
44 Starts a shell and executes the startup script.
46 BUGS
48 SEE ALSO
50 Execute, NewShell
52 INTERNALS
54 The prompt support is not using SetCurrentDirName() as this function
55 has improper limitations. More or less the same goes for GetProgramName().
57 ******************************************************************************/
59 /* TODO:
61 * Alias [] support
62 * Break support (and +(0L) before execution) -- CreateNewProc()?
63 * Script file execution capabilities (if script bit set)
64 * $ must be taken care of differently than it is now so that things
65 like cd SYS:Olle/$pelle works
69 #define DEBUG 0
70 #include <aros/debug.h>
72 #include <exec/memory.h>
73 #include <exec/libraries.h>
74 #include <proto/exec.h>
75 #include <dos/dos.h>
76 #include <dos/dosextens.h>
77 #include <dos/var.h>
78 #include <dos/filesystem.h>
79 #include <dos/bptr.h>
80 #include <dos/stdio.h>
81 #include <proto/dos.h>
82 #include <proto/alib.h>
83 #include <proto/utility.h>
84 #include <utility/tagitem.h>
85 #include <ctype.h>
86 #include <stdio.h>
87 #include <string.h>
88 #include <aros/asmcall.h>
89 #include <unistd.h>
90 #include <stdarg.h>
92 #include <aros/debug.h>
94 #define SH_GLOBAL_SYSBASE 1
95 #define SH_GLOBAL_DOSBASE 1
96 #include <aros/shcommands.h>
98 #define SET_HOMEDIR 1
100 #define min(a,b) ((a) < (b)) ? (a) : (b)
102 #define COMMANDSTR_LEN (256 + 2) /* Maximum length of a 'command' */
103 #define FILENAME_LEN 256 /* Maximum length of a redirection filename */
105 struct Redirection
107 BPTR newIn;
108 BPTR newOut;
109 BPTR oldIn;
110 BPTR oldOut;
112 BOOL haveCommand;
113 BOOL haveOutRD;
114 BOOL haveInRD;
115 BOOL haveAppRD;
117 STRPTR commandStr; /* The command to execute */
118 STRPTR outFileName; /* Redirection file for > or >> */
119 STRPTR inFileName; /* Redirection file for < */
123 struct CommandLine
125 STRPTR line;
126 LONG position;
127 LONG size;
131 struct ShellState
133 #if SET_HOMEDIR
134 BPTR oldHomeDir; /* shared lock on program file's parent directory */
135 BOOL homeDirChanged;
136 #endif
137 BOOL residentCommand; /* The last command executed was resident */
138 BOOL script; /* This command has the script bit set */
139 BPTR scriptLock;
143 struct CommandLineInterface *cli;
146 /* Prototypes */
148 /* Function: convertLine
150 * Action: Parses a command line and returns a filtered version (removing
151 * redirections, incorporating embedded commands, taking care of
152 * variable references and aliases.
154 * Input: struct CSource *filtered -- output command string
155 * struct CSource *cs -- input string
156 * struct Redirection *rd -- state
158 * Output: BOOL -- FALSE if there was some error, TRUE otherwise
160 BOOL convertLine(struct CSource *filtered, struct CSource *cs,
161 struct Redirection *rd);
164 /* Function: getCommand
166 * Action:
168 * Input: struct CSource *filtered -- output buffer
169 * struct CSource *cs -- input string
170 * struct Redirection *rd -- state
172 * Output: BOOL -- FALSE if there was some error, TRUE otherwise
174 BOOL getCommand(struct CSource *filtered, struct CSource *cs,
175 struct Redirection *rd);
179 /* Function: executeLine
181 * Action: Execute one line of commands
183 * Input: STRPTR command -- command
184 * STRPTR commandArgs -- arguments of the 'command'
185 * struct Redirection *rd -- state
187 * Output: LONG -- error code or 0 if everything went OK
189 LONG executeLine(STRPTR command, STRPTR commandArgs, struct Redirection *rd);
192 /* Function: readLine
194 * Action: Read one line of a stream into a buffer.
196 * Input: struct CommandLine *cl -- the result will be stored
197 * here
198 * BPTR inputStream -- stream to read the line from
200 * Note: This routine deals with buffering internally so "infinite" command
201 * lines are supported. You may specify NULL as the cl->line. The
202 * cl->line buffer may be disposed of by calling FreeVec().
204 * Output: BOOL -- FALSE if error, TRUE if everything went OK
206 BOOL readLine(struct CommandLine *cl, BPTR inputStream);
209 /* Function: checkLine
211 * Action: Parse a command line and do consistency checks
213 * Input: struct Redirection *rd -- state
214 * struct CommandLine *cl -- the command line
216 * Output: BOOL -- FALSE if error, TRUE if everything went OK
218 BOOL checkLine(struct Redirection *rd, struct CommandLine *cl);
221 /* Function: releaseFiles
223 * Action: Deallocate file resources used for redirecion and reinstall
224 * standard input and output streams.
226 * Input: struct Redirection *rd -- state
228 * Output: --
230 void releaseFiles(struct Redirection *rd);
233 /* Function: appendString
235 * Action: Add a string to the filtered command line
237 * Input: struct CSource *cs -- output stream (command line)
238 * STRPTR from -- string to append
239 * LONG size -- number of chars to copy
241 * Output: BOOL -- success/failure indicator
243 BOOL appendString(struct CSource *cs, STRPTR from, LONG size);
246 /* Function: printFlush
248 * Action: Do a formatted print that will instantly be displayed.
250 * Input: STRPTR fmt -- format string
251 * ... ... -- varagrs
253 * Output: BOOL -- success/failure indicator
255 #define printFlush(format...) do {Printf(format); Flush(Output());} while (0)
257 /* Function: interact
259 * Action: Execute a commandfile and then perform standard shell user
260 * interaction.
262 * Input: STRPTR script -- command file to execute before interacting
263 * (may be NULL)
265 * Output: LONG -- error code
267 LONG interact(void);
270 /* Function: loadCommand
272 * Action: Load a command, searching the paths, C: and the resident lists.
274 * Input: STRPTR commandName -- the command to load
275 * struct ShellState *ss -- state
277 * Output: BPTR -- segment of the loaded command or NULL if there was an
278 * error
280 BPTR loadCommand(STRPTR commandName, struct ShellState *ss);
283 /* Function: unloadCommand
285 * Action: Free the resources held by a (loaded) command.
287 * Input: BPTR commandSeg -- segment of the program to
288 * unload
289 * struct ShellState *ss -- state
291 * Output: --
293 void unloadCommand(BPTR commandSeg, struct ShellState *ss);
296 /* Function: Redirection_release
298 * Action: Release resources allocated in the state
300 * Input: struct Redirection *rd -- state
302 * Output: --
304 void Redirection_release(struct Redirection *rd);
307 /* Function: Redirection_init
309 * Action: Initialize a state structure
311 * Input: struct Redirection *rd -- state
313 * Output: BOOL -- success/failure indicator
315 BOOL Redirection_init(struct Redirection *rd);
318 /* Function: setPath
320 * Action: Set the current command (standard) path.
322 * Input: BPTR lock -- a lock on the directory
324 * Notes: This will set the current directory name via
325 * SetCurrentDirName() eventhough this is not used later.
327 * Output: --
329 static void setPath(BPTR lock);
332 /* Function: printPath
334 * Action: Print the current command path to Output().
336 * Input: --
338 * Notes: Used for Prompt purposes.
340 * Output: --
342 static void printPath(void);
345 /* Function: printPrompt
347 * Action: Print the prompt to indicate that user input is viable.
349 * Input: --
351 * Output: --
353 static void printPrompt(void);
356 /*****************************************************************************/
357 void setupResidentCommands(void);
358 #define PROCESS(x) ((struct Process *)(x))
360 AROS_SH1(Shell, 41.1,
361 AROS_SHA(STRPTR, ,COMMAND,/F,NULL))
363 AROS_SHCOMMAND_INIT
365 LONG error = RETURN_OK;
367 D(bug("Executing shell\n"));
369 UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 39);
370 if (!UtilityBase) return RETURN_FAIL;
372 setupResidentCommands();
374 cli = Cli();
375 setPath(NULL);
377 if (strcmp(FindTask(NULL)->tc_Node.ln_Name, "Boot Shell") == 0)
378 SetPrompt("%N> ");
380 if(SHArg(COMMAND) && SHArg(COMMAND)[0])
382 struct Redirection rd;
383 struct CommandLine cl = {SHArgLine(),
385 strlen(SHArg(COMMAND))};
387 if(Redirection_init(&rd))
389 D(bug("Running command %s\n", SHArg(COMMAND)));
390 error = checkLine(&rd, &cl);
391 Redirection_release(&rd);
394 D(bug("Command done\n"));
396 else
398 error = interact();
401 D(bug("Exiting shell\n"));
403 return error;
405 AROS_SHCOMMAND_EXIT
408 struct UtilityBase *UtilityBase;
410 void setupResidentCommands(void)
417 /* First we execute the script, then we interact with the user */
418 LONG interact(void)
420 ULONG cliNumber = PROCESS(FindTask(NULL))->pr_TaskNum;
421 LONG error = 0;
422 BOOL moreLeft = FALSE;
424 if (!cli->cli_Background)
426 SetVBuf(Output(), NULL, BUF_FULL, -1);
427 if (strcmp(FindTask(NULL)->tc_Node.ln_Name, "Boot Shell") == 0)
429 PutStr
431 "AROS - The Amiga® Research Operating System\n"
432 "Copyright © 1995-2003, The AROS Development Team. All rights reserved.\n"
433 "AROS is licensed under the terms of the AROS Public License (APL),\n"
434 "a copy of which you should have received with this distribution.\n"
435 "Visit http://www.aros.org/ for more information.\n"
438 else
440 IPTR data[] = {(IPTR)cliNumber};
442 VPrintf("New Shell process %ld\n", data);
444 SetVBuf(Output(), NULL, BUF_LINE, -1);
449 struct CommandLine cl = { NULL, 0, 0 };
450 struct Redirection rd;
452 if(Redirection_init(&rd))
454 if (cli->cli_Interactive)
455 printPrompt();
457 moreLeft = readLine(&cl, cli->cli_CurrentInput);
458 error = checkLine(&rd, &cl);
460 Redirection_release(&rd);
461 FreeVec(cl.line);
464 if (!moreLeft)
466 if (!cli->cli_Interactive)
468 Close(cli->cli_CurrentInput);
470 if (AROS_BSTR_strlen(cli->cli_CommandFile))
472 DeleteFile(AROS_BSTR_ADDR(cli->cli_CommandFile));
473 AROS_BSTR_setstrlen(cli->cli_CommandFile, 0);
476 if (!cli->cli_Background)
478 cli->cli_CurrentInput = cli->cli_StandardInput;
479 cli->cli_Interactive = TRUE;
480 moreLeft = TRUE;
481 Flush(Output());
482 Flush(Error());
486 } while(moreLeft);
488 if (cli->cli_Interactive) printFlush("Process %ld ending\n", cliNumber);
490 return error;
494 /* Close redirection files and install regular input and output streams */
495 void releaseFiles(struct Redirection *rd)
497 if (rd->newIn) Close(rd->newIn);
498 rd->newIn = NULL;
500 if (rd->newOut) Close(rd->newOut);
501 rd->newOut = NULL;
504 char avBuffer[256];
505 char varBuffer[256];
506 char argBuffer[256];
509 /* Take care of one command line */
510 BOOL checkLine(struct Redirection *rd, struct CommandLine *cl)
512 /* The allocation is taken care of by appendString */
513 struct CSource filtered = { NULL, 0, 0 };
514 struct CSource cs = { cl->line, strlen(cl->line), 0 };
515 struct LocalVar *lv;
516 BOOL result = FALSE;
518 lv = FindVar("echo", LV_VAR);
519 if (lv != NULL)
521 /* AmigaDOS' shell is content also with echo being set to anything
522 that begins with "on" in order to trigger commands echoing on,
523 it doesn't really have to be set to just "on". */
524 if (lv->lv_Len >= 2)
525 if (strncasecmp(lv->lv_Value, "on", 2) == 0)
527 /* Ok, commands echoing is on. */
528 PutStr(cl->line);
532 D(bug("Calling convertLine(), line = %s\n", cl->line));
534 if(convertLine(&filtered, &cs, rd))
536 D(bug("Position %i\n", filtered.CS_CurChr));
538 /* End string */
539 appendString(&filtered, "\n\0", 2);
541 /* Consistency checks */
542 if(rd->haveOutRD && rd->haveAppRD)
544 PutStr("Cannot combine > with >>\n");
545 goto exit;
548 /* Only a comment? */
549 if(!rd->haveCommand)
551 result = TRUE;
552 goto exit;
555 /* stegerg: Set redirection to default in/out handles */
557 if(rd->haveOutRD)
559 D(bug("Redirecting output to file %s\n", rd->outFileName));
561 rd->newOut = Open(rd->outFileName, MODE_NEWFILE);
563 if(BADDR(rd->newOut) == NULL)
565 PrintFault(IoErr(), rd->outFileName);
566 goto exit;
569 D(bug("Output stream opened\n"));
570 SelectOutput(rd->newOut);
573 if(rd->haveAppRD)
575 rd->newOut = Open(rd->outFileName, (FMF_MODE_OLDFILE | FMF_CREATE | FMF_APPEND) & ~FMF_AMIGADOS);
577 if(BADDR(rd->newOut) == NULL)
579 PrintFault(IoErr(), rd->outFileName);
580 goto exit;
583 D(bug("Output stream opened (append)\n"));
584 SelectOutput(rd->newOut);
587 if(rd->haveInRD)
589 rd->newIn = Open(rd->inFileName, MODE_OLDFILE/*FMF_READ*/);
591 if(BADDR(rd->newIn) == NULL)
593 PrintFault(IoErr(), rd->inFileName);
594 goto exit;
597 D(bug("Input stream opened\n"));
598 SelectInput(rd->newIn);
601 D(bug("Calling executeLine()\n"));
603 /* OK, we've got a command. Let's execute it! */
604 executeLine(rd->commandStr, filtered.CS_Buffer, rd);
606 SelectInput(cli->cli_StandardInput);
607 SelectOutput(cli->cli_StandardOutput);
608 result = TRUE;
610 else
612 PutStr("Erroneous command line.\n");
615 exit:
616 FreeVec(filtered.CS_Buffer);
618 if (cli->cli_Interactive)
620 Flush(Output());
621 Flush(Error());
624 return result;
627 /* The shell has the following semantics when it comes to command lines:
628 Redirection (<,>,>>) may be written anywhere (except before the command
629 itself); the following item (as defined by ReadItem() is the redirection
630 file. The first item of the command line is the command to be executed.
631 This may be an alias, that is there is a Local LV_ALIAS variable that
632 should be substituted for the command text. Aliasing only applies to
633 commands and not to options, for instance. Variables (set by SetEnv or Set)
634 may be referenced by prepending a '$' to the variable name. */
636 BOOL convertLine(struct CSource *filtered, struct CSource *cs,
637 struct Redirection *rd)
640 #define item cs->CS_Buffer[cs->CS_CurChr]
641 #define from cs->CS_Buffer
642 #define advance(x) cs->CS_CurChr += x;
644 LONG result;
646 while(TRUE)
648 D(bug("Str: %s\n", cs->CS_Buffer+cs->CS_CurChr));
650 while(item == ' ' || item == '\t')
652 STRPTR temp = " ";
654 temp[0] = item;
656 appendString(filtered, temp, 1);
657 advance(1);
660 /* Are we done yet? */
661 if(item == '\n' || item == ';' || item == '\0')
662 break;
664 if(item == '|')
666 BPTR pipefhs[2] = {0, 0};
667 int i;
668 struct TagItem tags[] =
670 { SYS_Input , (IPTR) NULL },
671 { SYS_Output , SYS_DupStream },
672 { SYS_Error , SYS_DupStream },
673 { SYS_Asynch , TRUE },
674 { NP_StackSize, Cli()->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT },
675 { TAG_DONE , 0 }
678 /* Prevent command lines like "Prompt> | Olle echo Oepir" */
679 if(!rd->haveCommand)
680 return FALSE;
682 item = 0;
684 /* There must be something after a pipe... */
687 i = cs->CS_CurChr + 1;
688 cs->CS_Buffer[i] == ' ' || cs->CS_Buffer[i] == '\t';
692 if(cs->CS_Buffer[i] == '\n' || cs->CS_Buffer[i] == ';' || cs->CS_Buffer[i] == '\0')
694 SetIoErr(ERROR_LINE_TOO_LONG); /* what kind of error must we report? */
695 return FALSE;
698 D(bug("commannd = %S\n", &item+1));
700 if(rd->haveOutRD)
702 if (SystemTagList(&item+1, tags) == -1)
703 return FALSE;
705 else
707 if (Pipe("PIPEFS:", &pipefhs[0], &pipefhs[1]) != DOSTRUE)
708 return FALSE;
710 tags[0].ti_Data = (IPTR)pipefhs[0];
712 if (SystemTagList(&item+1, tags) == -1)
714 Close(pipefhs[0]);
715 Close(pipefhs[1]);
717 return FALSE;
720 rd->oldOut = SelectOutput(pipefhs[1]);
721 rd->newOut = pipefhs[1];
724 else
725 if(item == '<')
727 /* Prevent command lines like "Prompt> <Olle type" */
728 if(!rd->haveCommand)
729 return FALSE;
731 /* Multiple redirections not allowed */
732 if(rd->haveInRD)
733 return FALSE;
735 advance(1);
736 result = ReadItem(rd->inFileName, FILENAME_LEN, cs);
738 D(bug("Found input redirection\n"));
740 if(result == ITEM_ERROR || result == ITEM_NOTHING)
741 return FALSE;
743 rd->haveInRD = TRUE;
745 else if(item == '>')
747 /* Prevent command lines like "Prompt> >>Olle echo Oepir" */
748 if(!rd->haveCommand)
749 return FALSE;
751 advance(1);
753 if(item == '>')
755 /* Multiple redirections not allowed */
756 if(rd->haveAppRD)
757 return FALSE;
759 advance(1);
760 result = ReadItem(rd->outFileName, FILENAME_LEN, cs);
762 D(bug("Found append redirection\n"));
764 if(result == ITEM_ERROR || result == ITEM_NOTHING)
765 return FALSE;
767 rd->haveAppRD = TRUE;
769 else
771 /* Multiple redirections not allowed */
772 if(rd->haveOutRD)
773 return FALSE;
775 result = ReadItem(rd->outFileName, FILENAME_LEN, cs);
777 D(bug("Found output redirection\n"));
779 if(result == ITEM_ERROR || result == ITEM_NOTHING)
780 return FALSE;
782 rd->haveOutRD = TRUE;
785 else if(item == '$')
786 /* Possible environment variable usage */
788 LONG size = cs->CS_CurChr;
790 advance(1);
791 result = ReadItem(avBuffer, sizeof(avBuffer), cs);
793 D(bug("Found variable\n"));
795 if(result == ITEM_ERROR || ITEM_NOTHING)
796 return FALSE;
800 (GetVar(avBuffer, varBuffer, sizeof(varBuffer),
801 GVF_GLOBAL_ONLY | LV_VAR) != -1) &&
802 !(varBuffer[0] == '$' && !strcmp(varBuffer+1, avBuffer))
805 struct CSource varCs = { varBuffer, sizeof(varBuffer), 0 };
807 D(bug("Real variable! Value = %s\n", varBuffer));
809 if(convertLine(filtered, &varCs, rd) == FALSE)
810 return FALSE;
812 else
813 /* If this "variable" wasn't defined, we use the '$' as a
814 regular character */
816 D(bug("No real variable\n"));
818 if(!rd->haveCommand)
820 cs->CS_CurChr = size;
821 getCommand(filtered, cs, rd);
823 else
824 appendString(filtered, cs->CS_Buffer + size,
825 cs->CS_CurChr - size);
828 else
831 /* This is a regular character -- that is, we have a command */
832 if(!rd->haveCommand)
834 D(bug("Found possible command\n"));
836 getCommand(filtered, cs, rd);
838 else
840 /* Copy argument */
841 LONG size = cs->CS_CurChr;
843 // P(kprintf("Checking argument\n"));
845 result = ReadItem(argBuffer, sizeof(argBuffer), cs);
847 // D(bug("Found possible argument\n"));
849 if(result == ITEM_ERROR || ITEM_NOTHING)
850 return FALSE;
852 appendString(filtered, from + size, cs->CS_CurChr - size);
854 D(bug("\n"));
856 D(bug("Found argument %s\n", argBuffer));
861 D(bug("Exiting convertLine()\n"));
863 return TRUE;
869 BOOL getCommand(struct CSource *filtered, struct CSource *cs,
870 struct Redirection *rd)
872 LONG result;
874 rd->haveCommand = TRUE;
876 D(bug("Command found!\n"));
878 result = ReadItem(rd->commandStr, COMMANDSTR_LEN, cs);
880 if(result == ITEM_ERROR || result == ITEM_NOTHING)
881 return FALSE;
883 /* Is this command an alias? */
884 if(GetVar(rd->commandStr, avBuffer, sizeof(avBuffer),
885 GVF_LOCAL_ONLY | LV_ALIAS) != -1)
887 struct CSource aliasCs = { avBuffer, sizeof(avBuffer), 0 };
889 result = ReadItem(rd->commandStr, COMMANDSTR_LEN, &aliasCs);
891 D(bug("Found alias! value = %s\n", avBuffer));
893 if(result == ITEM_ERROR || result == ITEM_NOTHING)
894 return FALSE;
896 /* We don't check if the alias was an alias as that might
897 lead to infinite loops (alias Copy Copy) */
899 /* Make a recursive call to take care of the rest of the
900 alias string */
901 return convertLine(filtered, &aliasCs, rd);
904 D(bug("Command = %s\n", rd->commandStr));
906 return TRUE;
909 #undef item
910 #undef from
911 #undef advance
913 #define __extendSize 512 /* How much to increase buffer if it's full */
916 BOOL readLine(struct CommandLine *cl, BPTR inputStream)
918 LONG letter;
920 while(TRUE)
922 letter = inputStream ? FGetC(inputStream) : EOF;
924 D(bug("Read character %c (%d)\n", letter, letter));
926 /* -2 to skip test for boundary for terminating '\n\0' */
927 if(cl->position > (cl->size - 2))
929 STRPTR newString = AllocVec(cl->size + __extendSize, MEMF_ANY);
931 D(bug("Allocated new buffer %p\n", newString));
933 if(cl->line != NULL)
934 CopyMem(cl->line, newString, cl->size);
936 cl->size += __extendSize;
937 FreeVec(cl->line);
938 cl->line = newString;
941 if(letter == '\n' || letter == EOF)
943 D(bug("Found end of line\n"));
944 break;
947 cl->line[cl->position++] = letter;
950 /* Terminate the line with a newline and a NULL terminator */
951 cl->line[cl->position++] = '\n';
952 cl->line[cl->position++] = '\0';
954 D(bug("commandline: %s\n", cl->line));
956 if(letter == EOF)
957 return FALSE;
959 return TRUE;
963 /* Currently, there is no error checking involved */
964 BOOL appendString(struct CSource *cs, STRPTR fromStr, LONG size)
966 /* +2 for additional null bytes, '\n', \0' */
967 while(cs->CS_CurChr + size + 2 > (cs->CS_Length - cs->CS_CurChr))
969 STRPTR newString = AllocVec(cs->CS_Length + __extendSize, MEMF_ANY);
971 CopyMem(cs->CS_Buffer, newString, cs->CS_Length);
972 cs->CS_Length += __extendSize;
973 FreeVec(cs->CS_Buffer);
974 cs->CS_Buffer = newString;
977 while(size > 0)
979 cs->CS_Buffer[cs->CS_CurChr++] = *fromStr++;
980 size--;
983 return TRUE;
986 void unloadCommand(BPTR commandSeg, struct ShellState *ss)
989 if (!cli->cli_Module) return;
991 if(ss->residentCommand)
993 struct Segment *residentSeg = (struct Segment *)BADDR(commandSeg);
995 Forbid();
997 /* Decrease usecount */
998 if(residentSeg->seg_UC > 0)
999 residentSeg->seg_UC--;
1001 Permit();
1003 ss->residentCommand = FALSE;
1005 else
1006 UnLoadSeg(commandSeg);
1008 #if SET_HOMEDIR
1009 if (ss->homeDirChanged)
1011 UnLock(SetProgramDir(ss->oldHomeDir));
1012 ss->homeDirChanged = FALSE;
1014 #endif
1018 BPTR loadCommand(STRPTR commandName, struct ShellState *ss)
1020 BPTR oldCurDir;
1021 BPTR commandSeg = NULL;
1022 BPTR *paths;
1023 struct Segment *residentSeg;
1024 BOOL absolutePath = strpbrk(commandName, "/:") != NULL;
1025 BPTR file;
1027 /* We check the resident lists only if we do not have an absolute path */
1028 if(!absolutePath)
1030 Forbid();
1032 /* Check regular list first... */
1033 residentSeg = FindSegment(commandName, NULL, FALSE);
1035 if(residentSeg == NULL)
1037 /* ... then the system list */
1038 residentSeg = FindSegment(commandName, NULL, TRUE);
1041 if(residentSeg != NULL)
1043 /* Can we use this command? */
1044 if(residentSeg->seg_UC == CMD_INTERNAL || residentSeg->seg_UC >= 0)
1046 if(residentSeg->seg_UC >= 0)
1047 residentSeg->seg_UC++;
1049 ss->residentCommand = TRUE;
1050 Permit();
1051 return MKBADDR(residentSeg);
1055 Permit();
1058 ss->residentCommand = FALSE;
1060 D(bug("Trying to load command1: %s\n", commandName));
1062 oldCurDir = CurrentDir(NULL);
1063 CurrentDir(oldCurDir);
1065 file = Open(commandName, MODE_OLDFILE);
1067 if (!file)
1071 absolutePath || /* If this was an absolute path, we don't check the paths set by
1072 'path' or the C: multiassign */
1073 IoErr() == ERROR_OBJECT_IN_USE /* The object might be exclusively locked */
1075 return NULL;
1077 /* Search the command in the path */
1081 paths = (BPTR *)BADDR(cli->cli_CommandDir);
1082 file == NULL && paths != NULL;
1083 paths = (BPTR *)BADDR(paths[0]) /* Go on with the next path */
1086 CurrentDir(paths[1]);
1087 file = Open(commandName, MODE_OLDFILE);
1090 /* The last resort -- the C: multiassign */
1091 if (!file)
1093 commandName-=2;
1094 file = Open(commandName, MODE_OLDFILE);
1098 if (file)
1100 commandSeg = LoadSeg(commandName);
1102 #if SET_HOMEDIR
1103 if (commandSeg)
1105 BPTR lock = ParentOfFH(file);
1107 if (lock)
1109 ss->oldHomeDir = SetProgramDir(lock);
1110 ss->homeDirChanged = TRUE;
1113 else
1114 #endif
1116 struct FileInfoBlock fib;
1117 if (Examine(file, &fib) && fib.fib_Protection & FIBF_SCRIPT)
1119 commandSeg = LoadSeg("C:Execute");
1120 if (commandSeg)
1122 ss->script = TRUE;
1123 ss->scriptLock = Lock(commandName, SHARED_LOCK);
1126 else
1127 SetIoErr(ERROR_FILE_NOT_OBJECT);
1130 Close(file);
1133 CurrentDir(oldCurDir);
1135 return commandSeg;
1139 /* Execute one command */
1140 LONG executeLine(STRPTR command, STRPTR commandArgs, struct Redirection *rd)
1142 BPTR module;
1143 LONG error = 0;
1144 struct ShellState ss = {FALSE};
1145 char cmd[4096];
1147 ss.script = FALSE;
1150 if ss->residentCommand isn't initialized as FALSE, it's value is rather
1151 random ( loadCommand doesn't change it ) so unloadCommand almost always
1152 thinks that last Command was resident, and doesn't do an UnloadSeg...
1155 D(bug("Trying to load command: %s\nArguments: %s\n", command,
1156 commandArgs));
1158 module = loadCommand(command, &ss);
1160 /* Set command name even if we couldn't load the command to be able to
1161 report errors correctly */
1162 SetProgramName(command);
1164 if(module != NULL)
1166 struct Task *me = FindTask(NULL);
1167 STRPTR oldtaskname = me->tc_Node.ln_Name;
1168 BOOL __debug_mem;
1169 LONG mem_before;
1171 BPTR seglist = ss.residentCommand ? ((struct Segment *)BADDR(module))->seg_Seg:module;
1173 STRPTR dst = cmd, src;
1174 LONG len = 0;
1176 if (ss.script)
1178 *dst++ = '"';
1179 NameFromLock(ss.scriptLock, dst, sizeof(cmd));
1180 while (*dst != '\0')
1182 ++dst;
1183 ++len;
1185 *dst++ = '"';
1186 *dst++ = ' ';
1187 UnLock(ss.scriptLock);
1188 len += 2;
1191 for (src = commandArgs; *src != '\0'; ++dst, ++src, ++len)
1192 *dst = *src;
1193 *dst = '\0';
1195 D(bug("Command loaded: len=%d, %s\n", len, cmd));
1197 SetIoErr(0); /* Clear error before we execute this command */
1198 SetSignal(0, SIGBREAKF_CTRL_C);
1200 cli->cli_Module = seglist;
1202 me->tc_Node.ln_Name = command;
1204 __debug_mem = FindVar("__debug_mem", LV_VAR) != NULL;
1206 if (__debug_mem)
1208 FreeVec(AllocVec(~0ul/2, MEMF_ANY)); /* Flush memory */
1210 mem_before = AvailMem(MEMF_ANY);
1211 Printf("Available total memory before command execution: %10ld\n", mem_before);
1214 cli->cli_ReturnCode = RunCommand(seglist, cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
1215 cmd, len);
1216 if (__debug_mem)
1218 LONG mem_after;
1219 FreeVec(AllocVec(~0ul/2, MEMF_ANY)); /* Flush memory */
1221 mem_after = AvailMem(MEMF_ANY);
1222 Printf("Available total memory after command execution: %10ld\n", mem_after);
1223 Printf("Memory difference (before - after): %10ld\n", mem_before - mem_after);
1226 me->tc_Node.ln_Name = oldtaskname;
1228 D(bug("Returned from command %s\n", command));
1229 unloadCommand(module, &ss);
1231 cli->cli_Result2 = IoErr();
1233 else
1235 /* Implicit cd? */
1236 if(!(rd->haveInRD || rd->haveOutRD || rd->haveAppRD) && (IoErr() == ERROR_OBJECT_WRONG_TYPE || IoErr() == ERROR_OBJECT_NOT_FOUND))
1238 BPTR lock = Lock(command, SHARED_LOCK);
1240 if(lock != NULL)
1242 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1244 if(fib != NULL)
1246 if(Examine(lock, fib))
1248 if(fib->fib_DirEntryType > 0)
1250 setPath(lock);
1251 lock = CurrentDir(lock);
1253 else
1254 SetIoErr(ERROR_OBJECT_WRONG_TYPE);
1257 FreeDosObject(DOS_FIB, fib);
1260 /* UnLock the old currentdir */
1261 UnLock(lock);
1265 if(IoErr())
1267 cli->cli_Result2 = IoErr();
1268 PrintFault(IoErr(), command);
1272 D(bug("Done with the command...\n"));
1274 return error;
1278 BOOL Redirection_init(struct Redirection *rd)
1280 bzero(rd, sizeof(struct Redirection));
1282 rd->commandStr = AllocVec(COMMANDSTR_LEN, MEMF_CLEAR);
1284 rd->outFileName = AllocVec(FILENAME_LEN, MEMF_CLEAR);
1285 rd->inFileName = AllocVec(FILENAME_LEN, MEMF_CLEAR);
1287 if(rd->commandStr == NULL || rd->outFileName == NULL ||
1288 rd->inFileName == NULL)
1290 Redirection_release(rd);
1291 return FALSE;
1294 /* Preset the first bytes to "C:" to handle C: multiassigns */
1295 *rd->commandStr++ = 'C';
1296 *rd->commandStr++ = ':';
1298 return TRUE;
1302 void Redirection_release(struct Redirection *rd)
1304 /* -2 as we set pointer 2 bytes ahead to be able to use C: as a multi-
1305 assign in a smooth way */
1306 FreeVec(rd->commandStr - 2);
1307 FreeVec(rd->outFileName);
1308 FreeVec(rd->inFileName);
1310 releaseFiles(rd);
1313 static void printPath(void)
1315 STRPTR buf;
1316 ULONG i;
1318 for(i = 256; ; i += 256)
1320 buf = AllocVec(i, MEMF_ANY);
1322 if(buf == NULL)
1323 break;
1325 if(GetCurrentDirName(buf, i) == DOSTRUE)
1327 FPuts(Output(), buf);
1328 FreeVec(buf);
1329 break;
1332 FreeVec(buf);
1334 if(IoErr() != ERROR_OBJECT_TOO_LARGE)
1335 break;
1340 static void setPath(BPTR lock)
1342 BPTR dir;
1343 STRPTR buf;
1344 ULONG i;
1346 if(lock == NULL)
1347 dir = CurrentDir(NULL);
1348 else
1349 dir = lock;
1351 for(i = 256; ; i += 256)
1353 buf = AllocVec(i, MEMF_ANY);
1355 if(buf == NULL)
1356 break;
1358 if(NameFromLock(dir, buf, i))
1360 SetCurrentDirName(buf);
1361 FreeVec(buf);
1362 break;
1365 FreeVec(buf);
1368 if(lock == NULL)
1369 CurrentDir(dir);
1372 static void printPrompt(void)
1374 BSTR prompt = Cli()->cli_Prompt;
1375 LONG length = AROS_BSTR_strlen(prompt);
1376 ULONG i;
1378 for(i = 0; i < length; i++)
1380 if(AROS_BSTR_getchar(prompt, i) == '%')
1382 i++;
1384 if(i == length)
1385 break;
1387 switch(AROS_BSTR_getchar(prompt, i))
1389 case 'N':
1390 case 'n':
1391 Printf("%ld", PROCESS(FindTask(NULL))->pr_TaskNum);
1392 break;
1393 case 'R':
1394 case 'r':
1395 Printf("%ld", Cli()->cli_ReturnCode);
1396 break;
1397 case 'S':
1398 case 's':
1399 printPath();
1400 break;
1401 default:
1402 FPutC(Output(), '%');
1403 FPutC(Output(), AROS_BSTR_getchar(prompt, i));
1404 break;
1407 else
1408 FPutC(Output(), AROS_BSTR_getchar(prompt, i));
1411 Flush(Output());