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 return ERROR_LINE_TOO_LONG
; /* invalid argument */
121 /* Is this command an alias ? */
122 if ((i
= GetVar(command
, buf
, FILE_MAX
, GVF_LOCAL_ONLY
| LV_ALIAS
)) > 0)
124 Buffer a
= { buf
, i
, 0, 0 }, b
= { 0 };
125 struct Node anode
, *n
;
129 D(bug("Handling alias '%s'\n", command
));
130 switch (bufferReadItem(cmd
, FILE_MAX
, &a
, DOSBase
))
136 return ERROR_LINE_TOO_LONG
; /* invalid argument line */
139 ForeachNode(aliased
, n
)
141 if (strcmp(cmd
, n
->ln_Name
) == 0)
142 return ERROR_LINE_TOO_LONG
; /* alias loop */
145 D(bug("[Shell] found alias: '%s'\n", buf
));
147 AddTail(aliased
, &anode
);
150 /* vars substitution */
151 if ((error
= convertLoop(convertVar
, '$', ss
, &a
, &b
)))
154 /* alias foo bar1 [] bar2 */
155 for (i
= 0; i
< b
.len
; ++i
)
156 if (b
.buf
[i
] == '[' && b
.buf
[i
+ 1] == ']')
160 bufferCopy(&b
, &a
, i
, SysBase
);
162 if ((TEXT
*)strrchr(buf
, ' ') != buf
+ strlen(buf
) - 1
163 && in
->len
> in
->cur
&& i
== b
.len
)
165 * We need a separator here, between the command
166 * and its first argument
168 bufferAppend(" ", 1, &a
, SysBase
);
170 if (in
->cur
< in
->len
)
171 bufferCopy(in
, &a
, in
->len
- in
->cur
- 1, SysBase
);
175 b
.cur
+= 2; /* skip [] */
176 bufferCopy(&b
, &a
, b
.len
- b
.cur
, SysBase
);
179 bufferAppend("\n", 1, &a
, SysBase
);
181 error
= readCommandR(ss
, &a
, out
, aliased
);
184 bufferFree(&a
, SysBase
);
185 bufferFree(&b
, SysBase
);
189 D(bug("[readCommandR] Copying buffer '%s', len %d, pos %d\n",
190 in
->buf
, in
->len
, in
->cur
));
192 return bufferCopy(in
, out
, in
->len
- in
->cur
, SysBase
);
195 static LONG
readCommand(ShellState
*ss
, Buffer
*in
, Buffer
*out
)
202 return readCommandR(ss
, in
, out
, &aliased
);
205 /* The shell has the following semantics when it comes to command lines:
206 Redirection (<,>,>>) may be written anywhere (except before the command
207 itself); the following item (as defined by ReadItem() is the redirection
208 file. The first item of the command line is the command to be executed.
209 This may be an alias, that is there is a Local LV_ALIAS variable that
210 should be substituted for the command text. Aliasing only applies to
211 commands and not to options, for instance. Variables (set by SetEnv or Set)
212 may be referenced by prepending a '$' to the variable name. */
213 LONG
convertLine(ShellState
*ss
, Buffer
*in
, Buffer
*out
, BOOL
*haveCommand
)
215 LONG c
= in
->buf
[in
->cur
], error
;
217 if (c
== ';' && c
!= ss
->mchar0
) /* skip comment */
220 if (c
== ss
->dot
) /* .dot command at start of line */
221 return convertLineDot(ss
, in
);
223 /* Vars and BackTicks can't be properly handled by using FindItem() as
224 it wouldn't find them when they aren't surrounded with blank spaces,
225 so we handle them ourselves here. Environment variables are always
226 referenced by prepending a '$' to their name, it's only scripts
227 argument variables that can be referenced by prepending a modified
228 (.dollar) sign. Environment variable names containing non-alpha-
229 numerical characters must be surrounded with braces ( ${_} ).
230 CLI number substitution <$$> handles .dollar and .bra and .ket
232 <$$> and Variables and BackTicks need to be handled only once per
233 line, but in right order: Commodore's DPAT script builds an
234 environment variable per script used, by including the current CLi's
235 number in the variable name: $qw{$$} so we must first handle CLI
236 number substitution, then extract variables, and then handle
237 BackTicks nested commands.
240 /* PASS 1: `backticks` substitution */
241 D(bug("[convertLine] Pass 1: on (%s)\n", in
->buf
));
242 if ((error
= convertLoop(convertBackTicks
, '`', ss
, in
, out
)))
244 D(bug("[convertLine] Pass 1: Error %lu parsing backticks\n", error
));
248 /* PASS 2: <args> substitution & CLI# <$$>*/
249 D(bug("[convertLine] Pass 2: on (%s)\n", out
->buf
));
250 if ((error
= convertLoop(convertArg
, ss
->bra
, ss
, out
, in
)))
252 D(bug("[convertLine] Pass 2: Error %lu parsing <arguments> substitution and <$$> CLI#\n", error
));
256 /* PASS 3: ${vars} substitution */
257 D(bug("[convertLine] Pass 3: on (%s)\n", in
->buf
));
258 if ((error
= convertLoop(convertVar
, '$', ss
, in
, out
)))
260 D(bug("[convertLine] Pass 3: Error %lu parsing variables\n", error
));
264 /* PASS 4: command & aliases */
265 D(bug("[convertLine] Pass 4: on (%s)\n", out
->buf
));
266 if ((error
= readCommand(ss
, out
, in
)))
268 D(bug("[convertLine] Pass 4: Error %lu parsing aliases\n", error
));
274 /* PASS 5: redirections */
275 D(bug("[convertLine] Pass 5: cur %d len %d (%s)\n", in
->cur
, in
->len
, in
->buf
));
276 error
= convertLoopRedir(ss
, in
, out
);
279 D(bug("[convertLine] Pass 5: Error %lu parsing redirect\n", error
));
283 if (out
->len
== 0 || out
->buf
[out
->len
-1] != '\n')
286 * Make sure that the output buffer (command arguments) ends with a newline.
287 * This is OS 3.1-compatible behavior. RunCommand() injects the supplied line
288 * into command's Input(), but doesn't append a newline. And without a newline,
289 * ReadArgs() will halt, waiting for it.
291 D(bug("[convertLine] Appending a newline\n"));
292 error
= bufferAppend("\n", 1, out
, SysBase
);
295 D(bug("[convertLine] Result: cur %d len %d (%s)\n", out
->cur
, out
->len
, out
->buf
));