Alias fixes:
[AROS.git] / workbench / c / Shell / convertLine.c
blobd6996ebea95aaa3fdab821148c089c84c17377d3
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <exec/lists.h>
7 #include <proto/alib.h>
8 #include <proto/dos.h>
9 #include <proto/exec.h>
11 #include <string.h>
13 #include "Shell.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;
21 BOOL quoted = FALSE;
23 bufferReset(out);
25 for (; in->cur < n; p = c)
27 c = in->buf[in->cur];
29 if (p == '*')
31 c = 0;
32 bufferCopy(in, out, 1, SysBase);
34 else if (c == '"')
36 quoted = !quoted;
37 bufferCopy(in, out, 1, SysBase);
39 else if (c == a)
41 if ((error = (*convertItem)(ss, in, out, &quoted)))
42 return error;
44 else if (!quoted && c == ';' && c != ss->mchar0)
46 /* rest of line is comment, ignore it */
47 break;
49 else
50 bufferCopy(in, out, 1, SysBase);
53 in->cur = n;
54 return 0;
57 static LONG convertLoopRedir(ShellState *ss, Buffer *in, Buffer *out)
59 LONG c, p = 0, error, n = in->len;
60 BOOL quoted = FALSE;
62 bufferReset(out);
64 for (; in->cur < n; p = c)
66 DB2(bug("[convertLoopRedir] cur %u (%c)\n", in->cur, in->buf[in->cur]));
67 c = in->buf[in->cur];
69 if (p == '*')
71 c = 0;
72 bufferCopy(in, out, 1, SysBase);
74 else if (c == '"')
76 quoted = !quoted;
77 bufferCopy(in, out, 1, SysBase);
79 else if (quoted)
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));
86 return error;
89 else
90 bufferCopy(in, out, 1, SysBase);
93 in->cur = n;
94 return 0;
97 static LONG readCommandR(ShellState *ss, Buffer *in, Buffer *out,
98 struct List *aliased)
100 STRPTR command = ss->command + 2;
101 TEXT buf[FILE_MAX];
102 LONG i;
104 i = bufferReadItem(command, FILE_MAX, in, DOSBase);
105 D(bug("[readCommandR] Got item %d: %s\n", i, command));
107 switch (i)
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);
113 case ITEM_UNQUOTED:
114 break;
115 case ITEM_NOTHING:
116 return 0;
117 default:
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;
126 TEXT cmd[FILE_MAX];
127 LONG error;
129 D(bug("Handling alias '%s'\n", command));
130 switch (bufferReadItem(cmd, FILE_MAX, &a, DOSBase))
132 case ITEM_QUOTED:
133 case ITEM_UNQUOTED:
134 break;
135 default:
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));
146 anode.ln_Name = cmd;
147 AddTail(aliased, &anode);
148 a.cur = 0;
150 /* vars substitution */
151 if ((error = convertLoop(convertVar, '$', ss, &a, &b)))
152 goto endReadAlias;
154 /* alias foo bar1 [] bar2 */
155 for (i = 0; i < b.len; ++i)
156 if (b.buf[i] == '[' && b.buf[i + 1] == ']')
157 break;
159 bufferReset(&a);
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);
173 if (i < b.len)
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);
183 endReadAlias:
184 bufferFree(&a, SysBase);
185 bufferFree(&b, SysBase);
186 return error;
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)
197 struct List aliased;
199 NewList(&aliased);
200 bufferReset(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 */
218 return 0;
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
231 signs subtitution.
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));
245 return 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));
253 return 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));
261 return 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));
269 return error;
272 *haveCommand = TRUE;
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);
277 if (error)
279 D(bug("[convertLine] Pass 5: Error %lu parsing redirect\n", error));
280 return 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));
297 return error;