Updated PCI IDs to latest snapshot.
[tangerine.git] / workbench / c / shellcommands / Shell.c
blobed024e9fa8c6d179adf72ff0cdf1b75e504e034d
1 /*
2 Copyright (C) 1995-2009, 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
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 Feb 2008 - initial support for .key/bra/ket/dot/dollar/default.
42 EXAMPLE
44 Shell FROM S:Startup-Sequence
46 Starts a shell and executes the startup script.
48 BUGS
50 SEE ALSO
52 Execute, NewShell
54 INTERNALS
56 The prompt support is not using SetCurrentDirName() as this function
57 has improper limitations. More or less the same goes for GetProgramName().
59 ******************************************************************************/
61 /* TODO:
63 * Alias [] support
64 * Break support (and +(0L) before execution) -- CreateNewProc()?
65 * Script file execution capabilities (if script bit set)
66 * $ must be taken care of differently than it is now so that things
67 like cd SYS:Olle/$pelle works
71 #define DEBUG 0
72 #define DEBUG1 1
73 #include <aros/debug.h>
75 #include <exec/memory.h>
76 #include <exec/libraries.h>
77 #include <proto/exec.h>
78 #include <dos/dos.h>
79 #include <dos/dosextens.h>
80 #include <dos/var.h>
81 #include <dos/filesystem.h>
82 #include <dos/bptr.h>
83 #include <dos/stdio.h>
84 #include <proto/dos.h>
85 #include <proto/alib.h>
86 #include <proto/utility.h>
87 #include <utility/tagitem.h>
88 #include <ctype.h>
89 #include <stdio.h>
90 #include <string.h>
91 #include <aros/asmcall.h>
92 #include <unistd.h>
93 #include <stdarg.h>
95 #include <aros/debug.h>
97 #define SH_GLOBAL_SYSBASE 1
98 #define SH_GLOBAL_DOSBASE 1
99 #include <aros/shcommands.h>
101 #define SET_HOMEDIR 1
103 #define min(a,b) ((a) < (b)) ? (a) : (b)
105 #define COMMANDSTR_LEN (256 + 2) /* Maximum length of a 'command' */
106 #define FILENAME_LEN 256 /* Maximum length of a redirection filename */
108 struct Redirection
110 BPTR newIn;
111 BPTR newOut;
112 BPTR oldIn;
113 BPTR oldOut;
115 BOOL haveCommand;
116 BOOL haveOutRD;
117 BOOL haveInRD;
118 BOOL haveAppRD;
120 STRPTR commandStr; /* The command to execute */
121 STRPTR outFileName; /* Redirection file for > or >> */
122 STRPTR inFileName; /* Redirection file for < */
126 struct CommandLine
128 STRPTR line;
129 LONG position;
130 LONG size;
134 struct ShellState
136 #if SET_HOMEDIR
137 BPTR oldHomeDir; /* shared lock on program file's parent directory */
138 BOOL homeDirChanged;
139 #endif
140 BOOL residentCommand; /* The last command executed was resident */
141 BOOL script; /* This command has the script bit set */
142 BPTR scriptLock;
145 struct CommandLineInterface *cli;
147 #define MAXARGS 32
148 #define MAXARGLEN 32
150 /* Template options (copied *and mofified* from Dos_ReadArgs) */
151 #define REQUIRED 0x80 /* /A */
152 #define KEYWORD 0x40 /* /K */
153 #define MULTIPLE 0x20 /* /M */
154 #define NORMAL 0x00 /* No option */
155 #define SWITCH 0x01 /* /S, implies /K */
156 #define TOGGLE 0x02 /* /T, implies /K */
157 #define NUMERIC 0x04 /* /N */
158 #define REST 0x08 /* /F */
160 struct InterpreterState
162 BOOL isBootShell;
163 LONG cliNumber;
164 LONG argcount;
165 TEXT argname[MAXARGS][MAXARGLEN];
166 LONG argnamelen[MAXARGS];
167 IPTR arg[MAXARGS];
168 LONG arglen[MAXARGS];
169 IPTR argdef[MAXARGS];
170 LONG argdeflen[MAXARGS];
171 UBYTE argtype[MAXARGS];
173 TEXT bra, ket, dollar, dot;
175 struct RDArgs *rdargs;
177 struct InterpreterState *stack;
182 /* Prototypes */
184 /* Function: convertLine
186 * Action: Parses a command line and returns a filtered version (removing
187 * redirections, incorporating embedded commands, taking care of
188 * variable references and aliases.
190 * Input: struct CSource *filtered -- output command string
191 * struct CSource *cs -- input string
192 * struct Redirection *rd -- state
193 * struct InterpreterState *is -- interpreter state
195 * Output: LONG -- error code or 0 if everything went OK
197 LONG convertLine(struct CSource *filtered, struct CSource *cs,
198 struct Redirection *rd, struct InterpreterState *is);
201 /* Function: getCommand
203 * Action:
205 * Input: struct CSource *filtered -- output buffer
206 * struct CSource *cs -- input string
207 * struct Redirection *rd -- state
208 * struct InterpreterState *is -- interpreter state
210 * Output: BOOL -- FALSE if there was some error, TRUE otherwise
212 BOOL getCommand(struct CSource *filtered, struct CSource *cs,
213 struct Redirection *rd, struct InterpreterState *is);
217 /* Function: executeLine
219 * Action: Execute one line of commands
221 * Input: STRPTR command -- command
222 * STRPTR commandArgs -- arguments of the 'command'
223 * struct Redirection *rd -- state
224 * struct InterpreterState *is -- interpreter state
226 * Output: LONG -- error code or 0 if everything went OK
228 LONG executeLine(STRPTR command, STRPTR commandArgs, struct Redirection *rd,
229 struct InterpreterState *is);
232 /* Function: readLine
234 * Action: Read one line of a stream into a buffer.
236 * Input: struct CommandLine *cl -- the result will be stored
237 * here
238 * BPTR inputStream -- stream to read the line from
240 * Note: This routine deals with buffering internally so "infinite" command
241 * lines are supported. You may specify NULL as the cl->line. The
242 * cl->line buffer may be disposed of by calling FreeVec().
244 * Output: BOOL -- FALSE if error, TRUE if everything went OK
246 BOOL readLine(struct CommandLine *cl, BPTR inputStream);
249 /* Function: checkLine
251 * Action: Parse a command line and do consistency checks
253 * Input: struct Redirection *rd -- state
254 * struct CommandLine *cl -- the command line
255 * struct InterpreterState *is -- interpreter state
257 * Output: LONG -- DOS error code
259 LONG checkLine(struct Redirection *rd, struct CommandLine *cl,
260 struct InterpreterState *is);
263 /* Function: releaseFiles
265 * Action: Deallocate file resources used for redirecion and reinstall
266 * standard input and output streams.
268 * Input: struct Redirection *rd -- state
270 * Output: --
272 void releaseFiles(struct Redirection *rd);
275 /* Function: appendString
277 * Action: Add a string to the filtered command line
279 * Input: struct CSource *cs -- output stream (command line)
280 * STRPTR from -- string to append
281 * LONG size -- number of chars to copy
283 * Output: BOOL -- success/failure indicator
285 BOOL appendString(struct CSource *cs, CONST_STRPTR from, LONG size);
288 /* Function: printFlush
290 * Action: Do a formatted print that will instantly be displayed.
292 * Input: STRPTR fmt -- format string
293 * ... ... -- varagrs
295 * Output: BOOL -- success/failure indicator
297 #define printFlush(format...) do {Printf(format); Flush(Output());} while (0)
299 /* Function: interact
301 * Action: Execute a commandfile and then perform standard shell user
302 * interaction.
304 * Input: struct InterpreterState *is -- interpreter state
306 * Output: LONG -- error code
308 LONG interact(struct InterpreterState *is);
311 /* Function: loadCommand
313 * Action: Load a command, searching the paths, C: and the resident lists.
315 * Input: STRPTR commandName -- the command to load
316 * struct ShellState *ss -- state
318 * Output: BPTR -- segment of the loaded command or NULL if there was an
319 * error
321 BPTR loadCommand(STRPTR commandName, struct ShellState *ss);
324 /* Function: unloadCommand
326 * Action: Free the resources held by a (loaded) command.
328 * Input: BPTR commandSeg -- segment of the program to
329 * unload
330 * struct ShellState *ss -- state
332 * Output: --
334 void unloadCommand(BPTR commandSeg, struct ShellState *ss);
337 /* Function: Redirection_release
339 * Action: Release resources allocated in the state
341 * Input: struct Redirection *rd -- state
343 * Output: --
345 void Redirection_release(struct Redirection *rd);
348 /* Function: Redirection_init
350 * Action: Initialize a state structure
352 * Input: struct Redirection *rd -- state
354 * Output: BOOL -- success/failure indicator
356 BOOL Redirection_init(struct Redirection *rd);
359 /* Function: setPath
361 * Action: Set the current command (standard) path.
363 * Input: BPTR lock -- a lock on the directory
365 * Notes: This will set the current directory name via
366 * SetCurrentDirName() eventhough this is not used later.
368 * Output: --
370 static void setPath(BPTR lock);
373 /* Function: printPath
375 * Action: Print the current command path to Output().
377 * Input: --
379 * Notes: Used for Prompt purposes.
381 * Output: --
383 static void printPath(void);
386 /* Function: printPrompt
388 * Action: Print the prompt to indicate that user input is viable.
390 * Input: struct InterpreterState *is -- interpreter state
392 * Output: --
394 static void printPrompt(struct InterpreterState *is);
397 /*****************************************************************************/
398 void setupResidentCommands(void);
400 static void initDefaultInterpreterState(struct InterpreterState *is)
402 int i;
404 is->argcount = 0;
406 for (i = 0; i < MAXARGS; ++i)
408 is->argnamelen[i] = 0;
409 is->argname[i][0] = '\0';
410 is->arg[i] = (IPTR)NULL;
411 is->arglen[i] = 0;
412 is->argdef[i] = (IPTR)NULL;
413 is->argdeflen[i] = 0;
414 is->argtype[i] = NORMAL;
417 is->bra = '<';
418 is->ket = '>';
419 is->dollar = '$';
420 is->dot = '.';
422 is->rdargs = NULL;
423 is->stack = NULL;
426 static LONG pushInterpreterState(struct InterpreterState *is)
428 struct InterpreterState *tmp_is;
430 tmp_is = (struct InterpreterState *)AllocMem(sizeof(*is), MEMF_LOCAL);
432 if (!tmp_is)
433 return ERROR_NO_FREE_STORE;
435 *tmp_is = *is;
436 initDefaultInterpreterState(is);
437 is->stack = tmp_is;
438 return 0;
441 static void popInterpreterState(struct InterpreterState *is)
443 struct InterpreterState *tmp_is = is->stack;
444 LONG i;
446 for (i = 0; i < is->argcount; ++i)
447 if (is->argdef[i])
448 FreeMem((APTR)is->argdef[i], is->argdeflen[i] + 1);
450 if (is->rdargs)
452 FreeDosObject(DOS_RDARGS, is->rdargs);
453 is->rdargs = NULL;
456 if (tmp_is)
458 *is = *tmp_is;
459 FreeMem(tmp_is, sizeof(*is));
461 else
462 initDefaultInterpreterState(is);
465 static LONG getArgumentIdx(struct InterpreterState *is,
466 CONST_STRPTR name, LONG len)
468 LONG i;
470 for (i = 0; i < is->argcount; ++i)
472 if (strncmp(is->argname[i], name, len) == 0)
473 return i;
476 if (is->argcount >= MAXARGS)
477 return -1;
479 i = is->argcount++;
480 CopyMem(name, is->argname[i], len);
481 is->argname[i][len] = '\0';
482 is->argnamelen[i] = len;
483 return i;
486 AROS_SH1(Shell, 41.1,
487 AROS_SHA(STRPTR, ,COMMAND,/F,NULL))
489 AROS_SHCOMMAND_INIT
491 struct Process *me = (struct Process *)FindTask(NULL);
492 struct InterpreterState is;
493 LONG error = RETURN_OK;
495 D(bug("Executing shell\n"));
497 UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 39);
498 if (!UtilityBase) return RETURN_FAIL;
500 setupResidentCommands();
502 cli = Cli();
503 setPath(NULL);
505 is.cliNumber = me->pr_TaskNum;
507 if (strcmp(me->pr_Task.tc_Node.ln_Name, "Boot Shell") == 0)
508 is.isBootShell = TRUE;
509 else
510 is.isBootShell = FALSE;
512 initDefaultInterpreterState(&is);
514 if (is.isBootShell)
515 SetPrompt("%N> ");
517 if(SHArg(COMMAND) && SHArg(COMMAND)[0])
519 struct Redirection rd;
520 struct CommandLine cl = {SHArgLine(),
522 strlen(SHArg(COMMAND))};
524 if(Redirection_init(&rd))
526 D(bug("Running command %s\n", SHArg(COMMAND)));
527 error = checkLine(&rd, &cl, &is);
528 Redirection_release(&rd);
531 D(bug("Command done\n"));
533 else
535 error = interact(&is);
538 D(bug("Exiting shell\n"));
540 return error;
542 AROS_SHCOMMAND_EXIT
545 struct UtilityBase *UtilityBase;
547 void setupResidentCommands(void)
554 /* First we execute the script, then we interact with the user */
555 LONG interact(struct InterpreterState *is)
557 LONG error = 0;
558 BOOL moreLeft = FALSE;
560 if (!cli->cli_Background)
562 SetVBuf(Output(), NULL, BUF_FULL, -1);
563 if (is->isBootShell)
565 PutStr
567 "AROS - The AROS Research Operating System\n"
568 "Copyright © 1995-2009, The AROS Development Team. All rights reserved.\n"
569 "AROS is licensed under the terms of the AROS Public License (APL),\n"
570 "a copy of which you should have received with this distribution.\n"
571 "Visit http://www.aros.org/ for more information.\n"
574 else
576 IPTR data[] = {(IPTR)is->cliNumber};
578 VPrintf("New Shell process %ld\n", data);
580 SetVBuf(Output(), NULL, BUF_LINE, -1);
585 struct CommandLine cl = { NULL, 0, 0 };
586 struct Redirection rd;
588 if(Redirection_init(&rd))
590 if (cli->cli_Interactive)
591 printPrompt(is);
593 moreLeft = readLine(&cl, cli->cli_CurrentInput);
594 error = checkLine(&rd, &cl, is);
596 Redirection_release(&rd);
597 FreeVec(cl.line);
600 if (!moreLeft)
602 popInterpreterState(is);
604 if (!cli->cli_Interactive)
606 Close(cli->cli_CurrentInput);
608 if (AROS_BSTR_strlen(cli->cli_CommandFile))
610 DeleteFile(AROS_BSTR_ADDR(cli->cli_CommandFile));
611 AROS_BSTR_setstrlen(cli->cli_CommandFile, 0);
614 if (!cli->cli_Background)
616 cli->cli_CurrentInput = cli->cli_StandardInput;
617 cli->cli_Interactive = TRUE;
618 moreLeft = TRUE;
619 Flush(Output());
620 Flush(Error());
624 } while(moreLeft);
626 if (cli->cli_Interactive)
627 printFlush("Process %ld ending\n", is->cliNumber);
629 return error;
633 /* Close redirection files and install regular input and output streams */
634 void releaseFiles(struct Redirection *rd)
636 if (rd->newIn) Close(rd->newIn);
637 rd->newIn = NULL;
639 if (rd->newOut) Close(rd->newOut);
640 rd->newOut = NULL;
643 char avBuffer[256];
644 char varBuffer[256];
645 char argBuffer[256];
648 /* Take care of one command line */
649 LONG checkLine(struct Redirection *rd, struct CommandLine *cl,
650 struct InterpreterState *is)
652 /* The allocation is taken care of by appendString */
653 struct CSource filtered = { NULL, 0, 0 };
654 struct CSource cs = { cl->line, strlen(cl->line), 0 };
655 struct LocalVar *lv;
656 LONG result = ERROR_UNKNOWN;
658 lv = FindVar("echo", LV_VAR);
659 if (lv != NULL)
661 /* AmigaDOS's shell is content also with echo being set to anything
662 that begins with "on" in order to trigger commands echoing on,
663 it doesn't really have to be set to just "on". */
664 if (lv->lv_Len >= 2)
665 if (strncasecmp(lv->lv_Value, "on", 2) == 0)
667 /* Ok, commands echoing is on. */
668 PutStr(cl->line);
672 D(bug("Calling convertLine(), line = %s\n", cl->line));
674 if ((result = convertLine(&filtered, &cs, rd, is)) == 0)
676 D(bug("Position %i\n", filtered.CS_CurChr));
678 /* End string */
679 appendString(&filtered, "\n\0", 2);
681 /* Consistency checks */
682 if(rd->haveOutRD && rd->haveAppRD)
684 PutStr("Cannot combine > with >>\n");
685 result = ERROR_TOO_MANY_LEVELS;
686 goto exit;
689 /* Only a comment? */
690 if(!rd->haveCommand)
692 result = 0;
693 goto exit;
696 /* stegerg: Set redirection to default in/out handles */
698 if(rd->haveOutRD)
700 D(bug("Redirecting output to file %s\n", rd->outFileName));
702 rd->newOut = Open(rd->outFileName, MODE_NEWFILE);
704 if(BADDR(rd->newOut) == NULL)
706 result = IoErr();
707 PrintFault(IoErr(), rd->outFileName);
708 goto exit;
711 D(bug("Output stream opened\n"));
712 SelectOutput(rd->newOut);
715 if(rd->haveAppRD)
717 rd->newOut = Open(rd->outFileName, MODE_READWRITE);
719 if(BADDR(rd->newOut) == NULL)
721 result = IoErr();
722 PrintFault(IoErr(), rd->outFileName);
723 goto exit;
726 Seek(rd->newOut, 0, OFFSET_END);
727 D(bug("Output stream opened (append)\n"));
728 SelectOutput(rd->newOut);
731 if(rd->haveInRD)
733 rd->newIn = Open(rd->inFileName, MODE_OLDFILE/*FMF_READ*/);
735 if(BADDR(rd->newIn) == NULL)
737 result = IoErr();
738 PrintFault(IoErr(), rd->inFileName);
739 goto exit;
742 D(bug("Input stream opened\n"));
743 SelectInput(rd->newIn);
746 D(bug("Calling executeLine()\n"));
748 /* OK, we've got a command. Let's execute it! */
749 result = executeLine(rd->commandStr, filtered.CS_Buffer, rd, is);
751 SelectInput(cli->cli_StandardInput);
752 SelectOutput(cli->cli_StandardOutput);
754 else
756 /* PutStr("Erroneous command line.\n"); */
757 PrintFault(result, filtered.CS_Buffer);
760 exit:
761 FreeVec(filtered.CS_Buffer);
763 if (cli->cli_Interactive)
765 Flush(Output());
766 Flush(Error());
769 return result;
772 static void substArgs(struct CSource *filtered, CONST_STRPTR s, LONG size,
773 struct InterpreterState *is)
775 CONST_STRPTR send = s + size;
776 LONG i, len;
778 while (s < send)
780 if (s[0] == is->bra)
782 char buf[32];
784 ++s;
785 if (s[0] == is->dollar && s[1] == is->dollar && s[2] == is->ket)
787 /* <$$> CLI number substitution */
788 s += 3;
789 len = sprintf(buf, "%d", is->cliNumber);
790 appendString(filtered, buf, len);
792 else for (i = 0; i < is->argcount; ++i)
794 len = is->argnamelen[i];
795 if (strncmp(s, is->argname[i], len) == 0)
797 CONST_STRPTR pos = strchr(s + len, is->ket);
799 if (s[len] == is->dollar && !is->arg[i] && pos)
801 /* default argument */
802 s += len + 1;
803 appendString(filtered, s, pos - s);
804 s = pos + 1;
805 break;
807 else if (s[len] == is->ket || s[len] == is->dollar && pos)
809 /* argument substitution */
810 STRPTR arg = NULL;
811 UBYTE t = is->argtype[i];
813 s = pos + 1;
814 if (t & KEYWORD)
816 arg = is->argname[i];
817 len = is->argnamelen[i];
819 else if (t & (SWITCH | TOGGLE))
821 arg = is->arg[i] ? "1" : "0";
822 len = 1;
824 else if (t & NUMERIC)
826 if (is->arg[i])
828 LONG value;
830 if (t & MULTIPLE)
832 LONG **m = (LONG**)is->arg[i];
834 bug("[Shell] substArgs: %s -> ",
835 is->argname[i]);
837 arg = (STRPTR)1;
838 while (*m)
840 if (arg == (STRPTR)1)
841 arg = 0;
842 else
843 appendString(filtered, " ", 1);
845 value = **m;
846 bug("%d ", value);
847 len = sprintf(buf, "%d", value);
848 appendString(filtered, buf, len);
849 ++m;
852 bug("\n");
853 break;
855 else
857 value = *(LONG*)is->arg[i];
858 len = sprintf(buf, "%d", value);
859 arg = buf;
862 else
864 arg = (STRPTR)is->argdef[i];
865 len = is->argdeflen[i];
868 else if (t & MULTIPLE)
870 CONST_STRPTR *m = (CONST_STRPTR*)is->arg[i];
872 bug("[Shell] substArgs: %s -> ", is->argname[i]);
873 if (!m || !*m)
875 arg = (STRPTR)is->argdef[i];
876 len = is->argdeflen[i];
878 bug("%s\n", arg);
880 if (arg > 0)
881 appendString(filtered, arg, len);
882 break;
885 arg = (STRPTR)1;
886 while (*m)
888 if (arg == (STRPTR)1)
889 arg = 0;
890 else
891 appendString(filtered, " ", 1);
893 bug("%s ", *m);
894 len = strlen(*m);
895 appendString(filtered, *m, len);
897 ++m;
899 bug("\n");
901 break;
903 else
905 arg = (STRPTR)is->arg[i];
906 if (!arg)
908 arg = (STRPTR)is->argdef[i];
909 len = is->argdeflen[i];
911 else
912 len = is->arglen[i];
915 bug("[Shell] substArgs: %s -> %s\n",
916 is->argname[i], arg);
918 if (arg > 0)
919 appendString(filtered, arg, len);
920 break;
925 if (is->argcount == i)
926 appendString(filtered, s - 1, 1);
928 else
929 appendString(filtered, s++, 1);
933 static BOOL doCommand(struct CSource *cs, struct CSource *filtered,
934 CONST_STRPTR cmd1, LONG l1,
935 CONST_STRPTR cmd2, LONG l2,
936 TEXT *res, LONG *error,
937 struct InterpreterState *is)
939 CONST_STRPTR s = cs->CS_Buffer + cs->CS_CurChr + 1;
940 CONST_STRPTR start = s;
941 LONG len = l1;
942 BOOL match = FALSE;
944 if (strncasecmp(s, cmd1, l1) == 0)
945 match = TRUE;
946 else if (cmd2 && strncasecmp(s, cmd2, l2) == 0)
948 match = TRUE;
949 len = l2;
952 if (match)
954 s += len;
955 while (s[0] == ' ' || s[0] == '\t')
956 ++s;
958 if (s[0] == '\n' || s[0] == '\0')
960 appendString(filtered, &is->dot, 1);
961 appendString(filtered, start, len);
962 *error = ERROR_REQUIRED_ARG_MISSING;
963 return TRUE;
965 else
967 *res = s[0];
968 cs->CS_CurChr += s - start + 2;
971 return TRUE;
974 return FALSE;
978 /* The shell has the following semantics when it comes to command lines:
979 Redirection (<,>,>>) may be written anywhere (except before the command
980 itself); the following item (as defined by ReadItem() is the redirection
981 file. The first item of the command line is the command to be executed.
982 This may be an alias, that is there is a Local LV_ALIAS variable that
983 should be substituted for the command text. Aliasing only applies to
984 commands and not to options, for instance. Variables (set by SetEnv or Set)
985 may be referenced by prepending a '$' to the variable name. */
987 LONG convertLine(struct CSource *filtered, struct CSource *cs,
988 struct Redirection *rd, struct InterpreterState *is)
991 #define item cs->CS_Buffer[cs->CS_CurChr]
992 #define from cs->CS_Buffer
993 #define advance(x) cs->CS_CurChr += x;
995 LONG result;
997 while(TRUE)
999 D(bug("Str: %s\n", cs->CS_Buffer+cs->CS_CurChr));
1001 while(item == ' ' || item == '\t')
1003 STRPTR temp = " ";
1005 temp[0] = item;
1007 appendString(filtered, temp, 1);
1008 advance(1);
1011 /* Are we done yet? */
1012 if(item == '\n' || item == ';' || item == '\0')
1013 break;
1015 if(item == '|')
1017 BPTR pipefhs[2] = {0, 0};
1018 int i;
1019 struct TagItem tags[] =
1021 { SYS_Input , (IPTR) NULL },
1022 { SYS_Output , SYS_DupStream },
1023 { SYS_Error , SYS_DupStream },
1024 { SYS_Asynch , TRUE },
1025 { NP_StackSize, cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT },
1026 { TAG_DONE , 0 }
1029 /* Prevent command lines like "Prompt> | Olle echo Oepir" */
1030 if(!rd->haveCommand)
1031 return ERROR_ACTION_NOT_KNOWN;
1033 item = 0;
1035 /* There must be something after a pipe... */
1038 i = cs->CS_CurChr + 1;
1039 cs->CS_Buffer[i] == ' ' || cs->CS_Buffer[i] == '\t';
1043 if(cs->CS_Buffer[i] == '\n' || cs->CS_Buffer[i] == ';' || cs->CS_Buffer[i] == '\0')
1045 SetIoErr(ERROR_LINE_TOO_LONG); /* what kind of error must we report? */
1046 return ERROR_LINE_TOO_LONG;
1049 D(bug("commannd = %S\n", &item+1));
1051 if(rd->haveOutRD)
1053 if (SystemTagList(&item+1, tags) == -1)
1054 return IoErr();
1056 else
1058 if (Pipe("PIPEFS:", &pipefhs[0], &pipefhs[1]) != DOSTRUE)
1059 return IoErr();
1061 tags[0].ti_Data = (IPTR)pipefhs[0];
1063 if (SystemTagList(&item+1, tags) == -1)
1065 LONG error = IoErr();
1067 Close(pipefhs[0]);
1068 Close(pipefhs[1]);
1070 return error;
1073 rd->oldOut = SelectOutput(pipefhs[1]);
1074 rd->newOut = pipefhs[1];
1077 else
1078 if(item == '<')
1080 /* Prevent command lines like "Prompt> <Olle type" */
1081 if(!rd->haveCommand)
1082 return ERROR_ACTION_NOT_KNOWN;
1084 /* Multiple redirections not allowed */
1085 if(rd->haveInRD)
1086 return ERROR_TOO_MANY_LEVELS;
1088 advance(1);
1089 result = ReadItem(rd->inFileName, FILENAME_LEN, cs);
1091 D(bug("Found input redirection\n"));
1093 if(result == ITEM_ERROR || result == ITEM_NOTHING)
1094 return ERROR_OBJECT_NOT_FOUND;
1096 rd->haveInRD = TRUE;
1098 else if(item == '>')
1100 /* Prevent command lines like "Prompt> >>Olle echo Oepir" */
1101 if(!rd->haveCommand)
1102 return ERROR_ACTION_NOT_KNOWN;
1104 advance(1);
1106 if(item == '>')
1108 /* Multiple redirections not allowed */
1109 if(rd->haveAppRD)
1110 return ERROR_TOO_MANY_LEVELS;
1112 advance(1);
1113 result = ReadItem(rd->outFileName, FILENAME_LEN, cs);
1115 D(bug("Found append redirection\n"));
1117 if(result == ITEM_ERROR || result == ITEM_NOTHING)
1118 return ERROR_OBJECT_NOT_FOUND;
1120 rd->haveAppRD = TRUE;
1122 else
1124 /* Multiple redirections not allowed */
1125 if(rd->haveOutRD)
1126 return ERROR_TOO_MANY_LEVELS;
1128 result = ReadItem(rd->outFileName, FILENAME_LEN, cs);
1130 D(bug("Found output redirection\n"));
1132 if(result == ITEM_ERROR || result == ITEM_NOTHING)
1133 return ERROR_OBJECT_NOT_FOUND;
1135 rd->haveOutRD = TRUE;
1138 else if (item == is->dollar) /* Possible environment variable usage */
1140 LONG size = cs->CS_CurChr;
1142 advance(1);
1143 result = ReadItem(avBuffer, sizeof(avBuffer), cs);
1145 D(bug("Found variable\n"));
1147 if(result == ITEM_ERROR || ITEM_NOTHING)
1148 return ERROR_REQUIRED_ARG_MISSING;
1152 (GetVar(avBuffer, varBuffer, sizeof(varBuffer),
1153 GVF_GLOBAL_ONLY | LV_VAR) != -1) &&
1154 !(varBuffer[0] == '$' && !strcmp(varBuffer+1, avBuffer))
1157 LONG result;
1158 struct CSource varCs = { varBuffer, sizeof(varBuffer), 0 };
1160 D(bug("Real variable! Value = %s\n", varBuffer));
1162 if ((result = convertLine(filtered, &varCs, rd, is)) != 0)
1163 return result;
1165 else
1166 /* If this "variable" wasn't defined, we use the '$' as a
1167 regular character */
1169 D(bug("No real variable\n"));
1171 if(!rd->haveCommand)
1173 cs->CS_CurChr = size;
1174 getCommand(filtered, cs, rd, is);
1176 else
1177 appendString(filtered, cs->CS_Buffer + size,
1178 cs->CS_CurChr - size);
1181 else
1183 STRPTR s = &item;
1185 if (strncmp(s, ".popis\n", 7) == 0)
1187 popInterpreterState(is);
1188 return 0;
1190 else if (strncmp(s, ".pushis\n", 8) == 0)
1192 pushInterpreterState(is);
1193 return 0;
1195 else if (item == is->dot)
1197 LONG error = 0, i, j, len;
1199 ++s;
1201 if (s[0] == ' ') /* dot comment */
1202 return 0;
1203 else if (doCommand(cs, filtered, "bra ", 4, NULL, 0,
1204 &is->bra, &error, is))
1206 return error;
1208 else if (doCommand(cs, filtered, "ket ", 4, NULL, 0,
1209 &is->ket, &error, is))
1211 return error;
1213 else if (doCommand(cs, filtered, "dollar ", 7, "dol ", 4,
1214 &is->dollar, &error, is))
1216 return error;
1218 else if (doCommand(cs, filtered, "dot ", 4, NULL, 0,
1219 &is->ket, &error, is))
1221 return error;
1223 else if (strncasecmp(s, "key ", 4) == 0 ||
1224 strncasecmp(s, "k ", 2) == 0)
1226 /* .key must be the first line of the script */
1227 struct RDArgs *rd;
1229 len = s[1] == ' ' ? 2 : 4;
1230 appendString(filtered, &is->dot, 1);
1231 appendString(filtered, s, len);
1232 advance(++len);
1234 if (is->rdargs)
1236 appendString(filtered, "duplicate", 10);
1237 FreeDosObject(DOS_RDARGS, is->rdargs);
1238 is->rdargs = NULL;
1239 return ERROR_ACTION_NOT_KNOWN;
1242 is->rdargs = AllocDosObject(DOS_RDARGS, NULL);
1243 if (is->rdargs)
1245 result = ReadItem(argBuffer, sizeof(argBuffer), cs);
1247 if (result == ITEM_ERROR)
1248 return ERROR_UNKNOWN;
1250 if (result == ITEM_NOTHING)
1251 return ERROR_KEY_NEEDS_ARG;
1253 len = strlen(argBuffer);
1254 appendString(filtered, argBuffer, len + 1);
1256 s = AROS_BSTR_ADDR(cli->cli_CommandName);
1257 len = AROS_BSTR_strlen(cli->cli_CommandName);
1258 is->rdargs->RDA_Source.CS_Buffer = s;
1259 is->rdargs->RDA_Source.CS_Length = len;
1260 is->rdargs->RDA_Source.CS_CurChr = 0;
1262 rd = ReadArgs(argBuffer, is->arg, is->rdargs);
1263 if (rd)
1265 UBYTE t;
1267 s = argBuffer;
1268 is->argcount = 0;
1270 for (i = 0; i < MAXARGS; ++i)
1272 len = 0;
1274 while (s[0] == ' ' || s[0] == '\t')
1275 ++s;
1277 while (s[0] != '/' && s[0] != '\0')
1279 ++len;
1280 ++s;
1283 j = getArgumentIdx(is, s - len, len);
1284 if (j < 0)
1285 return ERROR_UNKNOWN;
1287 t = NORMAL;
1288 while (s[0] == '/')
1290 switch (*++s)
1292 case 'A': t |= REQUIRED; break;
1293 case 'K': t |= KEYWORD; break;
1294 case 'M': t |= MULTIPLE; break;
1295 case 'S': t |= SWITCH; break;
1296 case 'T': t |= TOGGLE; break;
1297 case 'N': t |= NUMERIC; break;
1298 case 'F': t |= REST; break;
1299 default: return ERROR_UNKNOWN;
1301 ++s;
1304 is->argtype[j] = t;
1306 while (s[0] != ',' && s[0] != '\0')
1307 ++s;
1309 if (!(t & (NUMERIC | SWITCH | TOGGLE)))
1310 if (is->arg[j])
1311 is->arglen[j] =
1312 strlen((STRPTR)is->arg[j]);
1314 if (s[0] == '\0')
1315 break;
1316 ++s;
1319 #if DEBUG1
1320 kprintf("[Shell] argcount=%d\n", is->argcount);
1321 for (i = 0; i < is->argcount; ++i)
1323 t = is->argtype[i];
1324 if (t & (SWITCH | TOGGLE | NUMERIC))
1326 kprintf("[Shell] .key[%s]/%02x = %d\n",
1327 is->argname[i], (int)is->argtype[i],
1328 is->arg[i]);
1330 else if (t & MULTIPLE)
1332 CONST_STRPTR *m = (CONST_STRPTR*)is->arg[i];
1334 kprintf("[Shell] .key[%s]/%02x = ",
1335 is->argname[i], (int)is->argtype[i]);
1336 while (*m)
1338 kprintf("%s ", *m);
1339 ++m;
1341 kprintf("\n");
1343 else
1345 kprintf("[Shell] .key[%s]/%02x = %s\n",
1346 is->argname[i], (int)is->argtype[i],
1347 is->arg[i]);
1350 #endif
1352 else
1354 error = IoErr();
1355 D(bug("[Shell] bad args for: %s\n", argBuffer));
1356 FreeDosObject(DOS_RDARGS, is->rdargs);
1357 is->rdargs = NULL;
1358 return error;
1361 return 0;
1363 else
1365 D(bug("[Shell] memory exhausted\n"));
1366 return ERROR_NO_FREE_STORE;
1369 else if (strncasecmp(s, "default ", 8) == 0 ||
1370 strncasecmp(s, "def ", 4) == 0)
1372 len = s[3] == ' ' ? 5 : 9;
1373 advance(len);
1375 i = cs->CS_CurChr;
1376 result = ReadItem(argBuffer, sizeof(argBuffer), cs);
1378 if (result == ITEM_UNQUOTED)
1380 len = cs->CS_CurChr - i;
1382 i = getArgumentIdx(is, argBuffer, len);
1383 if (i < 0)
1384 return ERROR_UNKNOWN;
1386 advance(1);
1387 len = cs->CS_CurChr;
1388 result = ReadItem(argBuffer, sizeof(argBuffer), cs);
1389 len = cs->CS_CurChr - len;
1390 switch (result)
1392 case ITEM_ERROR:
1393 return ERROR_UNKNOWN;
1394 case ITEM_NOTHING:
1395 return ERROR_REQUIRED_ARG_MISSING;
1396 default:
1397 if (is->argdef[i])
1398 FreeMem((APTR)is->argdef[i], is->argdeflen[i] + 1);
1400 is->argdef[i] = (IPTR)AllocMem(len + 1, MEMF_LOCAL);
1401 CopyMem(argBuffer, (APTR)is->argdef[i], len);
1402 ((STRPTR)is->argdef[i])[len] = '\0';
1403 is->argdeflen[i] = len;
1404 advance(len);
1405 return 0;
1411 /* This is a regular character -- that is, we have a command */
1412 if(!rd->haveCommand)
1414 D(bug("Found possible command\n"));
1416 getCommand(filtered, cs, rd, is);
1418 else
1420 /* Copy argument */
1421 LONG size = cs->CS_CurChr;
1423 result = ReadItem(argBuffer, sizeof(argBuffer), cs);
1425 /*??AGR who coded this shit ? */
1426 if(result == ITEM_ERROR || ITEM_NOTHING)
1427 return ERROR_UNKNOWN;
1429 D(bug("\nFound argument %s\n", argBuffer));
1430 substArgs(filtered, from + size, cs->CS_CurChr - size, is);
1435 D(bug("Exiting convertLine()\n"));
1437 return 0;
1443 BOOL getCommand(struct CSource *filtered, struct CSource *cs,
1444 struct Redirection *rd, struct InterpreterState *is)
1446 LONG result;
1448 rd->haveCommand = TRUE;
1450 D(bug("Command found!\n"));
1452 result = ReadItem(rd->commandStr, COMMANDSTR_LEN, cs);
1454 if(result == ITEM_ERROR || result == ITEM_NOTHING)
1455 return FALSE;
1457 /* Is this command an alias? */
1458 if(GetVar(rd->commandStr, avBuffer, sizeof(avBuffer),
1459 GVF_LOCAL_ONLY | LV_ALIAS) != -1)
1461 struct CSource aliasCs = { avBuffer, sizeof(avBuffer), 0 };
1463 result = ReadItem(rd->commandStr, COMMANDSTR_LEN, &aliasCs);
1465 D(bug("Found alias! value = %s\n", avBuffer));
1467 if(result == ITEM_ERROR || result == ITEM_NOTHING)
1468 return FALSE;
1470 /* We don't check if the alias was an alias as that might
1471 lead to infinite loops (alias Copy Copy) */
1473 /* Make a recursive call to take care of the rest of the
1474 alias string */
1475 return convertLine(filtered, &aliasCs, rd, is);
1478 D(bug("Command = %s\n", rd->commandStr));
1480 return TRUE;
1483 #undef item
1484 #undef from
1485 #undef advance
1487 #define __extendSize 512 /* How much to increase buffer if it's full */
1490 BOOL readLine(struct CommandLine *cl, BPTR inputStream)
1492 LONG letter;
1494 while(TRUE)
1496 letter = inputStream ? FGetC(inputStream) : EOF;
1498 D(bug("Read character %c (%d)\n", letter, letter));
1500 /* -2 to skip test for boundary for terminating '\n\0' */
1501 if(cl->position > (cl->size - 2))
1503 STRPTR newString = AllocVec(cl->size + __extendSize, MEMF_ANY);
1505 D(bug("Allocated new buffer %p\n", newString));
1507 if(cl->line != NULL)
1508 CopyMem(cl->line, newString, cl->size);
1510 cl->size += __extendSize;
1511 FreeVec(cl->line);
1512 cl->line = newString;
1515 if(letter == '\n' || letter == EOF)
1517 D(bug("Found end of line\n"));
1518 break;
1521 cl->line[cl->position++] = letter;
1524 /* Terminate the line with a newline and a NULL terminator */
1525 cl->line[cl->position++] = '\n';
1526 cl->line[cl->position++] = '\0';
1528 D(bug("commandline: %s\n", cl->line));
1530 if(letter == EOF)
1531 return FALSE;
1533 return TRUE;
1537 /* Currently, there is no error checking involved */
1538 BOOL appendString(struct CSource *cs, CONST_STRPTR fromStr, LONG size)
1540 /* +2 for additional null bytes, '\n', \0' */
1541 while(cs->CS_CurChr + size + 2 > (cs->CS_Length - cs->CS_CurChr))
1543 STRPTR newString = AllocVec(cs->CS_Length + __extendSize, MEMF_ANY);
1545 CopyMem(cs->CS_Buffer, newString, cs->CS_Length);
1546 cs->CS_Length += __extendSize;
1547 FreeVec(cs->CS_Buffer);
1548 cs->CS_Buffer = newString;
1551 while(size > 0)
1553 cs->CS_Buffer[cs->CS_CurChr++] = *fromStr++;
1554 size--;
1557 return TRUE;
1560 void unloadCommand(BPTR commandSeg, struct ShellState *ss)
1562 if (!cli->cli_Module) return;
1564 if(ss->residentCommand)
1566 struct Segment *residentSeg = (struct Segment *)BADDR(commandSeg);
1568 Forbid();
1570 /* Decrease usecount */
1571 if(residentSeg->seg_UC > 0)
1572 residentSeg->seg_UC--;
1574 Permit();
1576 ss->residentCommand = FALSE;
1578 else
1579 UnLoadSeg(commandSeg);
1581 #if SET_HOMEDIR
1582 if (ss->homeDirChanged)
1584 UnLock(SetProgramDir(ss->oldHomeDir));
1585 ss->homeDirChanged = FALSE;
1587 #endif
1591 BPTR loadCommand(STRPTR commandName, struct ShellState *ss)
1593 BPTR oldCurDir;
1594 BPTR commandSeg = NULL;
1595 BPTR *paths;
1596 struct Segment *residentSeg;
1597 BOOL absolutePath = strpbrk(commandName, "/:") != NULL;
1598 BPTR file;
1600 /* We check the resident lists only if we do not have an absolute path */
1601 if(!absolutePath)
1603 Forbid();
1605 /* Check regular list first... */
1606 residentSeg = FindSegment(commandName, NULL, FALSE);
1608 if(residentSeg == NULL)
1610 /* ... then the system list */
1611 residentSeg = FindSegment(commandName, NULL, TRUE);
1614 if(residentSeg != NULL)
1616 /* Can we use this command? */
1617 if(residentSeg->seg_UC == CMD_INTERNAL || residentSeg->seg_UC >= 0)
1619 if(residentSeg->seg_UC >= 0)
1620 residentSeg->seg_UC++;
1622 ss->residentCommand = TRUE;
1623 Permit();
1624 return MKBADDR(residentSeg);
1628 Permit();
1631 ss->residentCommand = FALSE;
1633 D(bug("Trying to load command1: %s\n", commandName));
1635 oldCurDir = CurrentDir(NULL);
1636 CurrentDir(oldCurDir);
1638 file = Open(commandName, MODE_OLDFILE);
1640 if (!file)
1644 absolutePath || /* If this was an absolute path, we don't check the paths set by
1645 'path' or the C: multiassign */
1646 IoErr() == ERROR_OBJECT_IN_USE /* The object might be exclusively locked */
1648 return NULL;
1650 /* Search the command in the path */
1654 paths = (BPTR *)BADDR(cli->cli_CommandDir);
1655 file == NULL && paths != NULL;
1656 paths = (BPTR *)BADDR(paths[0]) /* Go on with the next path */
1659 CurrentDir(paths[1]);
1660 file = Open(commandName, MODE_OLDFILE);
1663 /* The last resort -- the C: multiassign */
1664 if (!file)
1666 commandName-=2;
1667 file = Open(commandName, MODE_OLDFILE);
1671 if (file)
1673 commandSeg = LoadSeg(commandName);
1675 #if SET_HOMEDIR
1676 if (commandSeg)
1678 BPTR lock = ParentOfFH(file);
1680 if (lock)
1682 ss->oldHomeDir = SetProgramDir(lock);
1683 ss->homeDirChanged = TRUE;
1686 else
1687 #endif
1689 struct FileInfoBlock fib;
1690 if (Examine(file, &fib) && fib.fib_Protection & FIBF_SCRIPT)
1692 commandSeg = LoadSeg("C:Execute");
1693 if (commandSeg)
1695 ss->script = TRUE;
1696 ss->scriptLock = Lock(commandName, SHARED_LOCK);
1699 else
1700 SetIoErr(ERROR_FILE_NOT_OBJECT);
1703 Close(file);
1706 CurrentDir(oldCurDir);
1708 return commandSeg;
1712 /* Execute one command */
1713 LONG executeLine(STRPTR command, STRPTR commandArgs, struct Redirection *rd,
1714 struct InterpreterState *is)
1716 BPTR module;
1717 LONG error = 0;
1718 struct ShellState ss = {FALSE};
1719 TEXT cmd[4096];
1721 ss.script = FALSE;
1724 if ss->residentCommand isn't initialized as FALSE, it's value is rather
1725 random ( loadCommand doesn't change it ) so unloadCommand almost always
1726 thinks that last Command was resident, and doesn't do an UnloadSeg...
1729 D(bug("Trying to load command: %s\nArguments: %s\n", command,
1730 commandArgs));
1732 module = loadCommand(command, &ss);
1734 /* Set command name even if we couldn't load the command to be able to
1735 report errors correctly */
1736 SetProgramName(command);
1738 if(module != NULL)
1740 struct Task *me = FindTask(NULL);
1741 STRPTR oldtaskname = me->tc_Node.ln_Name;
1742 BOOL __debug_mem;
1743 LONG mem_before;
1745 BPTR seglist = ss.residentCommand ? ((struct Segment *)BADDR(module))->seg_Seg:module;
1747 STRPTR dst = cmd, src;
1748 LONG len = 0;
1750 if (ss.script)
1752 *dst++ = '"';
1753 NameFromLock(ss.scriptLock, dst, sizeof(cmd));
1754 while (*dst != '\0')
1756 ++dst;
1757 ++len;
1759 *dst++ = '"';
1760 *dst++ = ' ';
1761 UnLock(ss.scriptLock);
1762 len += 2;
1764 #if 0
1765 error = pushInterpreterState(is);
1766 if (error)
1768 D(bug("Returned from command %s\n", command));
1769 unloadCommand(module, &ss);
1770 cli->cli_Result2 = error;
1771 return RETURN_FAIL;
1773 #endif
1776 src = commandArgs;
1777 if (src[0] == ' ')
1778 ++src;
1780 for (; *src != '\0'; ++dst, ++src, ++len)
1781 *dst = *src;
1782 *dst = '\0';
1784 D(bug("Command loaded: len=%d, args=%s\n", len, cmd));
1786 SetIoErr(0); /* Clear error before we execute this command */
1787 SetSignal(0, SIGBREAKF_CTRL_C);
1789 cli->cli_Module = seglist;
1791 me->tc_Node.ln_Name = command;
1793 __debug_mem = FindVar("__debug_mem", LV_VAR) != NULL;
1795 if (__debug_mem)
1797 FreeVec(AllocVec(~0ul/2, MEMF_ANY)); /* Flush memory */
1799 mem_before = AvailMem(MEMF_ANY);
1800 Printf("Available total memory before command execution: %10ld\n", mem_before);
1803 cli->cli_ReturnCode = RunCommand(seglist, cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
1804 cmd, len);
1805 if (__debug_mem)
1807 LONG mem_after;
1808 FreeVec(AllocVec(~0ul/2, MEMF_ANY)); /* Flush memory */
1810 mem_after = AvailMem(MEMF_ANY);
1811 Printf("Available total memory after command execution: %10ld\n", mem_after);
1812 Printf("Memory difference (before - after): %10ld\n", mem_before - mem_after);
1815 me->tc_Node.ln_Name = oldtaskname;
1817 D(bug("Returned from command %s\n", command));
1818 unloadCommand(module, &ss);
1820 cli->cli_Result2 = IoErr();
1822 else
1824 /* Implicit cd? */
1825 /* SFS returns ERROR_INVALID_COMPONENT_NAME if you try to open "" */
1826 if(!(rd->haveInRD || rd->haveOutRD || rd->haveAppRD) &&
1827 (IoErr() == ERROR_OBJECT_WRONG_TYPE || IoErr() == ERROR_OBJECT_NOT_FOUND || IoErr() == ERROR_INVALID_COMPONENT_NAME))
1829 BPTR lock = Lock(command, SHARED_LOCK);
1831 if(lock != NULL)
1833 struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
1835 if(fib != NULL)
1837 if(Examine(lock, fib))
1839 if(fib->fib_DirEntryType > 0)
1841 setPath(lock);
1842 lock = CurrentDir(lock);
1844 else
1845 SetIoErr(ERROR_OBJECT_WRONG_TYPE);
1848 FreeDosObject(DOS_FIB, fib);
1851 /* UnLock the old currentdir */
1852 UnLock(lock);
1856 if(IoErr())
1858 cli->cli_Result2 = IoErr();
1859 PrintFault(IoErr(), command);
1863 D(bug("Done with the command...\n"));
1865 return error;
1869 BOOL Redirection_init(struct Redirection *rd)
1871 bzero(rd, sizeof(struct Redirection));
1873 rd->commandStr = AllocVec(COMMANDSTR_LEN, MEMF_CLEAR);
1875 rd->outFileName = AllocVec(FILENAME_LEN, MEMF_CLEAR);
1876 rd->inFileName = AllocVec(FILENAME_LEN, MEMF_CLEAR);
1878 if(rd->commandStr == NULL || rd->outFileName == NULL ||
1879 rd->inFileName == NULL)
1881 Redirection_release(rd);
1882 return FALSE;
1885 /* Preset the first bytes to "C:" to handle C: multiassigns */
1886 *rd->commandStr++ = 'C';
1887 *rd->commandStr++ = ':';
1889 return TRUE;
1893 void Redirection_release(struct Redirection *rd)
1895 /* -2 as we set pointer 2 bytes ahead to be able to use C: as a multi-
1896 assign in a smooth way */
1897 FreeVec(rd->commandStr - 2);
1898 FreeVec(rd->outFileName);
1899 FreeVec(rd->inFileName);
1901 releaseFiles(rd);
1904 static void printPath(void)
1906 STRPTR buf;
1907 ULONG i;
1909 for(i = 256; ; i += 256)
1911 buf = AllocVec(i, MEMF_ANY);
1913 if(buf == NULL)
1914 break;
1916 if(GetCurrentDirName(buf, i) == DOSTRUE)
1918 FPuts(Output(), buf);
1919 FreeVec(buf);
1920 break;
1923 FreeVec(buf);
1925 if(IoErr() != ERROR_OBJECT_TOO_LARGE)
1926 break;
1931 static void setPath(BPTR lock)
1933 BPTR dir;
1934 STRPTR buf;
1935 ULONG i;
1937 if(lock == NULL)
1938 dir = CurrentDir(NULL);
1939 else
1940 dir = lock;
1942 for(i = 256; ; i += 256)
1944 buf = AllocVec(i, MEMF_ANY);
1946 if(buf == NULL)
1947 break;
1949 if(NameFromLock(dir, buf, i))
1951 SetCurrentDirName(buf);
1952 FreeVec(buf);
1953 break;
1956 FreeVec(buf);
1959 if(lock == NULL)
1960 CurrentDir(dir);
1963 static void printPrompt(struct InterpreterState *is)
1965 BSTR prompt = cli->cli_Prompt;
1966 LONG length = AROS_BSTR_strlen(prompt);
1967 ULONG i;
1969 for(i = 0; i < length; i++)
1971 if(AROS_BSTR_getchar(prompt, i) == '%')
1973 i++;
1975 if(i == length)
1976 break;
1978 switch(AROS_BSTR_getchar(prompt, i))
1980 case 'N':
1981 case 'n':
1982 Printf("%ld", is->cliNumber);
1983 break;
1984 case 'R':
1985 case 'r':
1986 Printf("%ld", cli->cli_ReturnCode);
1987 break;
1988 case 'S':
1989 case 's':
1990 printPath();
1991 break;
1992 default:
1993 FPutC(Output(), '%');
1994 FPutC(Output(), AROS_BSTR_getchar(prompt, i));
1995 break;
1998 else
1999 FPutC(Output(), AROS_BSTR_getchar(prompt, i));
2002 Flush(Output());