2 Copyright (C) 1995-2009, The AROS Development Team. All rights reserved.
8 /******************************************************************************
24 Start a shell (interactive or background).
28 COMMAND -- command line to execute
30 FROM -- script to invoke before user interaction
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.
44 Shell FROM S:Startup-Sequence
46 Starts a shell and executes the startup script.
56 The prompt support is not using SetCurrentDirName() as this function
57 has improper limitations. More or less the same goes for GetProgramName().
59 ******************************************************************************/
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
73 #include <aros/debug.h>
75 #include <exec/memory.h>
76 #include <exec/libraries.h>
77 #include <proto/exec.h>
79 #include <dos/dosextens.h>
81 #include <dos/filesystem.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>
91 #include <aros/asmcall.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 */
120 STRPTR commandStr
; /* The command to execute */
121 STRPTR outFileName
; /* Redirection file for > or >> */
122 STRPTR inFileName
; /* Redirection file for < */
137 BPTR oldHomeDir
; /* shared lock on program file's parent directory */
140 BOOL residentCommand
; /* The last command executed was resident */
141 BOOL script
; /* This command has the script bit set */
145 struct CommandLineInterface
*cli
;
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
165 TEXT argname
[MAXARGS
][MAXARGLEN
];
166 LONG argnamelen
[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
;
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
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
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
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
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
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
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
330 * struct ShellState *ss -- state
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
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
);
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.
370 static void setPath(BPTR lock
);
373 /* Function: printPath
375 * Action: Print the current command path to Output().
379 * Notes: Used for Prompt purposes.
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
394 static void printPrompt(struct InterpreterState
*is
);
397 /*****************************************************************************/
398 void setupResidentCommands(void);
400 static void initDefaultInterpreterState(struct InterpreterState
*is
)
406 for (i
= 0; i
< MAXARGS
; ++i
)
408 is
->argnamelen
[i
] = 0;
409 is
->argname
[i
][0] = '\0';
410 is
->arg
[i
] = (IPTR
)NULL
;
412 is
->argdef
[i
] = (IPTR
)NULL
;
413 is
->argdeflen
[i
] = 0;
414 is
->argtype
[i
] = NORMAL
;
426 static LONG
pushInterpreterState(struct InterpreterState
*is
)
428 struct InterpreterState
*tmp_is
;
430 tmp_is
= (struct InterpreterState
*)AllocMem(sizeof(*is
), MEMF_LOCAL
);
433 return ERROR_NO_FREE_STORE
;
436 initDefaultInterpreterState(is
);
441 static void popInterpreterState(struct InterpreterState
*is
)
443 struct InterpreterState
*tmp_is
= is
->stack
;
446 for (i
= 0; i
< is
->argcount
; ++i
)
448 FreeMem((APTR
)is
->argdef
[i
], is
->argdeflen
[i
] + 1);
452 FreeDosObject(DOS_RDARGS
, is
->rdargs
);
459 FreeMem(tmp_is
, sizeof(*is
));
462 initDefaultInterpreterState(is
);
465 static LONG
getArgumentIdx(struct InterpreterState
*is
,
466 CONST_STRPTR name
, LONG len
)
470 for (i
= 0; i
< is
->argcount
; ++i
)
472 if (strncmp(is
->argname
[i
], name
, len
) == 0)
476 if (is
->argcount
>= MAXARGS
)
480 CopyMem(name
, is
->argname
[i
], len
);
481 is
->argname
[i
][len
] = '\0';
482 is
->argnamelen
[i
] = len
;
486 AROS_SH1(Shell
, 41.1,
487 AROS_SHA(STRPTR
, ,COMMAND
,/F
,NULL
))
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();
505 is
.cliNumber
= me
->pr_TaskNum
;
507 if (strcmp(me
->pr_Task
.tc_Node
.ln_Name
, "Boot Shell") == 0)
508 is
.isBootShell
= TRUE
;
510 is
.isBootShell
= FALSE
;
512 initDefaultInterpreterState(&is
);
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"));
535 error
= interact(&is
);
538 D(bug("Exiting shell\n"));
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
)
558 BOOL moreLeft
= FALSE
;
560 if (!cli
->cli_Background
)
562 SetVBuf(Output(), NULL
, BUF_FULL
, -1);
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"
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
)
593 moreLeft
= readLine(&cl
, cli
->cli_CurrentInput
);
594 error
= checkLine(&rd
, &cl
, is
);
596 Redirection_release(&rd
);
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
;
626 if (cli
->cli_Interactive
)
627 printFlush("Process %ld ending\n", is
->cliNumber
);
633 /* Close redirection files and install regular input and output streams */
634 void releaseFiles(struct Redirection
*rd
)
636 if (rd
->newIn
) Close(rd
->newIn
);
639 if (rd
->newOut
) Close(rd
->newOut
);
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 };
656 LONG result
= ERROR_UNKNOWN
;
658 lv
= FindVar("echo", LV_VAR
);
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". */
665 if (strncasecmp(lv
->lv_Value
, "on", 2) == 0)
667 /* Ok, commands echoing is on. */
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
));
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
;
689 /* Only a comment? */
696 /* stegerg: Set redirection to default in/out handles */
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
)
707 PrintFault(IoErr(), rd
->outFileName
);
711 D(bug("Output stream opened\n"));
712 SelectOutput(rd
->newOut
);
717 rd
->newOut
= Open(rd
->outFileName
, MODE_READWRITE
);
719 if(BADDR(rd
->newOut
) == NULL
)
722 PrintFault(IoErr(), rd
->outFileName
);
726 Seek(rd
->newOut
, 0, OFFSET_END
);
727 D(bug("Output stream opened (append)\n"));
728 SelectOutput(rd
->newOut
);
733 rd
->newIn
= Open(rd
->inFileName
, MODE_OLDFILE
/*FMF_READ*/);
735 if(BADDR(rd
->newIn
) == NULL
)
738 PrintFault(IoErr(), rd
->inFileName
);
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
);
756 /* PutStr("Erroneous command line.\n"); */
757 PrintFault(result
, filtered
.CS_Buffer
);
761 FreeVec(filtered
.CS_Buffer
);
763 if (cli
->cli_Interactive
)
772 static void substArgs(struct CSource
*filtered
, CONST_STRPTR s
, LONG size
,
773 struct InterpreterState
*is
)
775 CONST_STRPTR send
= s
+ size
;
785 if (s
[0] == is
->dollar
&& s
[1] == is
->dollar
&& s
[2] == is
->ket
)
787 /* <$$> CLI number substitution */
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 */
803 appendString(filtered
, s
, pos
- s
);
807 else if (s
[len
] == is
->ket
|| s
[len
] == is
->dollar
&& pos
)
809 /* argument substitution */
811 UBYTE t
= is
->argtype
[i
];
816 arg
= is
->argname
[i
];
817 len
= is
->argnamelen
[i
];
819 else if (t
& (SWITCH
| TOGGLE
))
821 arg
= is
->arg
[i
] ? "1" : "0";
824 else if (t
& NUMERIC
)
832 LONG
**m
= (LONG
**)is
->arg
[i
];
834 bug("[Shell] substArgs: %s -> ",
840 if (arg
== (STRPTR
)1)
843 appendString(filtered
, " ", 1);
847 len
= sprintf(buf
, "%d", value
);
848 appendString(filtered
, buf
, len
);
857 value
= *(LONG
*)is
->arg
[i
];
858 len
= sprintf(buf
, "%d", value
);
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
]);
875 arg
= (STRPTR
)is
->argdef
[i
];
876 len
= is
->argdeflen
[i
];
881 appendString(filtered
, arg
, len
);
888 if (arg
== (STRPTR
)1)
891 appendString(filtered
, " ", 1);
895 appendString(filtered
, *m
, len
);
905 arg
= (STRPTR
)is
->arg
[i
];
908 arg
= (STRPTR
)is
->argdef
[i
];
909 len
= is
->argdeflen
[i
];
915 bug("[Shell] substArgs: %s -> %s\n",
916 is
->argname
[i
], arg
);
919 appendString(filtered
, arg
, len
);
925 if (is
->argcount
== i
)
926 appendString(filtered
, s
- 1, 1);
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
;
944 if (strncasecmp(s
, cmd1
, l1
) == 0)
946 else if (cmd2
&& strncasecmp(s
, cmd2
, l2
) == 0)
955 while (s
[0] == ' ' || s
[0] == '\t')
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
;
968 cs
->CS_CurChr
+= s
- start
+ 2;
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;
999 D(bug("Str: %s\n", cs
->CS_Buffer
+cs
->CS_CurChr
));
1001 while(item
== ' ' || item
== '\t')
1007 appendString(filtered
, temp
, 1);
1011 /* Are we done yet? */
1012 if(item
== '\n' || item
== ';' || item
== '\0')
1017 BPTR pipefhs
[2] = {0, 0};
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
},
1029 /* Prevent command lines like "Prompt> | Olle echo Oepir" */
1030 if(!rd
->haveCommand
)
1031 return ERROR_ACTION_NOT_KNOWN
;
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));
1053 if (SystemTagList(&item
+1, tags
) == -1)
1058 if (Pipe("PIPEFS:", &pipefhs
[0], &pipefhs
[1]) != DOSTRUE
)
1061 tags
[0].ti_Data
= (IPTR
)pipefhs
[0];
1063 if (SystemTagList(&item
+1, tags
) == -1)
1065 LONG error
= IoErr();
1073 rd
->oldOut
= SelectOutput(pipefhs
[1]);
1074 rd
->newOut
= pipefhs
[1];
1080 /* Prevent command lines like "Prompt> <Olle type" */
1081 if(!rd
->haveCommand
)
1082 return ERROR_ACTION_NOT_KNOWN
;
1084 /* Multiple redirections not allowed */
1086 return ERROR_TOO_MANY_LEVELS
;
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
;
1108 /* Multiple redirections not allowed */
1110 return ERROR_TOO_MANY_LEVELS
;
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
;
1124 /* Multiple redirections not allowed */
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
;
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
))
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)
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
);
1177 appendString(filtered
, cs
->CS_Buffer
+ size
,
1178 cs
->CS_CurChr
- size
);
1185 if (strncmp(s
, ".popis\n", 7) == 0)
1187 popInterpreterState(is
);
1190 else if (strncmp(s
, ".pushis\n", 8) == 0)
1192 pushInterpreterState(is
);
1195 else if (item
== is
->dot
)
1197 LONG error
= 0, i
, j
, len
;
1201 if (s
[0] == ' ') /* dot comment */
1203 else if (doCommand(cs
, filtered
, "bra ", 4, NULL
, 0,
1204 &is
->bra
, &error
, is
))
1208 else if (doCommand(cs
, filtered
, "ket ", 4, NULL
, 0,
1209 &is
->ket
, &error
, is
))
1213 else if (doCommand(cs
, filtered
, "dollar ", 7, "dol ", 4,
1214 &is
->dollar
, &error
, is
))
1218 else if (doCommand(cs
, filtered
, "dot ", 4, NULL
, 0,
1219 &is
->ket
, &error
, is
))
1223 else if (strncasecmp(s
, "key ", 4) == 0 ||
1224 strncasecmp(s
, "k ", 2) == 0)
1226 /* .key must be the first line of the script */
1229 len
= s
[1] == ' ' ? 2 : 4;
1230 appendString(filtered
, &is
->dot
, 1);
1231 appendString(filtered
, s
, len
);
1236 appendString(filtered
, "duplicate", 10);
1237 FreeDosObject(DOS_RDARGS
, is
->rdargs
);
1239 return ERROR_ACTION_NOT_KNOWN
;
1242 is
->rdargs
= AllocDosObject(DOS_RDARGS
, NULL
);
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
);
1270 for (i
= 0; i
< MAXARGS
; ++i
)
1274 while (s
[0] == ' ' || s
[0] == '\t')
1277 while (s
[0] != '/' && s
[0] != '\0')
1283 j
= getArgumentIdx(is
, s
- len
, len
);
1285 return ERROR_UNKNOWN
;
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
;
1306 while (s
[0] != ',' && s
[0] != '\0')
1309 if (!(t
& (NUMERIC
| SWITCH
| TOGGLE
)))
1312 strlen((STRPTR
)is
->arg
[j
]);
1320 kprintf("[Shell] argcount=%d\n", is
->argcount
);
1321 for (i
= 0; i
< is
->argcount
; ++i
)
1324 if (t
& (SWITCH
| TOGGLE
| NUMERIC
))
1326 kprintf("[Shell] .key[%s]/%02x = %d\n",
1327 is
->argname
[i
], (int)is
->argtype
[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
]);
1345 kprintf("[Shell] .key[%s]/%02x = %s\n",
1346 is
->argname
[i
], (int)is
->argtype
[i
],
1355 D(bug("[Shell] bad args for: %s\n", argBuffer
));
1356 FreeDosObject(DOS_RDARGS
, is
->rdargs
);
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;
1376 result
= ReadItem(argBuffer
, sizeof(argBuffer
), cs
);
1378 if (result
== ITEM_UNQUOTED
)
1380 len
= cs
->CS_CurChr
- i
;
1382 i
= getArgumentIdx(is
, argBuffer
, len
);
1384 return ERROR_UNKNOWN
;
1387 len
= cs
->CS_CurChr
;
1388 result
= ReadItem(argBuffer
, sizeof(argBuffer
), cs
);
1389 len
= cs
->CS_CurChr
- len
;
1393 return ERROR_UNKNOWN
;
1395 return ERROR_REQUIRED_ARG_MISSING
;
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
;
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
);
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"));
1443 BOOL
getCommand(struct CSource
*filtered
, struct CSource
*cs
,
1444 struct Redirection
*rd
, struct InterpreterState
*is
)
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
)
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
)
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
1475 return convertLine(filtered
, &aliasCs
, rd
, is
);
1478 D(bug("Command = %s\n", rd
->commandStr
));
1487 #define __extendSize 512 /* How much to increase buffer if it's full */
1490 BOOL
readLine(struct CommandLine
*cl
, BPTR inputStream
)
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
;
1512 cl
->line
= newString
;
1515 if(letter
== '\n' || letter
== EOF
)
1517 D(bug("Found end of line\n"));
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
));
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
;
1553 cs
->CS_Buffer
[cs
->CS_CurChr
++] = *fromStr
++;
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
);
1570 /* Decrease usecount */
1571 if(residentSeg
->seg_UC
> 0)
1572 residentSeg
->seg_UC
--;
1576 ss
->residentCommand
= FALSE
;
1579 UnLoadSeg(commandSeg
);
1582 if (ss
->homeDirChanged
)
1584 UnLock(SetProgramDir(ss
->oldHomeDir
));
1585 ss
->homeDirChanged
= FALSE
;
1591 BPTR
loadCommand(STRPTR commandName
, struct ShellState
*ss
)
1594 BPTR commandSeg
= NULL
;
1596 struct Segment
*residentSeg
;
1597 BOOL absolutePath
= strpbrk(commandName
, "/:") != NULL
;
1600 /* We check the resident lists only if we do not have an absolute path */
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
;
1624 return MKBADDR(residentSeg
);
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
);
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 */
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 */
1667 file
= Open(commandName
, MODE_OLDFILE
);
1673 commandSeg
= LoadSeg(commandName
);
1678 BPTR lock
= ParentOfFH(file
);
1682 ss
->oldHomeDir
= SetProgramDir(lock
);
1683 ss
->homeDirChanged
= TRUE
;
1689 struct FileInfoBlock fib
;
1690 if (Examine(file
, &fib
) && fib
.fib_Protection
& FIBF_SCRIPT
)
1692 commandSeg
= LoadSeg("C:Execute");
1696 ss
->scriptLock
= Lock(commandName
, SHARED_LOCK
);
1700 SetIoErr(ERROR_FILE_NOT_OBJECT
);
1706 CurrentDir(oldCurDir
);
1712 /* Execute one command */
1713 LONG
executeLine(STRPTR command
, STRPTR commandArgs
, struct Redirection
*rd
,
1714 struct InterpreterState
*is
)
1718 struct ShellState ss
= {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
,
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
);
1740 struct Task
*me
= FindTask(NULL
);
1741 STRPTR oldtaskname
= me
->tc_Node
.ln_Name
;
1745 BPTR seglist
= ss
.residentCommand
? ((struct Segment
*)BADDR(module
))->seg_Seg
:module
;
1747 STRPTR dst
= cmd
, src
;
1753 NameFromLock(ss
.scriptLock
, dst
, sizeof(cmd
));
1754 while (*dst
!= '\0')
1761 UnLock(ss
.scriptLock
);
1765 error
= pushInterpreterState(is
);
1768 D(bug("Returned from command %s\n", command
));
1769 unloadCommand(module
, &ss
);
1770 cli
->cli_Result2
= error
;
1780 for (; *src
!= '\0'; ++dst
, ++src
, ++len
)
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
;
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
,
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();
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
);
1833 struct FileInfoBlock
*fib
= AllocDosObject(DOS_FIB
, NULL
);
1837 if(Examine(lock
, fib
))
1839 if(fib
->fib_DirEntryType
> 0)
1842 lock
= CurrentDir(lock
);
1845 SetIoErr(ERROR_OBJECT_WRONG_TYPE
);
1848 FreeDosObject(DOS_FIB
, fib
);
1851 /* UnLock the old currentdir */
1858 cli
->cli_Result2
= IoErr();
1859 PrintFault(IoErr(), command
);
1863 D(bug("Done with the command...\n"));
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
);
1885 /* Preset the first bytes to "C:" to handle C: multiassigns */
1886 *rd
->commandStr
++ = 'C';
1887 *rd
->commandStr
++ = ':';
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
);
1904 static void printPath(void)
1909 for(i
= 256; ; i
+= 256)
1911 buf
= AllocVec(i
, MEMF_ANY
);
1916 if(GetCurrentDirName(buf
, i
) == DOSTRUE
)
1918 FPuts(Output(), buf
);
1925 if(IoErr() != ERROR_OBJECT_TOO_LARGE
)
1931 static void setPath(BPTR lock
)
1938 dir
= CurrentDir(NULL
);
1942 for(i
= 256; ; i
+= 256)
1944 buf
= AllocVec(i
, MEMF_ANY
);
1949 if(NameFromLock(dir
, buf
, i
))
1951 SetCurrentDirName(buf
);
1963 static void printPrompt(struct InterpreterState
*is
)
1965 BSTR prompt
= cli
->cli_Prompt
;
1966 LONG length
= AROS_BSTR_strlen(prompt
);
1969 for(i
= 0; i
< length
; i
++)
1971 if(AROS_BSTR_getchar(prompt
, i
) == '%')
1978 switch(AROS_BSTR_getchar(prompt
, i
))
1982 Printf("%ld", is
->cliNumber
);
1986 Printf("%ld", cli
->cli_ReturnCode
);
1993 FPutC(Output(), '%');
1994 FPutC(Output(), AROS_BSTR_getchar(prompt
, i
));
1999 FPutC(Output(), AROS_BSTR_getchar(prompt
, i
));