revert between 56095 -> 55830 in arch
[AROS.git] / workbench / c / Shell / convertLine.c
blob6d03b4248ea4beed606d736d20b8d010c2377a12
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 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;
127 TEXT cmd[FILE_MAX];
128 LONG error;
130 D(bug("Handling alias '%s'\n", command));
131 switch (bufferReadItem(cmd, FILE_MAX, &a, DOSBase))
133 case ITEM_QUOTED:
134 case ITEM_UNQUOTED:
135 break;
136 default:
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));
147 anode.ln_Name = cmd;
148 AddTail(aliased, &anode);
149 a.cur = 0;
151 /* vars substitution */
152 if ((error = convertLoop(convertVar, '$', ss, &a, &b)))
153 goto endReadAlias;
155 /* alias foo bar1 [] bar2 */
156 for (i = 0; i < b.len; ++i)
157 if (b.buf[i] == '[' && b.buf[i + 1] == ']')
158 break;
160 bufferReset(&a);
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);
174 if (i < b.len)
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);
184 endReadAlias:
185 bufferFree(&a, SysBase);
186 bufferFree(&b, SysBase);
187 return error;
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)
198 struct List aliased;
200 NewList(&aliased);
201 bufferReset(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 */
219 return 0;
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
232 signs subtitution.
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));
246 return 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));
254 return 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));
262 return 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",
270 error));
271 return error;
274 *haveCommand = TRUE;
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);
279 if (error)
281 D(bug("[convertLine] Pass 5: Error %lu parsing redirect\n", error));
282 return 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));
299 return error;