diff --git a/client/tarantool/tc_buf.c b/client/tarantool/tc_buf.c index b4556bb796078d1b86f5b54a84b2146268e22c87..4826ed1361294867c03afb24155875d829d138a5 100644 --- a/client/tarantool/tc_buf.c +++ b/client/tarantool/tc_buf.c @@ -37,9 +37,10 @@ /* Strip trailing ws from (char*) */ size_t strip_end_ws(char *str) { size_t last = 0; - for (size_t i = 0; str[i] != 0; ++i) + for (size_t i = 0; str[i] != 0; ++i) { if (!isspace(str[i])) last = i + 1; + } str[last] = '\0'; return last; } @@ -49,16 +50,18 @@ int tc_buf(struct tc_buf *buf) { buf->size = TC_BUF_INIT_SIZE; buf->used = 0; buf->data = (char *)malloc(buf->size); - if (buf->data == NULL) + if (buf->data == NULL) { return -1; + } return 0; } /* Append len bytes of memory from str pointed memory */ int tc_buf_append(struct tc_buf *buf, void *str, size_t len) { if (buf->size - buf->used < len) { - if (buf->size < len) + if (buf->size < len) { buf->size = len; + } buf->size *= TC_BUF_MULTIPLIER; char *nd = (char *)realloc(buf->data, buf->size); if (nd == NULL) @@ -112,6 +115,26 @@ int tc_buf_str_append(struct tc_buf *buf, char *str, size_t len) { return 0; } +/* Remove last num symbols from STR */ +size_t tc_buf_str_delete(struct tc_buf *buf, size_t len) { + size_t ret = tc_buf_delete(buf, len + 1); /* Remove '\0' + len */ + if (tc_buf_append(buf, (void *)"\0", 1)) + return 0; + return ret; +} + +/* + * Make admin command from multiline command + * and delete delimiter (last num bytes) + */ +void tc_buf_cmdfy(struct tc_buf *buf, size_t num) { + tc_buf_str_delete(buf, num); + for (int i = 0; i < buf->used; ++i) { + if (buf->data[i] == '\n') + buf->data[i] = ' '; + } +} + /* Remove trailing ws from STR */ int tc_buf_str_stripws(struct tc_buf *buf) { if (buf->data) { diff --git a/client/tarantool/tc_buf.h b/client/tarantool/tc_buf.h index 52383666e7a7adf1cd869c1e96ff3a4e197d781d..3e528c20d7742a424aa9a4c417f81a5d8f7ddd39 100644 --- a/client/tarantool/tc_buf.h +++ b/client/tarantool/tc_buf.h @@ -40,12 +40,15 @@ struct tc_buf { int tc_buf(struct tc_buf *buf); void *tc_buf_realloc(void *data, size_t size); int tc_buf_append(struct tc_buf *buf, void *str, size_t len); -size_t tc_buf_delete(struct tc_buf *buf, size_t len); +size_t tc_buf_delete(struct tc_buf *buf, size_t num); int tc_buf_isempty(struct tc_buf *buf); void tc_buf_clear(struct tc_buf *buf); void tc_buf_free(struct tc_buf *buf); int tc_buf_str(struct tc_buf *buf); int tc_buf_str_append(struct tc_buf *buf, char *str, size_t len); +size_t tc_buf_str_delete(struct tc_buf *buf, size_t num); int tc_buf_str_stripws(struct tc_buf *buf); int tc_buf_str_isempty(struct tc_buf *buf); + +void tc_buf_cmdfy(struct tc_buf *buf, size_t num); diff --git a/client/tarantool/tc_cli.c b/client/tarantool/tc_cli.c index 90894ee554fe2f588923cd59a45ce024f9101f5e..53fda89668654c5590b5b94f9cf9352dcc7f1d25 100644 --- a/client/tarantool/tc_cli.c +++ b/client/tarantool/tc_cli.c @@ -124,13 +124,13 @@ tc_cmd_usage(void) { char usage[] = "---\n" - "- console client commands\n" - "- - help\n" - " - tee 'path'\n" - " - notee\n" - " - loadfile 'path'\n" - " - setopt key=val\n" - " - (possible pairs: delim[iter]=\'string\')\n" + "console client commands:\n" + " - help\n" + " - tee 'path'\n" + " - notee\n" + " - loadfile 'path'\n" + " - setopt key val\n" + " - (possible pairs: delimiter 'string')\n" "...\n"; tc_printf("%s", usage); } @@ -224,6 +224,17 @@ tc_cmd_loadfile(char *path, int *reconnect) return rc; } +static void replace_newline(char *cmd) { + int len = strlen(cmd); + int offset = 0; + for (int i = 0; i < len - 1; ++i) + if (cmd[i] == '\\' && cmd[i + 1] == 'n') + cmd[i++ - offset++] = '\n'; + else if (offset != 0) + cmd[i - offset] = cmd[i]; + cmd[len - offset] = '\0'; +} + static enum tc_cli_cmd_ret tc_cmd_try(char *cmd, size_t size, int *reconnect) { @@ -262,8 +273,10 @@ tc_cmd_try(char *cmd, size_t size, int *reconnect) case TC_SETOPT: switch (tnt_lex(&lex, &tk)) { case TC_SETOPT_DELIM: - if (tnt_lex(&lex, &tk) == '=' && - tnt_lex(&lex, &tk) == TNT_TK_STRING) { + if (tnt_lex(&lex, &tk) != '=') { + tnt_lex_push(&lex, tk); + } + if (tnt_lex(&lex, &tk) == TNT_TK_STRING) { if (!TNT_TK_S(tk)->size) { tc.opt.delim = ""; tc.opt.delim_len = 0; @@ -276,11 +289,12 @@ tc_cmd_try(char *cmd, size_t size, int *reconnect) strncpy(temp, (const char *)TNT_TK_S(tk)->data, TNT_TK_S(tk)->size + 1); + replace_newline(temp); tc.opt.delim = temp; tc.opt.delim_len = strlen(tc.opt.delim); } else { tc_printf("---\n"); - tc_printf(" - Expected: setopt delim[iter]='string'\n"); + tc_printf(" - Expected `setopt delimiter 'string'`\n"); tc_printf("---\n"); } break; @@ -380,9 +394,9 @@ static char* tc_cli_readline_pipe() { ungetc(c_t, stdin); wctomb(str + pos++, 0); break; - } - else + } else { pos += wctomb(str + pos, c); + } } if (pos == 1 && c == WEOF) { free(str); @@ -393,7 +407,6 @@ static char* tc_cli_readline_pipe() { static int check_delim(char* str, size_t len, size_t sep_len) { const char *sep = tc.opt.delim; - len = strip_end_ws(str); if (sep_len == 0) return 1; if (len < sep_len) @@ -402,8 +415,6 @@ static int check_delim(char* str, size_t len, size_t sep_len) { for (i = len; sep_len > 0; --sep_len, --i) if (str[i - 1] != sep[sep_len - 1]) return 0; - str[i] = '\0'; - len = strip_end_ws(str); return 1; } @@ -433,44 +444,44 @@ int tc_cli(void) if (isatty(STDIN_FILENO)) { snprintf(prompt_delim, sizeof(prompt_delim), "%*s> ", prompt_len, "-"); - part_cmd = readline(!tc_buf_str_isempty(&cmd) ? prompt_delim - : prompt); + part_cmd = readline(! tc_buf_str_isempty(&cmd) + ? prompt_delim + : prompt); } else { clearerr(stdin); part_cmd = tc_cli_readline_pipe(); } if (!part_cmd) break; - size_t part_cmd_len = strlen(part_cmd); - int delim_exists = check_delim(part_cmd, - part_cmd_len, - tc.opt.delim_len); if (tc_buf_str_append(&cmd, part_cmd, strlen(part_cmd))) tc_error(TC_REALLOCATION_ERROR, cmd.size); + int delim_exists = check_delim( cmd.data, + cmd.used - 1, + tc.opt.delim_len); + if (tc_buf_str_append(&cmd, "\n", 1)) /* Append '\n' to the STR */ + tc_error(TC_REALLOCATION_ERROR, + cmd.size); free(part_cmd); - if (!delim_exists && !feof(stdin)) { - if (tc_buf_str_append(&cmd, " ", 1)) - tc_error(TC_REALLOCATION_ERROR, - cmd.size); + if (!delim_exists && !feof(stdin)) /* If still without delimiter and not EOF */ continue; - } - tc_buf_str_stripws(&cmd); + tc_buf_str_delete(&cmd, 1); /* Delete last appended '\n' */ + if (isatty(STDIN_FILENO)) /* Enable history on readline use only */ + add_history(cmd.data); + tc_buf_cmdfy(&cmd, tc.opt.delim_len); /* Create admin cmd from STR */ if (delim_exists && tc_buf_str_isempty(&cmd)) - goto next; + goto next; /* If empty - don't send to server, it crashes */ tc_print_cmd2tee(cmd.used != 1 ? prompt_delim : prompt, cmd.data, cmd.used - 1); enum tc_cli_cmd_ret ret = tc_cli_cmd(cmd.data, cmd.used - 1); - if (isatty(STDIN_FILENO)) - add_history(cmd.data); next: tc_buf_clear(&cmd); if (ret == TC_CLI_EXIT || feof(stdin)) { tc_buf_free(&cmd); break; } -} + } /* updating history file */ write_history(history);