[TD-13558]<feature>: taos shell refactor for 3.0 (#10871)
* [TD-13558]<feature>: taos shell refactor add taosTools as submodule * add tools/taos-tools * add more client interface for taosTools compile * update taos-tools * update taos-tools * refactor shell
This commit is contained in:
parent
990a6049bf
commit
e0dca33a98
|
@ -28,7 +28,6 @@ extern "C" {
|
||||||
#define tcgetattr TCGETATTR_FUNC_TAOS_FORBID
|
#define tcgetattr TCGETATTR_FUNC_TAOS_FORBID
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int32_t taosSystem(const char *cmd, char *buf, int32_t bufSize);
|
|
||||||
void* taosLoadDll(const char* filename);
|
void* taosLoadDll(const char* filename);
|
||||||
void* taosLoadSym(void* handle, char* name);
|
void* taosLoadSym(void* handle, char* name);
|
||||||
void taosCloseDll(void* handle);
|
void taosCloseDll(void* handle);
|
||||||
|
|
|
@ -29,61 +29,6 @@
|
||||||
struct termios oldtio;
|
struct termios oldtio;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int32_t taosSystem(const char *cmd, char *buf, int32_t bufSize) {
|
|
||||||
#if defined(WINDOWS)
|
|
||||||
FILE *fp;
|
|
||||||
if (cmd == NULL) {
|
|
||||||
// printf("taosSystem cmd is NULL!");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((fp = _popen(cmd, "r")) == NULL) {
|
|
||||||
// printf("popen cmd:%s error: %s", cmd, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
while (fgets(buf, bufSize, fp)) {
|
|
||||||
// printf("popen result:%s", buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_pclose(fp)) {
|
|
||||||
// printf("close popen file pointer fp error!");
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
// printf("popen res is :%d", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#elif defined(_TD_DARWIN_64)
|
|
||||||
printf("no support funtion");
|
|
||||||
return -1;
|
|
||||||
#else
|
|
||||||
FILE *fp;
|
|
||||||
int32_t res;
|
|
||||||
if (cmd == NULL) {
|
|
||||||
// printf("taosSystem cmd is NULL!");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((fp = popen(cmd, "r")) == NULL) {
|
|
||||||
// printf("popen cmd:%s error: %s", cmd, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
while (fgets(buf, bufSize, fp)) {
|
|
||||||
// printf("popen result:%s", buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((res = pclose(fp)) == -1) {
|
|
||||||
// printf("close popen file pointer fp error!");
|
|
||||||
} else {
|
|
||||||
// printf("popen res is :%d", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void* taosLoadDll(const char* filename) {
|
void* taosLoadDll(const char* filename) {
|
||||||
#if defined(WINDOWS)
|
#if defined(WINDOWS)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -103,7 +48,7 @@ void* taosLoadDll(const char* filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void* taosLoadSym(void* handle, char* name) {
|
void* taosLoadSym(void* handle, char* name) {
|
||||||
#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
|
#if defined(WINDOWS)
|
||||||
return NULL;
|
return NULL;
|
||||||
#elif defined(_TD_DARWIN_64)
|
#elif defined(_TD_DARWIN_64)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -123,7 +68,7 @@ void* taosLoadSym(void* handle, char* name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void taosCloseDll(void* handle) {
|
void taosCloseDll(void* handle) {
|
||||||
#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
|
#if defined(WINDOWS)
|
||||||
return;
|
return;
|
||||||
#elif defined(_TD_DARWIN_64)
|
#elif defined(_TD_DARWIN_64)
|
||||||
return;
|
return;
|
||||||
|
@ -135,7 +80,7 @@ void taosCloseDll(void* handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int taosSetConsoleEcho(bool on) {
|
int taosSetConsoleEcho(bool on) {
|
||||||
#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
|
#if defined(WINDOWS)
|
||||||
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
DWORD mode = 0;
|
DWORD mode = 0;
|
||||||
GetConsoleMode(hStdin, &mode);
|
GetConsoleMode(hStdin, &mode);
|
||||||
|
@ -146,28 +91,6 @@ int taosSetConsoleEcho(bool on) {
|
||||||
}
|
}
|
||||||
SetConsoleMode(hStdin, mode);
|
SetConsoleMode(hStdin, mode);
|
||||||
|
|
||||||
return 0;
|
|
||||||
#elif defined(_TD_DARWIN_64)
|
|
||||||
#define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL)
|
|
||||||
int err;
|
|
||||||
struct termios term;
|
|
||||||
|
|
||||||
if (tcgetattr(STDIN_FILENO, &term) == -1) {
|
|
||||||
perror("Cannot get the attribution of the terminal");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (on)
|
|
||||||
term.c_lflag |= ECHOFLAGS;
|
|
||||||
else
|
|
||||||
term.c_lflag &= ~ECHOFLAGS;
|
|
||||||
|
|
||||||
err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
|
||||||
if (err == -1 && err == EINTR) {
|
|
||||||
perror("Cannot set the attribution of the terminal");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
#define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL)
|
#define ECHOFLAGS (ECHO | ECHOE | ECHOK | ECHONL)
|
||||||
|
@ -186,7 +109,7 @@ int taosSetConsoleEcho(bool on) {
|
||||||
|
|
||||||
err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
err = tcsetattr(STDIN_FILENO, TCSAFLUSH, &term);
|
||||||
if (err == -1 || err == EINTR) {
|
if (err == -1 || err == EINTR) {
|
||||||
//printf("Cannot set the attribution of the terminal");
|
printf("Cannot set the attribution of the terminal");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,35 +118,8 @@ int taosSetConsoleEcho(bool on) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTerminalMode() {
|
void setTerminalMode() {
|
||||||
#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
|
#if defined(WINDOWS)
|
||||||
|
|
||||||
#elif defined(_TD_DARWIN_64)
|
|
||||||
struct termios newtio;
|
|
||||||
|
|
||||||
/* if (atexit() != 0) { */
|
|
||||||
/* fprintf(stderr, "Error register exit function!\n"); */
|
|
||||||
/* exit(EXIT_FAILURE); */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
memcpy(&newtio, &oldtio, sizeof(oldtio));
|
|
||||||
|
|
||||||
// Set new terminal attributes.
|
|
||||||
newtio.c_iflag &= ~(IXON | IXOFF | ICRNL | INLCR | IGNCR | IMAXBEL | ISTRIP);
|
|
||||||
newtio.c_iflag |= IGNBRK;
|
|
||||||
|
|
||||||
// newtio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
|
|
||||||
newtio.c_oflag |= OPOST;
|
|
||||||
newtio.c_oflag |= ONLCR;
|
|
||||||
newtio.c_oflag &= ~(OCRNL | ONLRET);
|
|
||||||
|
|
||||||
newtio.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHOE | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | ISIG);
|
|
||||||
newtio.c_cc[VMIN] = 1;
|
|
||||||
newtio.c_cc[VTIME] = 0;
|
|
||||||
|
|
||||||
if (tcsetattr(0, TCSANOW, &newtio) != 0) {
|
|
||||||
fprintf(stderr, "Fail to set terminal properties!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
struct termios newtio;
|
struct termios newtio;
|
||||||
|
|
||||||
|
@ -256,19 +152,7 @@ void setTerminalMode() {
|
||||||
|
|
||||||
int32_t getOldTerminalMode() {
|
int32_t getOldTerminalMode() {
|
||||||
#if defined(WINDOWS)
|
#if defined(WINDOWS)
|
||||||
|
|
||||||
#elif defined(_TD_DARWIN_64)
|
|
||||||
/* Make sure stdin is a terminal. */
|
|
||||||
if (!isatty(STDIN_FILENO)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the parameter of current terminal
|
|
||||||
if (tcgetattr(0, &oldtio) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
#else
|
#else
|
||||||
/* Make sure stdin is a terminal. */
|
/* Make sure stdin is a terminal. */
|
||||||
if (!isatty(STDIN_FILENO)) {
|
if (!isatty(STDIN_FILENO)) {
|
||||||
|
@ -285,13 +169,8 @@ int32_t getOldTerminalMode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetTerminalMode() {
|
void resetTerminalMode() {
|
||||||
#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
|
#if defined(WINDOWS)
|
||||||
|
|
||||||
#elif defined(_TD_DARWIN_64)
|
|
||||||
if (tcsetattr(0, TCSANOW, &oldtio) != 0) {
|
|
||||||
fprintf(stderr, "Fail to reset the terminal properties!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (tcsetattr(0, TCSANOW, &oldtio) != 0) {
|
if (tcsetattr(0, TCSANOW, &oldtio) != 0) {
|
||||||
fprintf(stderr, "Fail to reset the terminal properties!\n");
|
fprintf(stderr, "Fail to reset the terminal properties!\n");
|
||||||
|
|
|
@ -1,529 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can use, redistribute, and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License, version 3
|
|
||||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define __USE_XOPEN
|
|
||||||
#include "os.h"
|
|
||||||
#include "tglobal.h"
|
|
||||||
#include "shell.h"
|
|
||||||
#include "shellCommand.h"
|
|
||||||
#include "tbase64.h"
|
|
||||||
#include "tlog.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#include <wordexp.h>
|
|
||||||
#include <argp.h>
|
|
||||||
#include <termio.h>
|
|
||||||
|
|
||||||
#define OPT_ABORT 1 /* <20>Cabort */
|
|
||||||
|
|
||||||
int indicator = 1;
|
|
||||||
struct termios oldtio;
|
|
||||||
|
|
||||||
void insertChar(Command *cmd, char *c, int size);
|
|
||||||
const char *argp_program_version = version;
|
|
||||||
const char *argp_program_bug_address = "<support@taosdata.com>";
|
|
||||||
static char doc[] = "";
|
|
||||||
static char args_doc[] = "";
|
|
||||||
static struct argp_option options[] = {
|
|
||||||
{"host", 'h', "HOST", 0, "TDengine server FQDN to connect. The default host is localhost."},
|
|
||||||
{"password", 'p', 0, 0, "The password to use when connecting to the server."},
|
|
||||||
{"port", 'P', "PORT", 0, "The TCP/IP port number to use for the connection."},
|
|
||||||
{"user", 'u', "USER", 0, "The user name to use when connecting to the server."},
|
|
||||||
{"auth", 'A', "Auth", 0, "The auth string to use when connecting to the server."},
|
|
||||||
{"config-dir", 'c', "CONFIG_DIR", 0, "Configuration directory."},
|
|
||||||
{"dump-config", 'C', 0, 0, "Dump configuration."},
|
|
||||||
{"commands", 's', "COMMANDS", 0, "Commands to run without enter the shell."},
|
|
||||||
{"raw-time", 'r', 0, 0, "Output time as uint64_t."},
|
|
||||||
{"file", 'f', "FILE", 0, "Script to run without enter the shell."},
|
|
||||||
{"directory", 'D', "DIRECTORY", 0, "Use multi-thread to import all SQL files in the directory separately."},
|
|
||||||
{"thread", 'T', "THREADNUM", 0, "Number of threads when using multi-thread to import data."},
|
|
||||||
{"check", 'k', "CHECK", 0, "Check tables."},
|
|
||||||
{"database", 'd', "DATABASE", 0, "Database to use when connecting to the server."},
|
|
||||||
{"timezone", 'z', "TIMEZONE", 0, "Time zone of the shell, default is local."},
|
|
||||||
{"netrole", 'n', "NETROLE", 0, "Net role when network connectivity test, default is startup, options: client|server|rpc|startup|sync|speen|fqdn."},
|
|
||||||
{"pktlen", 'l', "PKTLEN", 0, "Packet length used for net test, default is 1000 bytes."},
|
|
||||||
{"pktnum", 'N', "PKTNUM", 0, "Packet numbers used for net test, default is 100."},
|
|
||||||
{"pkttype", 'S', "PKTTYPE", 0, "Packet type used for net test, default is TCP."},
|
|
||||||
{0}};
|
|
||||||
|
|
||||||
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|
||||||
/* Get the input argument from argp_parse, which we
|
|
||||||
know is a pointer to our arguments structure. */
|
|
||||||
SShellArguments *arguments = state->input;
|
|
||||||
wordexp_t full_path;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case 'h':
|
|
||||||
arguments->host = arg;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
if (arg) {
|
|
||||||
arguments->port = atoi(arg);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid port\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
arguments->timezone = arg;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
arguments->user = arg;
|
|
||||||
break;
|
|
||||||
case 'A':
|
|
||||||
arguments->auth = arg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
if (wordexp(arg, &full_path, 0) != 0) {
|
|
||||||
fprintf(stderr, "Invalid path %s\n", arg);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (strlen(full_path.we_wordv[0]) >= TSDB_FILENAME_LEN) {
|
|
||||||
fprintf(stderr, "config file path: %s overflow max len %d\n", full_path.we_wordv[0], TSDB_FILENAME_LEN - 1);
|
|
||||||
wordfree(&full_path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
tstrncpy(configDir, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
|
||||||
wordfree(&full_path);
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
arguments->dump_config = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
arguments->commands = arg;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
arguments->is_raw_time = true;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
if ((0 == strlen(arg)) || (wordexp(arg, &full_path, 0) != 0)) {
|
|
||||||
fprintf(stderr, "Invalid path %s\n", arg);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
tstrncpy(arguments->file, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
|
||||||
wordfree(&full_path);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
if (wordexp(arg, &full_path, 0) != 0) {
|
|
||||||
fprintf(stderr, "Invalid path %s\n", arg);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
tstrncpy(arguments->dir, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
|
||||||
wordfree(&full_path);
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
if (arg) {
|
|
||||||
arguments->threadNum = atoi(arg);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid number of threads\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
arguments->check = atoi(arg);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
arguments->database = arg;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
arguments->netTestRole = arg;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (arg) {
|
|
||||||
arguments->pktLen = atoi(arg);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid packet length\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'N':
|
|
||||||
if (arg) {
|
|
||||||
arguments->pktNum = atoi(arg);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid packet number\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
arguments->pktType = arg;
|
|
||||||
break;
|
|
||||||
case OPT_ABORT:
|
|
||||||
arguments->abort = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ARGP_ERR_UNKNOWN;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Our argp parser. */
|
|
||||||
static struct argp argp = {options, parse_opt, args_doc, doc};
|
|
||||||
|
|
||||||
char LINUXCLIENT_VERSION[] = "Welcome to the TDengine shell from %s, Client Version:%s\n"
|
|
||||||
"Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n";
|
|
||||||
char g_password[SHELL_MAX_PASSWORD_LEN];
|
|
||||||
|
|
||||||
static void parse_args(
|
|
||||||
int argc, char *argv[], SShellArguments *arguments) {
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if ((strncmp(argv[i], "-p", 2) == 0)
|
|
||||||
|| (strncmp(argv[i], "--password", 10) == 0)) {
|
|
||||||
printf(LINUXCLIENT_VERSION, tsOsName, taos_get_client_info());
|
|
||||||
if ((strlen(argv[i]) == 2)
|
|
||||||
|| (strncmp(argv[i], "--password", 10) == 0)) {
|
|
||||||
printf("Enter password: ");
|
|
||||||
taosSetConsoleEcho(false);
|
|
||||||
if (scanf("%20s", g_password) > 1) {
|
|
||||||
fprintf(stderr, "password reading error\n");
|
|
||||||
}
|
|
||||||
taosSetConsoleEcho(true);
|
|
||||||
if (EOF == getchar()) {
|
|
||||||
fprintf(stderr, "getchar() return EOF\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tstrncpy(g_password, (char *)(argv[i] + 2), SHELL_MAX_PASSWORD_LEN);
|
|
||||||
strcpy(argv[i], "-p");
|
|
||||||
}
|
|
||||||
arguments->password = g_password;
|
|
||||||
arguments->is_use_passwd = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void shellParseArgument(int argc, char *argv[], SShellArguments *arguments) {
|
|
||||||
static char verType[32] = {0};
|
|
||||||
sprintf(verType, "version: %s\n", version);
|
|
||||||
|
|
||||||
argp_program_version = verType;
|
|
||||||
|
|
||||||
if (argc > 1) {
|
|
||||||
parse_args(argc, argv, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
argp_parse(&argp, argc, argv, 0, 0, arguments);
|
|
||||||
if (arguments->abort) {
|
|
||||||
#ifndef _ALPINE
|
|
||||||
#if 0
|
|
||||||
error(10, 0, "ABORTED");
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t shellReadCommand(TAOS *con, char *command) {
|
|
||||||
unsigned hist_counter = history.hend;
|
|
||||||
char utf8_array[10] = "\0";
|
|
||||||
Command cmd;
|
|
||||||
memset(&cmd, 0, sizeof(cmd));
|
|
||||||
cmd.buffer = (char *)calloc(1, MAX_COMMAND_SIZE);
|
|
||||||
cmd.command = (char *)calloc(1, MAX_COMMAND_SIZE);
|
|
||||||
showOnScreen(&cmd);
|
|
||||||
|
|
||||||
// Read input.
|
|
||||||
char c;
|
|
||||||
while (1) {
|
|
||||||
c = (char)getchar(); // getchar() return an 'int' value
|
|
||||||
|
|
||||||
if (c == EOF) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 0) { // For UTF-8
|
|
||||||
int count = countPrefixOnes(c);
|
|
||||||
utf8_array[0] = c;
|
|
||||||
for (int k = 1; k < count; k++) {
|
|
||||||
c = (char)getchar();
|
|
||||||
utf8_array[k] = c;
|
|
||||||
}
|
|
||||||
insertChar(&cmd, utf8_array, count);
|
|
||||||
} else if (c < '\033') {
|
|
||||||
// Ctrl keys. TODO: Implement ctrl combinations
|
|
||||||
switch (c) {
|
|
||||||
case 1: // ctrl A
|
|
||||||
positionCursorHome(&cmd);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
printf("\n");
|
|
||||||
resetCommand(&cmd, "");
|
|
||||||
kill(0, SIGINT);
|
|
||||||
break;
|
|
||||||
case 4: // EOF or Ctrl+D
|
|
||||||
printf("\n");
|
|
||||||
taos_close(con);
|
|
||||||
// write the history
|
|
||||||
write_history();
|
|
||||||
exitShell();
|
|
||||||
break;
|
|
||||||
case 5: // ctrl E
|
|
||||||
positionCursorEnd(&cmd);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
backspaceChar(&cmd);
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
printf("\n");
|
|
||||||
if (isReadyGo(&cmd)) {
|
|
||||||
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
|
||||||
tfree(cmd.buffer);
|
|
||||||
tfree(cmd.command);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
updateBuffer(&cmd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 11: // Ctrl + K;
|
|
||||||
clearLineAfter(&cmd);
|
|
||||||
break;
|
|
||||||
case 12: // Ctrl + L;
|
|
||||||
system("clear");
|
|
||||||
showOnScreen(&cmd);
|
|
||||||
break;
|
|
||||||
case 21: // Ctrl + U;
|
|
||||||
clearLineBefore(&cmd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (c == '\033') {
|
|
||||||
c = (char)getchar();
|
|
||||||
switch (c) {
|
|
||||||
case '[':
|
|
||||||
c = (char)getchar();
|
|
||||||
switch (c) {
|
|
||||||
case 'A': // Up arrow
|
|
||||||
if (hist_counter != history.hstart) {
|
|
||||||
hist_counter = (hist_counter + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE;
|
|
||||||
resetCommand(&cmd, (history.hist[hist_counter] == NULL) ? "" : history.hist[hist_counter]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'B': // Down arrow
|
|
||||||
if (hist_counter != history.hend) {
|
|
||||||
int next_hist = (hist_counter + 1) % MAX_HISTORY_SIZE;
|
|
||||||
|
|
||||||
if (next_hist != history.hend) {
|
|
||||||
resetCommand(&cmd, (history.hist[next_hist] == NULL) ? "" : history.hist[next_hist]);
|
|
||||||
} else {
|
|
||||||
resetCommand(&cmd, "");
|
|
||||||
}
|
|
||||||
hist_counter = next_hist;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'C': // Right arrow
|
|
||||||
moveCursorRight(&cmd);
|
|
||||||
break;
|
|
||||||
case 'D': // Left arrow
|
|
||||||
moveCursorLeft(&cmd);
|
|
||||||
break;
|
|
||||||
case '1':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// Home key
|
|
||||||
positionCursorHome(&cmd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '2':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// Insert key
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '3':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// Delete key
|
|
||||||
deleteChar(&cmd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '4':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// End key
|
|
||||||
positionCursorEnd(&cmd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '5':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// Page up key
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
if ((c = (char)getchar()) == '~') {
|
|
||||||
// Page down key
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 72:
|
|
||||||
// Home key
|
|
||||||
positionCursorHome(&cmd);
|
|
||||||
break;
|
|
||||||
case 70:
|
|
||||||
// End key
|
|
||||||
positionCursorEnd(&cmd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (c == 0x7f) {
|
|
||||||
// press delete key
|
|
||||||
backspaceChar(&cmd);
|
|
||||||
} else {
|
|
||||||
insertChar(&cmd, &c, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shellLoopQuery(void *arg) {
|
|
||||||
if (indicator) {
|
|
||||||
getOldTerminalMode();
|
|
||||||
indicator = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAOS *con = (TAOS *)arg;
|
|
||||||
|
|
||||||
setThreadName("shellLoopQuery");
|
|
||||||
|
|
||||||
taosThreadCleanupPush(cleanup_handler, NULL);
|
|
||||||
|
|
||||||
char *command = malloc(MAX_COMMAND_SIZE);
|
|
||||||
if (command == NULL){
|
|
||||||
uError("failed to malloc command");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t err = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Read command from shell.
|
|
||||||
memset(command, 0, MAX_COMMAND_SIZE);
|
|
||||||
setTerminalMode();
|
|
||||||
err = shellReadCommand(con, command);
|
|
||||||
if (err) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
resetTerminalMode();
|
|
||||||
} while (shellRunCommand(con, command) == 0);
|
|
||||||
|
|
||||||
tfree(command);
|
|
||||||
exitShell();
|
|
||||||
|
|
||||||
taosThreadCleanupPop(1);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_history_path(char *_history) { snprintf(_history, TSDB_FILENAME_LEN, "%s/%s", getenv("HOME"), HISTORY_FILE); }
|
|
||||||
|
|
||||||
void clearScreen(int ecmd_pos, int cursor_pos) {
|
|
||||||
struct winsize w;
|
|
||||||
if (ioctl(0, TIOCGWINSZ, &w) < 0 || w.ws_col == 0 || w.ws_row == 0) {
|
|
||||||
//fprintf(stderr, "No stream device, and use default value(col 120, row 30)\n");
|
|
||||||
w.ws_col = 120;
|
|
||||||
w.ws_row = 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cursor_x = cursor_pos / w.ws_col;
|
|
||||||
int cursor_y = cursor_pos % w.ws_col;
|
|
||||||
int command_x = ecmd_pos / w.ws_col;
|
|
||||||
positionCursor(cursor_y, LEFT);
|
|
||||||
positionCursor(command_x - cursor_x, DOWN);
|
|
||||||
fprintf(stdout, "\033[2K");
|
|
||||||
for (int i = 0; i < command_x; i++) {
|
|
||||||
positionCursor(1, UP);
|
|
||||||
fprintf(stdout, "\033[2K");
|
|
||||||
}
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showOnScreen(Command *cmd) {
|
|
||||||
struct winsize w;
|
|
||||||
if (ioctl(0, TIOCGWINSZ, &w) < 0 || w.ws_col == 0 || w.ws_row == 0) {
|
|
||||||
//fprintf(stderr, "No stream device\n");
|
|
||||||
w.ws_col = 120;
|
|
||||||
w.ws_row = 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
TdWchar wc;
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
// Print out the command.
|
|
||||||
char *total_string = malloc(MAX_COMMAND_SIZE);
|
|
||||||
memset(total_string, '\0', MAX_COMMAND_SIZE);
|
|
||||||
if (strcmp(cmd->buffer, "") == 0) {
|
|
||||||
sprintf(total_string, "%s%s", PROMPT_HEADER, cmd->command);
|
|
||||||
} else {
|
|
||||||
sprintf(total_string, "%s%s", CONTINUE_PROMPT, cmd->command);
|
|
||||||
}
|
|
||||||
|
|
||||||
int remain_column = w.ws_col;
|
|
||||||
/* size = cmd->commandSize + prompt_size; */
|
|
||||||
for (char *str = total_string; size < cmd->commandSize + prompt_size;) {
|
|
||||||
int ret = taosMbToWchar(&wc, str, MB_CUR_MAX);
|
|
||||||
if (ret < 0) break;
|
|
||||||
size += ret;
|
|
||||||
/* assert(size >= 0); */
|
|
||||||
int width = taosWcharWidth(wc);
|
|
||||||
if (remain_column > width) {
|
|
||||||
printf("%lc", wc);
|
|
||||||
remain_column -= width;
|
|
||||||
} else {
|
|
||||||
if (remain_column == width) {
|
|
||||||
printf("%lc\n\r", wc);
|
|
||||||
remain_column = w.ws_col;
|
|
||||||
} else {
|
|
||||||
printf("\n\r%lc", wc);
|
|
||||||
remain_column = w.ws_col - width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str = total_string + size;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(total_string);
|
|
||||||
/* for (int i = 0; i < size; i++){ */
|
|
||||||
/* char c = total_string[i]; */
|
|
||||||
/* if (k % w.ws_col == 0) { */
|
|
||||||
/* printf("%c\n\r", c); */
|
|
||||||
/* } */
|
|
||||||
/* else { */
|
|
||||||
/* printf("%c", c); */
|
|
||||||
/* } */
|
|
||||||
/* k += 1; */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
// Position the cursor
|
|
||||||
int cursor_pos = cmd->screenOffset + prompt_size;
|
|
||||||
int ecmd_pos = cmd->endOffset + prompt_size;
|
|
||||||
|
|
||||||
int cursor_x = cursor_pos / w.ws_col;
|
|
||||||
int cursor_y = cursor_pos % w.ws_col;
|
|
||||||
// int cursor_y = cursor % w.ws_col;
|
|
||||||
int command_x = ecmd_pos / w.ws_col;
|
|
||||||
int command_y = ecmd_pos % w.ws_col;
|
|
||||||
// int command_y = (command.size() + prompt_size) % w.ws_col;
|
|
||||||
positionCursor(command_y, LEFT);
|
|
||||||
positionCursor(command_x, UP);
|
|
||||||
positionCursor(cursor_x, DOWN);
|
|
||||||
positionCursor(cursor_y, RIGHT);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_handler(void *arg) { resetTerminalMode(); }
|
|
||||||
|
|
||||||
void exitShell() {
|
|
||||||
/*int32_t ret =*/ resetTerminalMode();
|
|
||||||
taos_cleanup();
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
|
@ -13,13 +13,524 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define __USE_XOPEN
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
#include "tglobal.h"
|
#include "tglobal.h"
|
||||||
|
#include "shellCommand.h"
|
||||||
|
#include "tbase64.h"
|
||||||
|
#include "tlog.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
#include <wordexp.h>
|
||||||
|
#include <argp.h>
|
||||||
|
#include <termio.h>
|
||||||
|
|
||||||
|
#define OPT_ABORT 1 /* abort */
|
||||||
|
|
||||||
|
|
||||||
|
int indicator = 1;
|
||||||
|
|
||||||
|
void insertChar(Command *cmd, char *c, int size);
|
||||||
|
const char *argp_program_version = version;
|
||||||
|
const char *argp_program_bug_address = "<support@taosdata.com>";
|
||||||
|
static char doc[] = "";
|
||||||
|
static char args_doc[] = "";
|
||||||
|
|
||||||
TdThread pid;
|
TdThread pid;
|
||||||
static tsem_t cancelSem;
|
static tsem_t cancelSem;
|
||||||
|
|
||||||
|
static struct argp_option options[] = {
|
||||||
|
{"host", 'h', "HOST", 0, "TDengine server FQDN to connect. The default host is localhost."},
|
||||||
|
{"password", 'p', 0, 0, "The password to use when connecting to the server."},
|
||||||
|
{"port", 'P', "PORT", 0, "The TCP/IP port number to use for the connection."},
|
||||||
|
{"user", 'u', "USER", 0, "The user name to use when connecting to the server."},
|
||||||
|
{"auth", 'A', "Auth", 0, "The auth string to use when connecting to the server."},
|
||||||
|
{"config-dir", 'c', "CONFIG_DIR", 0, "Configuration directory."},
|
||||||
|
{"dump-config", 'C', 0, 0, "Dump configuration."},
|
||||||
|
{"commands", 's', "COMMANDS", 0, "Commands to run without enter the shell."},
|
||||||
|
{"raw-time", 'r', 0, 0, "Output time as uint64_t."},
|
||||||
|
{"file", 'f', "FILE", 0, "Script to run without enter the shell."},
|
||||||
|
{"directory", 'D', "DIRECTORY", 0, "Use multi-thread to import all SQL files in the directory separately."},
|
||||||
|
{"thread", 'T', "THREADNUM", 0, "Number of threads when using multi-thread to import data."},
|
||||||
|
{"check", 'k', "CHECK", 0, "Check tables."},
|
||||||
|
{"database", 'd', "DATABASE", 0, "Database to use when connecting to the server."},
|
||||||
|
{"timezone", 'z', "TIMEZONE", 0, "Time zone of the shell, default is local."},
|
||||||
|
{"netrole", 'n', "NETROLE", 0, "Net role when network connectivity test, default is startup, options: client|server|rpc|startup|sync|speen|fqdn."},
|
||||||
|
{"pktlen", 'l', "PKTLEN", 0, "Packet length used for net test, default is 1000 bytes."},
|
||||||
|
{"pktnum", 'N', "PKTNUM", 0, "Packet numbers used for net test, default is 100."},
|
||||||
|
{"pkttype", 'S', "PKTTYPE", 0, "Packet type used for net test, default is TCP."},
|
||||||
|
{0}};
|
||||||
|
|
||||||
|
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||||
|
/* Get the input argument from argp_parse, which we
|
||||||
|
know is a pointer to our arguments structure. */
|
||||||
|
SShellArguments *arguments = state->input;
|
||||||
|
wordexp_t full_path;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'h':
|
||||||
|
arguments->host = arg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
if (arg) {
|
||||||
|
arguments->port = atoi(arg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid port\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
arguments->timezone = arg;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
arguments->user = arg;
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
arguments->auth = arg;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (wordexp(arg, &full_path, 0) != 0) {
|
||||||
|
fprintf(stderr, "Invalid path %s\n", arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strlen(full_path.we_wordv[0]) >= TSDB_FILENAME_LEN) {
|
||||||
|
fprintf(stderr, "config file path: %s overflow max len %d\n", full_path.we_wordv[0], TSDB_FILENAME_LEN - 1);
|
||||||
|
wordfree(&full_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tstrncpy(configDir, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
||||||
|
wordfree(&full_path);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
arguments->dump_config = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
arguments->commands = arg;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
arguments->is_raw_time = true;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if ((0 == strlen(arg)) || (wordexp(arg, &full_path, 0) != 0)) {
|
||||||
|
fprintf(stderr, "Invalid path %s\n", arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tstrncpy(arguments->file, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
||||||
|
wordfree(&full_path);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (wordexp(arg, &full_path, 0) != 0) {
|
||||||
|
fprintf(stderr, "Invalid path %s\n", arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tstrncpy(arguments->dir, full_path.we_wordv[0], TSDB_FILENAME_LEN);
|
||||||
|
wordfree(&full_path);
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
if (arg) {
|
||||||
|
arguments->threadNum = atoi(arg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid number of threads\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
arguments->check = atoi(arg);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
arguments->database = arg;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
arguments->netTestRole = arg;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (arg) {
|
||||||
|
arguments->pktLen = atoi(arg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid packet length\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (arg) {
|
||||||
|
arguments->pktNum = atoi(arg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid packet number\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
arguments->pktType = arg;
|
||||||
|
break;
|
||||||
|
case OPT_ABORT:
|
||||||
|
arguments->abort = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Our argp parser. */
|
||||||
|
static struct argp argp = {options, parse_opt, args_doc, doc};
|
||||||
|
|
||||||
|
char LINUXCLIENT_VERSION[] = "Welcome to the TDengine shell from %s, Client Version:%s\n"
|
||||||
|
"Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n";
|
||||||
|
char g_password[SHELL_MAX_PASSWORD_LEN];
|
||||||
|
|
||||||
|
static void parse_args(
|
||||||
|
int argc, char *argv[], SShellArguments *arguments) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if ((strncmp(argv[i], "-p", 2) == 0)
|
||||||
|
|| (strncmp(argv[i], "--password", 10) == 0)) {
|
||||||
|
printf(LINUXCLIENT_VERSION, tsOsName, taos_get_client_info());
|
||||||
|
if ((strlen(argv[i]) == 2)
|
||||||
|
|| (strncmp(argv[i], "--password", 10) == 0)) {
|
||||||
|
printf("Enter password: ");
|
||||||
|
taosSetConsoleEcho(false);
|
||||||
|
if (scanf("%20s", g_password) > 1) {
|
||||||
|
fprintf(stderr, "password reading error\n");
|
||||||
|
}
|
||||||
|
taosSetConsoleEcho(true);
|
||||||
|
if (EOF == getchar()) {
|
||||||
|
fprintf(stderr, "getchar() return EOF\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tstrncpy(g_password, (char *)(argv[i] + 2), SHELL_MAX_PASSWORD_LEN);
|
||||||
|
strcpy(argv[i], "-p");
|
||||||
|
}
|
||||||
|
arguments->password = g_password;
|
||||||
|
arguments->is_use_passwd = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shellParseArgument(int argc, char *argv[], SShellArguments *arguments) {
|
||||||
|
static char verType[32] = {0};
|
||||||
|
sprintf(verType, "version: %s\n", version);
|
||||||
|
|
||||||
|
argp_program_version = verType;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
parse_args(argc, argv, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
argp_parse(&argp, argc, argv, 0, 0, arguments);
|
||||||
|
if (arguments->abort) {
|
||||||
|
#ifndef _ALPINE
|
||||||
|
#if 0
|
||||||
|
error(10, 0, "ABORTED");
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t shellReadCommand(TAOS *con, char *command) {
|
||||||
|
unsigned hist_counter = history.hend;
|
||||||
|
char utf8_array[10] = "\0";
|
||||||
|
Command cmd;
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.buffer = (char *)calloc(1, MAX_COMMAND_SIZE);
|
||||||
|
cmd.command = (char *)calloc(1, MAX_COMMAND_SIZE);
|
||||||
|
showOnScreen(&cmd);
|
||||||
|
|
||||||
|
// Read input.
|
||||||
|
char c;
|
||||||
|
while (1) {
|
||||||
|
c = (char)getchar(); // getchar() return an 'int' value
|
||||||
|
|
||||||
|
if (c == EOF) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0) { // For UTF-8
|
||||||
|
int count = countPrefixOnes(c);
|
||||||
|
utf8_array[0] = c;
|
||||||
|
for (int k = 1; k < count; k++) {
|
||||||
|
c = (char)getchar();
|
||||||
|
utf8_array[k] = c;
|
||||||
|
}
|
||||||
|
insertChar(&cmd, utf8_array, count);
|
||||||
|
} else if (c < '\033') {
|
||||||
|
// Ctrl keys. TODO: Implement ctrl combinations
|
||||||
|
switch (c) {
|
||||||
|
case 1: // ctrl A
|
||||||
|
positionCursorHome(&cmd);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
printf("\n");
|
||||||
|
resetCommand(&cmd, "");
|
||||||
|
kill(0, SIGINT);
|
||||||
|
break;
|
||||||
|
case 4: // EOF or Ctrl+D
|
||||||
|
printf("\n");
|
||||||
|
taos_close(con);
|
||||||
|
// write the history
|
||||||
|
write_history();
|
||||||
|
exitShell();
|
||||||
|
break;
|
||||||
|
case 5: // ctrl E
|
||||||
|
positionCursorEnd(&cmd);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
backspaceChar(&cmd);
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
printf("\n");
|
||||||
|
if (isReadyGo(&cmd)) {
|
||||||
|
sprintf(command, "%s%s", cmd.buffer, cmd.command);
|
||||||
|
tfree(cmd.buffer);
|
||||||
|
tfree(cmd.command);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
updateBuffer(&cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 11: // Ctrl + K;
|
||||||
|
clearLineAfter(&cmd);
|
||||||
|
break;
|
||||||
|
case 12: // Ctrl + L;
|
||||||
|
system("clear");
|
||||||
|
showOnScreen(&cmd);
|
||||||
|
break;
|
||||||
|
case 21: // Ctrl + U;
|
||||||
|
clearLineBefore(&cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (c == '\033') {
|
||||||
|
c = (char)getchar();
|
||||||
|
switch (c) {
|
||||||
|
case '[':
|
||||||
|
c = (char)getchar();
|
||||||
|
switch (c) {
|
||||||
|
case 'A': // Up arrow
|
||||||
|
if (hist_counter != history.hstart) {
|
||||||
|
hist_counter = (hist_counter + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE;
|
||||||
|
resetCommand(&cmd, (history.hist[hist_counter] == NULL) ? "" : history.hist[hist_counter]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'B': // Down arrow
|
||||||
|
if (hist_counter != history.hend) {
|
||||||
|
int next_hist = (hist_counter + 1) % MAX_HISTORY_SIZE;
|
||||||
|
|
||||||
|
if (next_hist != history.hend) {
|
||||||
|
resetCommand(&cmd, (history.hist[next_hist] == NULL) ? "" : history.hist[next_hist]);
|
||||||
|
} else {
|
||||||
|
resetCommand(&cmd, "");
|
||||||
|
}
|
||||||
|
hist_counter = next_hist;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'C': // Right arrow
|
||||||
|
moveCursorRight(&cmd);
|
||||||
|
break;
|
||||||
|
case 'D': // Left arrow
|
||||||
|
moveCursorLeft(&cmd);
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// Home key
|
||||||
|
positionCursorHome(&cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// Insert key
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// Delete key
|
||||||
|
deleteChar(&cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// End key
|
||||||
|
positionCursorEnd(&cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// Page up key
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
if ((c = (char)getchar()) == '~') {
|
||||||
|
// Page down key
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 72:
|
||||||
|
// Home key
|
||||||
|
positionCursorHome(&cmd);
|
||||||
|
break;
|
||||||
|
case 70:
|
||||||
|
// End key
|
||||||
|
positionCursorEnd(&cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (c == 0x7f) {
|
||||||
|
// press delete key
|
||||||
|
backspaceChar(&cmd);
|
||||||
|
} else {
|
||||||
|
insertChar(&cmd, &c, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *shellLoopQuery(void *arg) {
|
||||||
|
if (indicator) {
|
||||||
|
getOldTerminalMode();
|
||||||
|
indicator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAOS *con = (TAOS *)arg;
|
||||||
|
|
||||||
|
setThreadName("shellLoopQuery");
|
||||||
|
|
||||||
|
taosThreadCleanupPush(cleanup_handler, NULL);
|
||||||
|
|
||||||
|
char *command = malloc(MAX_COMMAND_SIZE);
|
||||||
|
if (command == NULL){
|
||||||
|
uError("failed to malloc command");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t err = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Read command from shell.
|
||||||
|
memset(command, 0, MAX_COMMAND_SIZE);
|
||||||
|
setTerminalMode();
|
||||||
|
err = shellReadCommand(con, command);
|
||||||
|
if (err) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resetTerminalMode();
|
||||||
|
} while (shellRunCommand(con, command) == 0);
|
||||||
|
|
||||||
|
tfree(command);
|
||||||
|
exitShell();
|
||||||
|
|
||||||
|
taosThreadCleanupPop(1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_history_path(char *_history) { snprintf(_history, TSDB_FILENAME_LEN, "%s/%s", getenv("HOME"), HISTORY_FILE); }
|
||||||
|
|
||||||
|
void clearScreen(int ecmd_pos, int cursor_pos) {
|
||||||
|
struct winsize w;
|
||||||
|
if (ioctl(0, TIOCGWINSZ, &w) < 0 || w.ws_col == 0 || w.ws_row == 0) {
|
||||||
|
//fprintf(stderr, "No stream device, and use default value(col 120, row 30)\n");
|
||||||
|
w.ws_col = 120;
|
||||||
|
w.ws_row = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cursor_x = cursor_pos / w.ws_col;
|
||||||
|
int cursor_y = cursor_pos % w.ws_col;
|
||||||
|
int command_x = ecmd_pos / w.ws_col;
|
||||||
|
positionCursor(cursor_y, LEFT);
|
||||||
|
positionCursor(command_x - cursor_x, DOWN);
|
||||||
|
fprintf(stdout, "\033[2K");
|
||||||
|
for (int i = 0; i < command_x; i++) {
|
||||||
|
positionCursor(1, UP);
|
||||||
|
fprintf(stdout, "\033[2K");
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showOnScreen(Command *cmd) {
|
||||||
|
struct winsize w;
|
||||||
|
if (ioctl(0, TIOCGWINSZ, &w) < 0 || w.ws_col == 0 || w.ws_row == 0) {
|
||||||
|
//fprintf(stderr, "No stream device\n");
|
||||||
|
w.ws_col = 120;
|
||||||
|
w.ws_row = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
TdWchar wc;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
// Print out the command.
|
||||||
|
char *total_string = malloc(MAX_COMMAND_SIZE);
|
||||||
|
memset(total_string, '\0', MAX_COMMAND_SIZE);
|
||||||
|
if (strcmp(cmd->buffer, "") == 0) {
|
||||||
|
sprintf(total_string, "%s%s", PROMPT_HEADER, cmd->command);
|
||||||
|
} else {
|
||||||
|
sprintf(total_string, "%s%s", CONTINUE_PROMPT, cmd->command);
|
||||||
|
}
|
||||||
|
|
||||||
|
int remain_column = w.ws_col;
|
||||||
|
/* size = cmd->commandSize + prompt_size; */
|
||||||
|
for (char *str = total_string; size < cmd->commandSize + prompt_size;) {
|
||||||
|
int ret = taosMbToWchar(&wc, str, MB_CUR_MAX);
|
||||||
|
if (ret < 0) break;
|
||||||
|
size += ret;
|
||||||
|
/* assert(size >= 0); */
|
||||||
|
int width = taosWcharWidth(wc);
|
||||||
|
if (remain_column > width) {
|
||||||
|
printf("%lc", wc);
|
||||||
|
remain_column -= width;
|
||||||
|
} else {
|
||||||
|
if (remain_column == width) {
|
||||||
|
printf("%lc\n\r", wc);
|
||||||
|
remain_column = w.ws_col;
|
||||||
|
} else {
|
||||||
|
printf("\n\r%lc", wc);
|
||||||
|
remain_column = w.ws_col - width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = total_string + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(total_string);
|
||||||
|
/* for (int i = 0; i < size; i++){ */
|
||||||
|
/* char c = total_string[i]; */
|
||||||
|
/* if (k % w.ws_col == 0) { */
|
||||||
|
/* printf("%c\n\r", c); */
|
||||||
|
/* } */
|
||||||
|
/* else { */
|
||||||
|
/* printf("%c", c); */
|
||||||
|
/* } */
|
||||||
|
/* k += 1; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
// Position the cursor
|
||||||
|
int cursor_pos = cmd->screenOffset + prompt_size;
|
||||||
|
int ecmd_pos = cmd->endOffset + prompt_size;
|
||||||
|
|
||||||
|
int cursor_x = cursor_pos / w.ws_col;
|
||||||
|
int cursor_y = cursor_pos % w.ws_col;
|
||||||
|
// int cursor_y = cursor % w.ws_col;
|
||||||
|
int command_x = ecmd_pos / w.ws_col;
|
||||||
|
int command_y = ecmd_pos % w.ws_col;
|
||||||
|
// int command_y = (command.size() + prompt_size) % w.ws_col;
|
||||||
|
positionCursor(command_y, LEFT);
|
||||||
|
positionCursor(command_x, UP);
|
||||||
|
positionCursor(cursor_x, DOWN);
|
||||||
|
positionCursor(cursor_y, RIGHT);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_handler(void *arg) { resetTerminalMode(); }
|
||||||
|
|
||||||
|
void exitShell() {
|
||||||
|
/*int32_t ret =*/ resetTerminalMode();
|
||||||
|
taos_cleanup();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) {
|
void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) {
|
||||||
tsem_post(&cancelSem);
|
tsem_post(&cancelSem);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue