source: mainline/uspace/app/bdsh/input.c@ 313ac8e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 313ac8e was e62f8e3, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

Adding a maximal loop counter for nested aliases

This commit was requested by a a reviewer. The old
version allowed to have endless nested aliases. This
commit changes this allowing only 20 nested aliases.

  • Property mode set to 100644
File size: 9.4 KB
RevLine 
[36ab7c7]1/*
2 * Copyright (c) 2008 Tim Post
[9be9c4d]3 * Copyright (c) 2011 Jiri Svoboda
[b2727f18]4 * Copyright (c) 2011 Martin Sucha
[63087f1d]5 * Copyright (c) 2018 Matthieu Riolo
[216d6fc]6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
[36ab7c7]9 * modification, are permitted provided that the following conditions
10 * are met:
[216d6fc]11 *
[36ab7c7]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.
[216d6fc]19 *
[36ab7c7]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.
[216d6fc]30 */
31
32#include <stdio.h>
33#include <stdlib.h>
[19f857a]34#include <str.h>
[73878c1]35#include <io/console.h>
36#include <io/keycode.h>
37#include <io/style.h>
[7e0cb78]38#include <io/color.h>
[3bf907a]39#include <vfs/vfs.h>
[371a012]40#include <clipboard.h>
[f1b37d6]41#include <macros.h>
[6071a8f]42#include <errno.h>
[da2bd08]43#include <assert.h>
[3e6a98c5]44#include <stdbool.h>
[36a75a2]45#include <tinput.h>
[55e35a22]46#include <adt/odict.h>
[966f753]47#include <adt/list.h>
[216d6fc]48
49#include "config.h"
[9be9c4d]50#include "compl.h"
[216d6fc]51#include "util.h"
52#include "scli.h"
53#include "input.h"
54#include "errors.h"
55#include "exec.h"
[6939edb]56#include "tok.h"
[216d6fc]57
[5db9084]58extern volatile unsigned int cli_quit;
59
[ed372da]60/** Text input field. */
[36a75a2]61static tinput_t *tinput;
[88944695]62
[6939edb]63/* Private helpers */
[6ea9a1d]64static int run_command(char **, cliuser_t *, iostate_t *);
[ae45201]65static void print_pipe_usage(void);
[659e9473]66
[966f753]67typedef struct {
68 link_t alias_hup_link;
69 alias_t *alias;
70} alias_hup_t;
71
72static bool find_alias_hup(alias_t *alias, list_t *alias_hups)
73{
74 list_foreach(*alias_hups, alias_hup_link, alias_hup_t, link) {
75 if (alias == link->alias) {
76 return true;
77 }
78 }
79
80 return false;
81}
82
[7c3fb9b]83/*
84 * Tokenizes input from console, sees if the first word is a built-in, if so
[216d6fc]85 * invokes the built-in entry point (a[0]) passing all arguments in a[] to
[7c3fb9b]86 * the handler
87 */
[e62f8e3]88static errno_t process_input_nohup(cliuser_t *usr, list_t *alias_hups, size_t count_executed_hups)
[216d6fc]89{
[e62f8e3]90 if (count_executed_hups >= HUBS_MAX) {
91 cli_error(CL_EFAIL, "%s: maximal alias hubs reached\n", PACKAGE_NAME);
92 return ELIMIT;
93 }
94
[01e397ac]95 token_t *tokens_buf = calloc(WORD_MAX, sizeof(token_t));
96 if (tokens_buf == NULL)
[b9ae539]97 return ENOMEM;
[01e397ac]98 token_t *tokens = tokens_buf;
[a35b458]99
[216d6fc]100 char *cmd[WORD_MAX];
[b7fd2a0]101 errno_t rc = EOK;
[6939edb]102 tokenizer_t tok;
[0662451]103 unsigned int i, pipe_count, processed_pipes;
104 unsigned int pipe_pos[2];
[ae45201]105 char *redir_from = NULL;
106 char *redir_to = NULL;
[216d6fc]107
[b48d046]108 if (usr->line == NULL) {
[01e397ac]109 free(tokens_buf);
[1569a9b]110 return EINVAL;
[b9ae539]111 }
[216d6fc]112
[0662451]113 rc = tok_init(&tok, usr->line, tokens, WORD_MAX);
[6939edb]114 if (rc != EOK) {
115 goto finit;
[216d6fc]116 }
[a35b458]117
[0662451]118 size_t tokens_length;
119 rc = tok_tokenize(&tok, &tokens_length);
[6939edb]120 if (rc != EOK) {
121 goto finit;
122 }
[a35b458]123
[0662451]124 if (tokens_length > 0 && tokens[0].type == TOKTYPE_SPACE) {
125 tokens++;
126 tokens_length--;
127 }
[a35b458]128
[1433ecda]129 if (tokens_length > 0 && tokens[tokens_length - 1].type == TOKTYPE_SPACE) {
[0662451]130 tokens_length--;
131 }
[a35b458]132
[7c3fb9b]133 /*
134 * Until full support for pipes is implemented, allow for a simple case:
[ae45201]135 * [from <file> |] command [| to <file>]
[1b20da0]136 *
[ae45201]137 * First find the pipes and check that there are no more
138 */
[0662451]139 for (i = 0, pipe_count = 0; i < tokens_length; i++) {
140 if (tokens[i].type == TOKTYPE_PIPE) {
[ae45201]141 if (pipe_count >= 2) {
142 print_pipe_usage();
143 rc = ENOTSUP;
144 goto finit;
145 }
146 pipe_pos[pipe_count] = i;
147 pipe_count++;
148 }
149 }
[a35b458]150
[0662451]151 unsigned int cmd_token_start = 0;
152 unsigned int cmd_token_end = tokens_length;
[a35b458]153
[ae45201]154 processed_pipes = 0;
[a35b458]155
[ae45201]156 /* Check if the first part (from <file> |) is present */
[0662451]157 if (pipe_count > 0 && (pipe_pos[0] == 3 || pipe_pos[0] == 4) && str_cmp(tokens[0].text, "from") == 0) {
[ae45201]158 /* Ignore the first three tokens (from, file, pipe) and set from */
[0662451]159 redir_from = tokens[2].text;
[1433ecda]160 cmd_token_start = pipe_pos[0] + 1;
[ae45201]161 processed_pipes++;
162 }
[a35b458]163
[ae45201]164 /* Check if the second part (| to <file>) is present */
165 if ((pipe_count - processed_pipes) > 0 &&
[0662451]166 (pipe_pos[processed_pipes] == tokens_length - 4 ||
167 (pipe_pos[processed_pipes] == tokens_length - 5 &&
[1433ecda]168 tokens[tokens_length - 4].type == TOKTYPE_SPACE)) &&
169 str_cmp(tokens[tokens_length - 3].text, "to") == 0) {
[ae45201]170 /* Ignore the last three tokens (pipe, to, file) and set to */
[1433ecda]171 redir_to = tokens[tokens_length - 1].text;
[0662451]172 cmd_token_end = pipe_pos[processed_pipes];
[ae45201]173 processed_pipes++;
174 }
[a35b458]175
[ae45201]176 if (processed_pipes != pipe_count) {
177 print_pipe_usage();
178 rc = ENOTSUP;
179 goto finit;
180 }
[a35b458]181
[0662451]182 /* Convert tokens of the command to string array */
183 unsigned int cmd_pos = 0;
184 for (i = cmd_token_start; i < cmd_token_end; i++) {
185 if (tokens[i].type != TOKTYPE_SPACE) {
186 cmd[cmd_pos++] = tokens[i].text;
187 }
188 }
189 cmd[cmd_pos++] = NULL;
[a35b458]190
[0662451]191 if (cmd[0] == NULL) {
[ae45201]192 print_pipe_usage();
193 rc = ENOTSUP;
194 goto finit;
195 }
[a35b458]196
[55e35a22]197 /* test if the passed cmd is an alias */
[a02aa5c]198 odlink_t *alias_link = odict_find_eq(&alias_dict, (void *)cmd[0], NULL);
[55e35a22]199 if (alias_link != NULL) {
[a02aa5c]200 alias_t *data = odict_get_instance(alias_link, alias_t, odict);
[506cbf0]201 /* check if the alias already has been resolved once */
[966f753]202 if (!find_alias_hup(data, alias_hups)) {
[25cfc3d]203 alias_hup_t *hup = (alias_hup_t *)calloc(1, sizeof(alias_hup_t));
[e62f8e3]204 if (hup == NULL) {
205 cli_error(CL_EFAIL, "%s: cannot allocate alias structure\n", PACKAGE_NAME);
206 rc = ENOMEM;
207 goto finit;
208 }
209
[966f753]210 hup->alias = data;
211 list_append(&hup->alias_hup_link, alias_hups);
212
213 char *oldLine = usr->line;
214 const size_t input_length = str_size(usr->line) - str_size(cmd[0]) + str_size(data->value) + 1;
[e62f8e3]215 usr->line = (char *)malloc(input_length);
216 if (usr->line == NULL) {
217 cli_error(CL_EFAIL, "%s: cannot allocate input structure\n", PACKAGE_NAME);
218 rc = ENOMEM;
219 goto finit;
220 }
221
[966f753]222 usr->line[0] = '\0';
223
224 unsigned int cmd_replace_index = cmd_token_start;
225 for (i = 0; i < tokens_length; i++) {
226 if (i == cmd_replace_index) {
[506cbf0]227 /* if there is a pipe symbol than cmd_token_start will point at the SPACE after the pipe symbol */
[966f753]228 if (tokens[i].type == TOKTYPE_SPACE) {
229 cmd_replace_index++;
230 str_append(usr->line, input_length, tokens[i].text);
231 continue;
232 }
233
234 str_append(usr->line, input_length, data->value);
235 } else {
[55e35a22]236 str_append(usr->line, input_length, tokens[i].text);
237 }
238 }
[4bf08aa5]239
[506cbf0]240 /* reprocess input after string replace */
[e62f8e3]241 rc = process_input_nohup(usr, alias_hups, count_executed_hups + 1);
[966f753]242 usr->line = oldLine;
243 goto finit;
244 }
[4bf08aa5]245 }
246
[6ea9a1d]247 iostate_t new_iostate = {
248 .stdin = stdin,
249 .stdout = stdout,
250 .stderr = stderr
251 };
[a35b458]252
[6ea9a1d]253 FILE *from = NULL;
254 FILE *to = NULL;
[a35b458]255
[ae45201]256 if (redir_from) {
[6ea9a1d]257 from = fopen(redir_from, "r");
[ae45201]258 if (from == NULL) {
259 printf("Cannot open file %s\n", redir_from);
260 rc = errno;
[6ea9a1d]261 goto finit_with_files;
[ae45201]262 }
[6ea9a1d]263 new_iostate.stdin = from;
[ae45201]264 }
[a35b458]265
[ae45201]266 if (redir_to) {
[6ea9a1d]267 to = fopen(redir_to, "w");
[ae45201]268 if (to == NULL) {
269 printf("Cannot open file %s\n", redir_to);
270 rc = errno;
[6ea9a1d]271 goto finit_with_files;
[ae45201]272 }
[6ea9a1d]273 new_iostate.stdout = to;
[ae45201]274 }
[10f4b47c]275
[1569a9b]276 if (run_command(cmd, usr, &new_iostate) == 0) {
277 rc = EOK;
278 } else {
279 rc = EINVAL;
280 }
[a35b458]281
[6ea9a1d]282finit_with_files:
283 if (from != NULL) {
284 fclose(from);
285 }
286 if (to != NULL) {
287 fclose(to);
288 }
[a35b458]289
[6939edb]290finit:
[216d6fc]291 if (NULL != usr->line) {
292 free(usr->line);
293 usr->line = (char *) NULL;
294 }
[6939edb]295 tok_fini(&tok);
[01e397ac]296 free(tokens_buf);
[216d6fc]297
298 return rc;
299}
300
[966f753]301errno_t process_input(cliuser_t *usr)
302{
303 list_t alias_hups;
304 list_initialize(&alias_hups);
305
[e62f8e3]306 errno_t rc = process_input_nohup(usr, &alias_hups, 0);
[966f753]307
308 list_foreach_safe(alias_hups, cur_link, next_link) {
309 alias_hup_t *cur_item = list_get_instance(cur_link, alias_hup_t, alias_hup_link);
310 free(cur_item);
311 }
312
313 return rc;
314}
315
[193d280c]316void print_pipe_usage(void)
[ae45201]317{
318 printf("Invalid syntax!\n");
319 printf("Usage of redirection (pipes in the future):\n");
320 printf("from filename | command ...\n");
321 printf("from filename | command ... | to filename\n");
322 printf("command ... | to filename\n");
[a35b458]323
[ae45201]324}
325
[6ea9a1d]326int run_command(char **cmd, cliuser_t *usr, iostate_t *new_iostate)
[659e9473]327{
328 int id = 0;
[a35b458]329
[659e9473]330 /* We have rubbish */
331 if (NULL == cmd[0]) {
332 return CL_ENOENT;
333 }
[a35b458]334
[659e9473]335 /* Is it a builtin command ? */
336 if ((id = (is_builtin(cmd[0]))) > -1) {
[6ea9a1d]337 return run_builtin(id, cmd, usr, new_iostate);
[659e9473]338 }
[a35b458]339
[659e9473]340 /* Is it a module ? */
341 if ((id = (is_module(cmd[0]))) > -1) {
[6ea9a1d]342 return run_module(id, cmd, new_iostate);
[659e9473]343 }
344
345 /* See what try_exec thinks of it */
[6ea9a1d]346 return try_exec(cmd[0], cmd, new_iostate);
[659e9473]347}
348
[216d6fc]349void get_input(cliuser_t *usr)
350{
[19528516]351 char *str;
[b7fd2a0]352 errno_t rc;
[a35b458]353
[9be9c4d]354 tinput_set_prompt(tinput, usr->prompt);
[9805cde]355
[5db9084]356 rc = tinput_read(tinput, &str);
357 if (rc == ENOENT) {
358 /* User requested exit */
359 cli_quit = 1;
360 putchar('\n');
361 return;
362 }
363
364 if (rc != EOK) {
365 /* Error in communication with console */
[6d5e378]366 cli_quit = 1;
[5db9084]367 return;
368 }
[19528516]369
370 /* Check for empty input. */
371 if (str_cmp(str, "") == 0) {
372 free(str);
[216d6fc]373 return;
[19528516]374 }
[216d6fc]375
[19528516]376 usr->line = str;
[216d6fc]377 return;
378}
[da2bd08]379
[36a75a2]380int input_init(void)
[da2bd08]381{
[36a75a2]382 tinput = tinput_new();
383 if (tinput == NULL) {
384 printf("Failed to initialize input.\n");
385 return 1;
386 }
387
[9be9c4d]388 tinput_set_compl_ops(tinput, &compl_ops);
389
[36a75a2]390 return 0;
[da2bd08]391}
Note: See TracBrowser for help on using the repository browser.