/* * Copyright (c) 2008 Tim Post * Copyright (c) 2011 Jiri Svoboda * Copyright (c) 2011 Martin Sucha * Copyright (c) 2018 Matthieu Riolo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "compl.h" #include "util.h" #include "scli.h" #include "input.h" #include "errors.h" #include "exec.h" #include "tok.h" extern volatile unsigned int cli_quit; /** Text input field. */ static tinput_t *tinput; /* Private helpers */ static int run_command(char **, cliuser_t *, iostate_t *); static void print_pipe_usage(void); /* * Tokenizes input from console, sees if the first word is a built-in, if so * invokes the built-in entry point (a[0]) passing all arguments in a[] to * the handler */ errno_t process_input(cliuser_t *usr) { token_t *tokens_buf = calloc(WORD_MAX, sizeof(token_t)); if (tokens_buf == NULL) return ENOMEM; token_t *tokens = tokens_buf; char *cmd[WORD_MAX]; errno_t rc = EOK; tokenizer_t tok; unsigned int i, pipe_count, processed_pipes; unsigned int pipe_pos[2]; char *redir_from = NULL; char *redir_to = NULL; if (usr->line == NULL) { free(tokens_buf); return EINVAL; } rc = tok_init(&tok, usr->line, tokens, WORD_MAX); if (rc != EOK) { goto finit; } size_t tokens_length; rc = tok_tokenize(&tok, &tokens_length); if (rc != EOK) { goto finit; } if (tokens_length > 0 && tokens[0].type == TOKTYPE_SPACE) { tokens++; tokens_length--; } if (tokens_length > 0 && tokens[tokens_length - 1].type == TOKTYPE_SPACE) { tokens_length--; } /* * Until full support for pipes is implemented, allow for a simple case: * [from |] command [| to ] * * First find the pipes and check that there are no more */ for (i = 0, pipe_count = 0; i < tokens_length; i++) { if (tokens[i].type == TOKTYPE_PIPE) { if (pipe_count >= 2) { print_pipe_usage(); rc = ENOTSUP; goto finit; } pipe_pos[pipe_count] = i; pipe_count++; } } unsigned int cmd_token_start = 0; unsigned int cmd_token_end = tokens_length; processed_pipes = 0; /* Check if the first part (from |) is present */ if (pipe_count > 0 && (pipe_pos[0] == 3 || pipe_pos[0] == 4) && str_cmp(tokens[0].text, "from") == 0) { /* Ignore the first three tokens (from, file, pipe) and set from */ redir_from = tokens[2].text; cmd_token_start = pipe_pos[0] + 1; processed_pipes++; } /* Check if the second part (| to ) is present */ if ((pipe_count - processed_pipes) > 0 && (pipe_pos[processed_pipes] == tokens_length - 4 || (pipe_pos[processed_pipes] == tokens_length - 5 && tokens[tokens_length - 4].type == TOKTYPE_SPACE)) && str_cmp(tokens[tokens_length - 3].text, "to") == 0) { /* Ignore the last three tokens (pipe, to, file) and set to */ redir_to = tokens[tokens_length - 1].text; cmd_token_end = pipe_pos[processed_pipes]; processed_pipes++; } if (processed_pipes != pipe_count) { print_pipe_usage(); rc = ENOTSUP; goto finit; } /* Convert tokens of the command to string array */ unsigned int cmd_pos = 0; for (i = cmd_token_start; i < cmd_token_end; i++) { if (tokens[i].type != TOKTYPE_SPACE) { cmd[cmd_pos++] = tokens[i].text; } } cmd[cmd_pos++] = NULL; if (cmd[0] == NULL) { print_pipe_usage(); rc = ENOTSUP; goto finit; } /* test if the passed cmd is an alias */ odlink_t* alias_link = odict_find_eq(&alias_dict, (void*)cmd[0], NULL); if (alias_link != NULL) { alias_t* data = odict_get_instance(alias_link, alias_t, odict); char* oldLine = usr->line; const size_t input_length = str_size(usr->line) - str_size(cmd[0]) + str_size(data->value) + 1; usr->line = (char*)malloc(INPUT_MAX * sizeof(char)); usr->line[0] = '\0'; unsigned int cmd_replace_index = cmd_token_start; for (i = 0; i < tokens_length; i++) { if (i == cmd_replace_index) { //if there is a pipe symbol than cmd_token_start will point at the SPACE after the pipe symbol if (tokens[i].type == TOKTYPE_SPACE) { cmd_replace_index++; str_append(usr->line, input_length, tokens[i].text); continue; } str_append(usr->line, input_length, data->value); } else { str_append(usr->line, input_length, tokens[i].text); } } //reprocess input after string replace rc = process_input(usr); usr->line = oldLine; goto finit; } iostate_t new_iostate = { .stdin = stdin, .stdout = stdout, .stderr = stderr }; FILE *from = NULL; FILE *to = NULL; if (redir_from) { from = fopen(redir_from, "r"); if (from == NULL) { printf("Cannot open file %s\n", redir_from); rc = errno; goto finit_with_files; } new_iostate.stdin = from; } if (redir_to) { to = fopen(redir_to, "w"); if (to == NULL) { printf("Cannot open file %s\n", redir_to); rc = errno; goto finit_with_files; } new_iostate.stdout = to; } if (run_command(cmd, usr, &new_iostate) == 0) { rc = EOK; } else { rc = EINVAL; } finit_with_files: if (from != NULL) { fclose(from); } if (to != NULL) { fclose(to); } finit: if (NULL != usr->line) { free(usr->line); usr->line = (char *) NULL; } tok_fini(&tok); free(tokens_buf); return rc; } void print_pipe_usage(void) { printf("Invalid syntax!\n"); printf("Usage of redirection (pipes in the future):\n"); printf("from filename | command ...\n"); printf("from filename | command ... | to filename\n"); printf("command ... | to filename\n"); } int run_command(char **cmd, cliuser_t *usr, iostate_t *new_iostate) { int id = 0; /* We have rubbish */ if (NULL == cmd[0]) { return CL_ENOENT; } /* Is it a builtin command ? */ if ((id = (is_builtin(cmd[0]))) > -1) { return run_builtin(id, cmd, usr, new_iostate); } /* Is it a module ? */ if ((id = (is_module(cmd[0]))) > -1) { return run_module(id, cmd, new_iostate); } /* See what try_exec thinks of it */ return try_exec(cmd[0], cmd, new_iostate); } void get_input(cliuser_t *usr) { char *str; errno_t rc; tinput_set_prompt(tinput, usr->prompt); rc = tinput_read(tinput, &str); if (rc == ENOENT) { /* User requested exit */ cli_quit = 1; putchar('\n'); return; } if (rc != EOK) { /* Error in communication with console */ cli_quit = 1; return; } /* Check for empty input. */ if (str_cmp(str, "") == 0) { free(str); return; } usr->line = str; return; } int input_init(void) { tinput = tinput_new(); if (tinput == NULL) { printf("Failed to initialize input.\n"); return 1; } tinput_set_compl_ops(tinput, &compl_ops); return 0; }