source: mainline/uspace/app/bdsh/input.c@ 12f5a1be

Last change on this file since 12f5a1be was 12f5a1be, checked in by Manuele Conti <manuele.conti@…>, 4 years ago

Remove debug code and CStyle

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * Copyright (c) 2011 Jiri Svoboda
4 * Copyright (c) 2011 Martin Sucha
5 * Copyright (c) 2018 Matthieu Riolo
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * - The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <str.h>
35#include <io/console.h>
36#include <io/keycode.h>
37#include <io/style.h>
38#include <io/color.h>
39#include <vfs/vfs.h>
40#include <clipboard.h>
41#include <macros.h>
42#include <errno.h>
43#include <assert.h>
44#include <stdbool.h>
45#include <tinput.h>
46#include <adt/odict.h>
47#include <adt/list.h>
48
49#include "config.h"
50#include "compl.h"
51#include "util.h"
52#include "scli.h"
53#include "input.h"
54#include "errors.h"
55#include "exec.h"
56#include "tok.h"
57
58#define MAX_PIPES 10U
59
60extern volatile unsigned int cli_quit;
61
62/** Text input field. */
63static tinput_t *tinput;
64
65/* Private helpers */
66static int run_command(char **, cliuser_t *, iostate_t *);
67
68typedef struct {
69 link_t alias_hup_link;
70 alias_t *alias;
71} alias_hup_t;
72
73static bool find_alias_hup(alias_t *alias, list_t *alias_hups)
74{
75 list_foreach(*alias_hups, alias_hup_link, alias_hup_t, link) {
76 if (alias == link->alias) {
77 return true;
78 }
79 }
80
81 return false;
82}
83
84static errno_t find_alias(char **cmd, list_t *alias_hups, alias_t **data)
85{
86 errno_t rc = EOK;
87
88 /* test if the passed cmd is an alias */
89 odlink_t *alias_link = odict_find_eq(&alias_dict, (void *)cmd[0], NULL);
90 if (alias_link != NULL) {
91 *data = odict_get_instance(alias_link, alias_t, odict);
92 /* check if the alias already has been resolved once */
93 if (! find_alias_hup(*data, alias_hups)) {
94 alias_hup_t *hup = (alias_hup_t *)calloc(1, sizeof(alias_hup_t));
95 if (hup == NULL) {
96 cli_error(CL_EFAIL, "%s: cannot allocate alias structure\n", PACKAGE_NAME);
97 rc = ENOMEM;
98 goto exit;
99 }
100 hup->alias = *data;
101 list_append(&hup->alias_hup_link, alias_hups);
102 }
103 }
104
105exit:
106 return rc;
107}
108
109static errno_t replace_alias(token_t *tokens, unsigned int tokens_start, unsigned int tokens_len, alias_t *data, char **cmd, char **line)
110{
111 errno_t rc = EOK;
112 const size_t input_length = str_size(*line) - str_size(cmd[0]) + str_size(data->value) + 1;
113 char *newline = (char *)malloc(input_length);
114 if (newline == NULL) {
115 cli_error(CL_EFAIL, "%s: cannot allocate input structure\n", PACKAGE_NAME);
116 rc = ENOMEM;
117 goto exit;
118 }
119
120 newline[0] = '\0';
121
122 unsigned int cmd_replace_index = tokens_start;
123 for (unsigned int i = 0; i < tokens_len; i++) {
124 if (i == cmd_replace_index) {
125 /* if there is a pipe symbol than cmd_token_start will point at the SPACE after the pipe symbol */
126 if (tokens[i].type == TOKTYPE_SPACE) {
127 tokens_start++;
128 str_append(*line, input_length, tokens[i].text);
129 continue;
130 }
131
132 str_append(newline, input_length, data->value);
133 } else {
134 str_append(newline, input_length, tokens[i].text);
135 }
136 }
137
138 *line = newline;
139exit:
140 return rc;
141}
142
143/*
144 * Tokenizes input from console, sees if the first word is a built-in, if so
145 * invokes the built-in entry point (a[0]) passing all arguments in a[] to
146 * the handler
147 */
148static errno_t process_input_nohup(cliuser_t *usr, list_t *alias_hups, size_t count_executed_hups)
149{
150 char *cmd[WORD_MAX];
151 size_t cmd_argc = 0;
152 errno_t rc = EOK;
153 tokenizer_t tok;
154 unsigned int i, pipe_count;
155 unsigned int pipe_pos[MAX_PIPES];
156 char *redir_from = NULL;
157 char *redir_to = NULL;
158
159 if (count_executed_hups >= HUBS_MAX) {
160 cli_error(CL_EFAIL, "%s: maximal alias hubs reached\n", PACKAGE_NAME);
161 return ELIMIT;
162 }
163
164 token_t *tokens_buf = calloc(WORD_MAX, sizeof(token_t));
165 if (tokens_buf == NULL)
166 return ENOMEM;
167 token_t *tokens = tokens_buf;
168
169 if (usr->line == NULL) {
170 free(tokens_buf);
171 return EINVAL;
172 }
173
174 rc = tok_init(&tok, usr->line, tokens, WORD_MAX);
175 if (rc != EOK) {
176 goto finit;
177 }
178
179 size_t tokens_length;
180 rc = tok_tokenize(&tok, &tokens_length);
181 if (rc != EOK) {
182 goto finit;
183 }
184
185 if (tokens_length > 0 && tokens[0].type == TOKTYPE_SPACE) {
186 tokens++;
187 tokens_length--;
188 }
189
190 if (tokens_length > 0 && tokens[tokens_length - 1].type == TOKTYPE_SPACE) {
191 tokens_length--;
192 }
193
194 cmd_argc = tokens_length;
195 unsigned wait_from = 0;
196 unsigned wait_to = 0;
197 for (i = 0, pipe_count = 0; i < tokens_length; i++) {
198 switch (tokens[i].type) {
199 case TOKTYPE_PIPE:
200 pipe_pos[pipe_count++] = i;
201 cmd_argc = i;
202 redir_from = redir_to = tmpnam(NULL);
203 break;
204
205 case TOKTYPE_RDIN:
206 wait_from = 1;
207 cmd_argc = i;
208 break;
209
210 case TOKTYPE_RDOU:
211 wait_to = 1;
212 cmd_argc = i;
213 break;
214
215 case TOKTYPE_TEXT:
216 if (wait_from) {
217 redir_from = tokens[i].text;
218 wait_from = 0;
219 }
220 if (wait_to) {
221 redir_to = tokens[i].text;
222 wait_to = 0;
223 }
224 break;
225
226 default:
227 break;
228 }
229
230 if (pipe_count > MAX_PIPES) {
231 rc = ENOTSUP;
232 goto finit;
233 }
234 }
235
236 if (wait_from || wait_to) {
237 printf("Parse error near `\\n'\n");
238 goto finit;
239 }
240
241 unsigned int cmd_token_start = 0;
242 unsigned int cmd_token_end = cmd_argc;
243
244 iostate_t new_iostate = {
245 .stdin = stdin,
246 .stdout = stdout,
247 .stderr = stderr
248 };
249
250 FILE *from = NULL;
251 FILE *to = NULL;
252
253
254 for (unsigned p = 0; p <= pipe_count; p++) {
255 /* Convert tokens of the command to string array */
256 unsigned int cmd_pos = 0;
257 for (i = cmd_token_start; i < cmd_token_end; i++) {
258 if (tokens[i].type != TOKTYPE_SPACE) {
259 cmd[cmd_pos++] = tokens[i].text;
260 }
261 }
262 cmd[cmd_pos] = NULL;
263
264 if (cmd[0] == NULL) {
265 printf("Command not found.\n");
266 rc = ENOTSUP;
267 goto finit;
268 }
269
270 alias_t *data = NULL;
271 rc = find_alias(cmd, alias_hups, &data);
272 if (rc != EOK) {
273 goto finit;
274 }
275
276 if (data != NULL) {
277 rc = replace_alias(tokens, cmd_token_start, tokens_length, data, cmd, &usr->line);
278 if (rc == EOK) {
279 /* reprocess input after string replace */
280 rc = process_input_nohup(usr, alias_hups, count_executed_hups + 1);
281 }
282 goto finit;
283 }
284
285 if (redir_to) {
286 if ((p < pipe_count) || (pipe_count == 0)) {
287 to = fopen(redir_to, "w");
288 if (to == NULL) {
289 printf("Cannot open file %s\n", redir_to);
290 rc = errno;
291 goto finit_with_files;
292 }
293 new_iostate.stdout = to;
294 }
295 }
296
297 if (redir_from) {
298 if ((p && p == pipe_count) || (pipe_count == 0)) {
299 from = fopen(redir_from, "r");
300 if (from == NULL) {
301 printf("Cannot open file %s\n", redir_from);
302 rc = errno;
303 goto finit_with_files;
304 }
305 new_iostate.stdin = from;
306 }
307 }
308
309 if (run_command(cmd, usr, &new_iostate) == 0) {
310 rc = EOK;
311 } else {
312 rc = EINVAL;
313 }
314
315 if (to) {
316 fclose(to);
317 to = NULL;
318 }
319 if (from) {
320 fclose(from);
321 from = NULL;
322 }
323 // Restore the Standard Input, Output and Error file descriptors
324 new_iostate.stdin = stdin;
325 new_iostate.stdout = stdout;
326 new_iostate.stderr = stderr;
327
328 cmd_token_start = cmd_token_end + 1;
329 cmd_token_end = (p < pipe_count - 1) ? pipe_pos[p + 1] : tokens_length;
330 }
331
332finit_with_files:
333 if (from != NULL) {
334 fclose(from);
335 }
336 if (to != NULL) {
337 fclose(to);
338 }
339
340finit:
341 if (NULL != usr->line) {
342 free(usr->line);
343 usr->line = (char *) NULL;
344 }
345 tok_fini(&tok);
346 free(tokens_buf);
347
348 return rc;
349}
350
351errno_t process_input(cliuser_t *usr)
352{
353 list_t alias_hups;
354 list_initialize(&alias_hups);
355
356 errno_t rc = process_input_nohup(usr, &alias_hups, 0);
357
358 list_foreach_safe(alias_hups, cur_link, next_link) {
359 alias_hup_t *cur_item = list_get_instance(cur_link, alias_hup_t, alias_hup_link);
360 free(cur_item);
361 }
362
363 return rc;
364}
365
366int run_command(char **cmd, cliuser_t *usr, iostate_t *new_iostate)
367{
368 int id = 0;
369
370 /* We have rubbish */
371 if (NULL == cmd[0]) {
372 return CL_ENOENT;
373 }
374
375 /* Is it a builtin command ? */
376 if ((id = (is_builtin(cmd[0]))) > -1) {
377 return run_builtin(id, cmd, usr, new_iostate);
378 }
379
380 /* Is it a module ? */
381 if ((id = (is_module(cmd[0]))) > -1) {
382 return run_module(id, cmd, new_iostate);
383 }
384
385 /* See what try_exec thinks of it */
386 return try_exec(cmd[0], cmd, new_iostate);
387}
388
389void get_input(cliuser_t *usr)
390{
391 char *str;
392 errno_t rc;
393
394 tinput_set_prompt(tinput, usr->prompt);
395
396 rc = tinput_read(tinput, &str);
397 if (rc == ENOENT) {
398 /* User requested exit */
399 cli_quit = 1;
400 putchar('\n');
401 return;
402 }
403
404 if (rc != EOK) {
405 /* Error in communication with console */
406 cli_quit = 1;
407 return;
408 }
409
410 /* Check for empty input. */
411 if (str_cmp(str, "") == 0) {
412 free(str);
413 return;
414 }
415
416 usr->line = str;
417 return;
418}
419
420int input_init(void)
421{
422 tinput = tinput_new();
423 if (tinput == NULL) {
424 printf("Failed to initialize input.\n");
425 return 1;
426 }
427
428 tinput_set_compl_ops(tinput, &compl_ops);
429
430 return 0;
431}
Note: See TracBrowser for help on using the repository browser.