2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
6 #include <exec/lists.h>
7 #include <proto/alib.h>
9 #include <proto/exec.h>
15 #include <aros/debug.h>
17 static LONG
convertLoop(LONG (*convertItem
)(ShellState
*, Buffer
*, Buffer
*, BOOL
*),
18 LONG a
, ShellState
*ss
, Buffer
*in
, Buffer
*out
)
20 LONG c
, p
= 0, error
, n
= in
->len
;
25 for (; in
->cur
< n
; p
= c
)
32 bufferCopy(in
, out
, 1, SysBase
);
37 bufferCopy(in
, out
, 1, SysBase
);
41 if ((error
= (*convertItem
)(ss
, in
, out
, "ed
)))
44 else if (!quoted
&& c
== ';' && c
!= ss
->mchar0
)
46 /* rest of line is comment, ignore it */
50 bufferCopy(in
, out
, 1, SysBase
);
57 static LONG
convertLoopRedir(ShellState
*ss
, Buffer
*in
, Buffer
*out
)
59 LONG c
, p
= 0, error
, n
= in
->len
;
64 for (; in
->cur
< n
; p
= c
)
66 DB2(bug("[convertLoopRedir] cur %u (%c)\n", in
->cur
, in
->buf
[in
->cur
]));
72 bufferCopy(in
, out
, 1, SysBase
);
77 bufferCopy(in
, out
, 1, SysBase
);
80 bufferCopy(in
, out
, 1, SysBase
);
81 else if (c
== '<' || c
== '>')
83 if ((error
= convertRedir(ss
, in
, out
)))
85 D(bug("[convertLoopRedir] convertRedir(%s) error %u\n", in
->buf
, error
));
90 bufferCopy(in
, out
, 1, SysBase
);
97 static LONG
readCommandR(ShellState
*ss
, Buffer
*in
, Buffer
*out
,
100 STRPTR command
= ss
->command
+ 2;
104 i
= bufferReadItem(command
, FILE_MAX
, in
, DOSBase
);
105 D(bug("[readCommandR] Got item %d: %s\n", i
, command
));
109 case ITEM_QUOTED
: /* no alias expansion */
110 if (in
->cur
< in
->len
)
111 ++in
->cur
; /* skip separator */
112 return bufferCopy(in
, out
, in
->len
- in
->cur
, SysBase
);
118 PutStr("Error in command name\n");
119 return ERROR_OBJECT_NOT_FOUND
;
122 /* Is this command an alias ? */
123 if ((i
= GetVar(command
, buf
, FILE_MAX
, GVF_LOCAL_ONLY
| LV_ALIAS
)) > 0)
125 Buffer a
= { buf
, i
, 0, 0 }, b
= { 0 };
126 struct Node anode
, *n
;
130 D(bug("Handling alias '%s'\n", command
));
131 switch (bufferReadItem(cmd
, FILE_MAX
, &a
, DOSBase
))
137 return ERROR_LINE_TOO_LONG
; /* invalid argument line */
140 ForeachNode(aliased
, n
)
142 if (strcmp(cmd
, n
->ln_Name
) == 0)
143 return ERROR_LINE_TOO_LONG
; /* alias loop */
146 D(bug("[Shell] found alias: '%s'\n", buf
));
148 AddTail(aliased
, &anode
);
151 /* vars substitution */
152 if ((error
= convertLoop(convertVar
, '$', ss
, &a
, &b
)))
155 /* alias foo bar1 [] bar2 */
156 for (i
= 0; i
< b
.len
; ++i
)
157 if (b
.buf
[i
] == '[' && b
.buf
[i
+ 1] == ']')
161 bufferCopy(&b
, &a
, i
, SysBase
);
163 if ((TEXT
*)strrchr(buf
, ' ') != buf
+ strlen(buf
) - 1
164 && in
->len
> in
->cur
&& i
== b
.len
)
166 * We need a separator here, between the command
167 * and its first argument
169 bufferAppend(" ", 1, &a
, SysBase
);
171 if (in
->cur
< in
->len
)
172 bufferCopy(in
, &a
, in
->len
- in
->cur
- 1, SysBase
);
176 b
.cur
+= 2; /* skip [] */
177 bufferCopy(&b
, &a
, b
.len
- b
.cur
, SysBase
);
180 bufferAppend("\n", 1, &a
, SysBase
);
182 error
= readCommandR(ss
, &a
, out
, aliased
);
185 bufferFree(&a
, SysBase
);
186 bufferFree(&b
, SysBase
);
190 D(bug("[readCommandR] Copying buffer '%s', len %d, pos %d\n",
191 in
->buf
, in
->len
, in
->cur
));
193 return bufferCopy(in
, out
, in
->len
- in
->cur
, SysBase
);
196 static LONG
readCommand(ShellState
*ss
, Buffer
*in
, Buffer
*out
)
203 return readCommandR(ss
, in
, out
, &aliased
);
206 /* The shell has the following semantics when it comes to command lines:
207 Redirection (<,>,>>) may be written anywhere (except before the command
208 itself); the following item (as defined by ReadItem() is the redirection
209 file. The first item of the command line is the command to be executed.
210 This may be an alias, that is there is a Local LV_ALIAS variable that
211 should be substituted for the command text. Aliasing only applies to
212 commands and not to options, for instance. Variables (set by SetEnv or Set)
213 may be referenced by prepending a '$' to the variable name. */
214 LONG
convertLine(ShellState
*ss
, Buffer
*in
, Buffer
*out
, BOOL
*haveCommand
)
216 LONG c
= in
->buf
[in
->cur
], error
;
218 if (c
== ';' && c
!= ss
->mchar0
) /* skip comment */
221 if (c
== ss
->dot
) /* .dot command at start of line */
222 return convertLineDot(ss
, in
);
224 /* Vars and BackTicks can't be properly handled by using FindItem() as
225 it wouldn't find them when they aren't surrounded with blank spaces,
226 so we handle them ourselves here. Environment variables are always
227 referenced by prepending a '$' to their name, it's only scripts
228 argument variables that can be referenced by prepending a modified
229 (.dollar) sign. Environment variable names containing non-alpha-
230 numerical characters must be surrounded with braces ( ${_} ).
231 CLI number substitution <$$> handles .dollar and .bra and .ket
233 <$$> and Variables and BackTicks need to be handled only once per
234 line, but in right order: Commodore's DPAT script builds an
235 environment variable per script used, by including the current CLI's
236 number in the variable name: $qw{$$} so we must first handle CLI
237 number substitution, then extract variables, and then handle
238 BackTicks nested commands.
241 /* PASS 1: `backticks` substitution */
242 D(bug("[convertLine] Pass 1: on (%s)\n", in
->buf
));
243 if ((error
= convertLoop(convertBackTicks
, '`', ss
, in
, out
)))
245 D(bug("[convertLine] Pass 1: Error %lu parsing backticks\n", error
));
249 /* PASS 2: <args> substitution & CLI# <$$>*/
250 D(bug("[convertLine] Pass 2: on (%s)\n", out
->buf
));
251 if ((error
= convertLoop(convertArg
, ss
->bra
, ss
, out
, in
)))
253 D(bug("[convertLine] Pass 2: Error %lu parsing <arguments> substitution and <$$> CLI#\n", error
));
257 /* PASS 3: ${vars} substitution */
258 D(bug("[convertLine] Pass 3: on (%s)\n", in
->buf
));
259 if ((error
= convertLoop(convertVar
, '$', ss
, in
, out
)))
261 D(bug("[convertLine] Pass 3: Error %lu parsing variables\n", error
));
265 /* PASS 4: command & aliases */
266 D(bug("[convertLine] Pass 4: on (%s)\n", out
->buf
));
267 if ((error
= readCommand(ss
, out
, in
)))
269 D(bug("[convertLine] Pass 4: Error %lu parsing command/aliases\n",
276 /* PASS 5: redirections */
277 D(bug("[convertLine] Pass 5: cur %d len %d (%s)\n", in
->cur
, in
->len
, in
->buf
));
278 error
= convertLoopRedir(ss
, in
, out
);
281 D(bug("[convertLine] Pass 5: Error %lu parsing redirect\n", error
));
285 if (out
->len
== 0 || out
->buf
[out
->len
-1] != '\n')
288 * Make sure that the output buffer (command arguments) ends with a newline.
289 * This is OS 3.1-compatible behavior. RunCommand() injects the supplied line
290 * into command's Input(), but doesn't append a newline. And without a newline,
291 * ReadArgs() will halt, waiting for it.
293 D(bug("[convertLine] Appending a newline\n"));
294 error
= bufferAppend("\n", 1, out
, SysBase
);
297 D(bug("[convertLine] Result: cur %d len %d (%s)\n", out
->cur
, out
->len
, out
->buf
));