diff --git a/include/os/osSystem.h b/include/os/osSystem.h index 33b0a46ee9..6770be6e46 100644 --- a/include/os/osSystem.h +++ b/include/os/osSystem.h @@ -31,19 +31,19 @@ extern "C" { typedef struct TdCmd *TdCmdPtr; -TdCmdPtr taosOpenCmd(const char *cmd); -int64_t taosGetLineCmd(TdCmdPtr pCmd, char ** __restrict ptrBuf); -int32_t taosEOFCmd(TdCmdPtr pCmd); -int64_t taosCloseCmd(TdCmdPtr *ppCmd); +TdCmdPtr taosOpenCmd(const char* cmd); +int64_t taosGetLineCmd(TdCmdPtr pCmd, char** __restrict ptrBuf); +int32_t taosEOFCmd(TdCmdPtr pCmd); +int64_t taosCloseCmd(TdCmdPtr* ppCmd); void* taosLoadDll(const char* filename); void* taosLoadSym(void* handle, char* name); void taosCloseDll(void* handle); int32_t taosSetConsoleEcho(bool on); -void setTerminalMode(); -int32_t getOldTerminalMode(); -void resetTerminalMode(); +void taosSetTerminalMode(); +int32_t taosGetOldTerminalMode(); +void taosResetTerminalMode(); #ifdef __cplusplus } diff --git a/include/util/tdef.h b/include/util/tdef.h index 5f40ef062f..b44520a97e 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -76,8 +76,6 @@ extern const int32_t TYPE_BYTES[15]; #define TSDB_DEFAULT_PASS "taosdata" #endif -#define SHELL_MAX_PASSWORD_LEN 20 - #define TSDB_TRUE 1 #define TSDB_FALSE 0 #define TSDB_OK 0 diff --git a/source/os/src/osSystem.c b/source/os/src/osSystem.c index 665f6370e1..acfeed481c 100644 --- a/source/os/src/osSystem.c +++ b/source/os/src/osSystem.c @@ -119,7 +119,7 @@ int taosSetConsoleEcho(bool on) { #endif } -void setTerminalMode() { +void taosSetTerminalMode() { #if defined(WINDOWS) #else @@ -152,7 +152,7 @@ void setTerminalMode() { #endif } -int32_t getOldTerminalMode() { +int32_t taosGetOldTerminalMode() { #if defined(WINDOWS) #else @@ -170,7 +170,7 @@ int32_t getOldTerminalMode() { #endif } -void resetTerminalMode() { +void taosResetTerminalMode() { #if defined(WINDOWS) #else diff --git a/tools/shell/inc/shellCommand.h b/tools/shell/inc/shellCommand.h deleted file mode 100644 index 2602d41055..0000000000 --- a/tools/shell/inc/shellCommand.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * 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 . - */ - -#ifndef _TD_SHELL_COMMAND_H_ -#define _TD_SHELL_COMMAND_H_ - -#include "shellInt.h" - -#define LEFT 1 -#define RIGHT 2 -#define UP 3 -#define DOWN 4 - -typedef struct { - char *buffer; - char *command; - unsigned commandSize; - unsigned bufferSize; - unsigned cursorOffset; - unsigned screenOffset; - unsigned endOffset; -} Command; - -extern void backspaceChar(Command *cmd); -extern void clearLineBefore(Command *cmd); -extern void clearLineAfter(Command *cmd); -extern void deleteChar(Command *cmd); -extern void moveCursorLeft(Command *cmd); -extern void moveCursorRight(Command *cmd); -extern void positionCursorHome(Command *cmd); -extern void positionCursorEnd(Command *cmd); -extern void showOnScreen(Command *cmd); -extern void updateBuffer(Command *cmd); -extern int isReadyGo(Command *cmd); -extern void resetCommand(Command *cmd, const char s[]); - -int countPrefixOnes(unsigned char c); -void clearScreen(int ecmd_pos, int cursor_pos); -void printChar(char c, int times); -void positionCursor(int step, int direction); - -#endif /*_TD_SHELL_COMMAND_H_*/ diff --git a/tools/shell/inc/shellInt.h b/tools/shell/inc/shellInt.h index 7bacd81820..24300b06a6 100644 --- a/tools/shell/inc/shellInt.h +++ b/tools/shell/inc/shellInt.h @@ -19,34 +19,37 @@ #include "os.h" #include "taos.h" #include "taosdef.h" +#include "taoserror.h" +#include "tconfig.h" +#include "tglobal.h" +#include "ttypes.h" +#include "tutil.h" -#include -#include - -#define MAX_HISTORY_SIZE 1000 -#define MAX_COMMAND_SIZE 1048586 -#define HISTORY_FILE ".taos_history" -#define DEFAULT_RES_SHOW_NUM 100 +#define SHELL_MAX_HISTORY_SIZE 1000 +#define SHELL_MAX_COMMAND_SIZE 1048586 +#define SHELL_HISTORY_FILE ".taos_history" +#define SHELL_DEFAULT_RES_SHOW_NUM 100 +#define SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH 30 typedef struct { - char* hist[MAX_HISTORY_SIZE]; + char* hist[SHELL_MAX_HISTORY_SIZE]; + char file[TSDB_FILENAME_LEN]; int32_t hstart; int32_t hend; } SShellHistory; typedef struct { const char* host; - const char* password; const char* user; const char* auth; const char* database; const char* file; const char* cfgdir; const char* commands; + const char* netrole; + char password[TSDB_USET_PASSWORD_LEN]; bool is_gen_auth; bool is_raw_time; - bool is_client; - bool is_server; bool is_version; bool is_dump_config; bool is_check; @@ -55,38 +58,51 @@ typedef struct { uint16_t port; int32_t pktLen; int32_t pktNum; + int32_t displayWidth; int32_t abort; } SShellArgs; typedef struct { - SShellArgs args; - SShellHistory history; - TAOS* conn; - int64_t result; + const char* clientVersion; + const char* promptHeader; + const char* promptContinue; + const char* osname; + int32_t promptSize; + char programVersion[32]; +} SShellOsDetails; + +typedef struct { + SShellArgs args; + SShellHistory history; + SShellOsDetails info; + TAOS* conn; + TdThread pid; + tsem_t cancelSem; + int64_t result; } SShellObj; +// shellArguments.c int32_t shellParseArgs(int32_t argc, char* argv[]); -int32_t shellInit(); -void shellCleanup(void* arg); -void shellExit(); -void* shellThreadLoop(void* arg); -void shellPrintError(TAOS_RES* tres, int64_t st); -int32_t shellRegexMatch(const char* s, const char* reg, int32_t cflags); -void shellGetGrantInfo(); -void shellReadHistory(); -void shellWriteHistory(); -void shellHistoryPath(char* history); +// shellCommand.c +int32_t shellReadCommand(char* command); -int32_t shellReadCommand(char command[]); -int32_t shellRunCommand(char* command); -void shellRunCommandImp(char command[]); -void shellSourceFile(TAOS* con, char* fptr); -int32_t shellDumpResult(TAOS_RES* con, char* fname, int32_t* error_no, bool printMode); +// shellEngine.c +int32_t shellExecute(); -extern char PROMPT_HEADER[]; -extern char CONTINUE_PROMPT[]; -extern int32_t prompt_size; +// shellUtil.c +int32_t shellCheckIntSize(); +void shellPrintVersion(); +void shellGenerateAuth(); +void shellDumpConfig(); +void shellCheckServerStatus(); +bool shellRegexMatch(const char* s, const char* reg, int32_t cflags); + +// shellNettest.c +void shellTestNetWork(); + +// shellMain.c extern SShellObj shell; +extern void taos_init(); #endif /*_TD_SHELL_INT_H_*/ diff --git a/tools/shell/src/backup/shellDarwin.c b/tools/shell/src/backup/shellDarwin.c deleted file mode 100644 index 9bae306a70..0000000000 --- a/tools/shell/src/backup/shellDarwin.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * 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 . - */ - -#define __USE_XOPEN - -#include "os.h" - -#include "shellInt.h" -#include "shellCommand.h" -#include "tbase64.h" - -#include "tscLog.h" - -#define OPT_ABORT 1 /* �Cabort */ - -int indicator = 1; -struct termios oldtio; - -void insertChar(Command *cmd, char *c, int size); - - -void printHelp() { - char indent[10] = " "; - printf("taos shell is used to test the TDengine database\n"); - - printf("%s%s\n", indent, "-h"); - printf("%s%s%s\n", indent, indent, "TDengine server IP address to connect. The default host is localhost."); - printf("%s%s\n", indent, "-p"); - printf("%s%s%s\n", indent, indent, "The password to use when connecting to the server."); - printf("%s%s\n", indent, "-P"); - printf("%s%s%s\n", indent, indent, "The TCP/IP port number to use for the connection"); - printf("%s%s\n", indent, "-u"); - printf("%s%s%s\n", indent, indent, "The user name to use when connecting to the server."); - printf("%s%s\n", indent, "-c"); - printf("%s%s%s\n", indent, indent, "Configuration directory."); - printf("%s%s\n", indent, "-s"); - printf("%s%s%s\n", indent, indent, "Commands to run without enter the shell."); - printf("%s%s\n", indent, "-r"); - printf("%s%s%s\n", indent, indent, "Output time as unsigned long.."); - printf("%s%s\n", indent, "-f"); - printf("%s%s%s\n", indent, indent, "Script to run without enter the shell."); - printf("%s%s\n", indent, "-d"); - printf("%s%s%s\n", indent, indent, "Database to use when connecting to the server."); - printf("%s%s\n", indent, "-t"); - printf("%s%s%s\n", indent, indent, "Time zone of the shell, default is local."); - printf("%s%s\n", indent, "-D"); - printf("%s%s%s\n", indent, indent, "Use multi-thread to import all SQL files in the directory separately."); - printf("%s%s\n", indent, "-T"); - printf("%s%s%s\n", indent, indent, "Number of threads when using multi-thread to import data."); - - exit(EXIT_SUCCESS); -} - -char DARWINCLIENT_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]; - -void shellParseArgs(int argc, char *argv[], SShellArgs *arguments) { - wordexp_t full_path; - for (int i = 1; i < argc; i++) { - // for host - if (strcmp(argv[i], "-h") == 0) { - if (i < argc - 1) { - arguments->host = argv[++i]; - } else { - fprintf(stderr, "option -h requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for password - else if ((strncmp(argv[i], "-p", 2) == 0) - || (strncmp(argv[i], "--password", 10) == 0)) { - strcpy(tsOsName, "Darwin"); - printf(DARWINCLIENT_VERSION, tsOsName, taos_get_client_info()); - if ((strlen(argv[i]) == 2) - || (strncmp(argv[i], "--password", 10) == 0)) { - printf("Enter password: "); - taosSetConsoleEcho(false); - if (scanf("%s", g_password) > 1) { - fprintf(stderr, "password read error\n"); - } - taosSetConsoleEcho(true); - getchar(); - } else { - tstrncpy(g_password, (char *)(argv[i] + 2), SHELL_MAX_PASSWORD_LEN); - } - arguments->password = g_password; - arguments->is_use_passwd = true; - strcpy(argv[i], ""); - argc -= 1; - } - // for management port - else if (strcmp(argv[i], "-P") == 0) { - if (i < argc - 1) { - arguments->port = atoi(argv[++i]); - } else { - fprintf(stderr, "option -P requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for user - else if (strcmp(argv[i], "-u") == 0) { - if (i < argc - 1) { - arguments->user = argv[++i]; - } else { - fprintf(stderr, "option -u requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-c") == 0) { - if (i < argc - 1) { - if (strlen(argv[++i]) >= TSDB_FILENAME_LEN) { - fprintf(stderr, "config file path: %s overflow max len %d\n", argv[i], TSDB_FILENAME_LEN - 1); - exit(EXIT_FAILURE); - } - strcpy(configDir, argv[i]); - } else { - fprintf(stderr, "Option -c requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-s") == 0) { - if (i < argc - 1) { - arguments->commands = argv[++i]; - } else { - fprintf(stderr, "option -s requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-r") == 0) { - arguments->is_raw_time = true; - } - // For temperory batch commands to run TODO - else if (strcmp(argv[i], "-f") == 0) { - if (i < argc - 1) { - strcpy(arguments->file, argv[++i]); - } else { - fprintf(stderr, "option -f requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for default database - else if (strcmp(argv[i], "-d") == 0) { - if (i < argc - 1) { - arguments->database = argv[++i]; - } else { - fprintf(stderr, "option -d requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // For time zone - else if (strcmp(argv[i], "-t") == 0) { - if (i < argc - 1) { - arguments->timezone = argv[++i]; - } else { - fprintf(stderr, "option -t requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // For import directory - else if (strcmp(argv[i], "-D") == 0) { - if (i < argc - 1) { - if (wordexp(argv[++i], &full_path, 0) != 0) { - fprintf(stderr, "Invalid path %s\n", argv[i]); - exit(EXIT_FAILURE); - } - strcpy(arguments->dir, full_path.we_wordv[0]); - wordfree(&full_path); - } else { - fprintf(stderr, "option -D requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // For time zone - else if (strcmp(argv[i], "-T") == 0) { - if (i < argc - 1) { - arguments->threadNum = atoi(argv[++i]); - } else { - fprintf(stderr, "option -T requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // For temperory command TODO - else if (strcmp(argv[i], "--help") == 0) { - printHelp(); - exit(EXIT_FAILURE); - } else { - fprintf(stderr, "wrong options\n"); - printHelp(); - exit(EXIT_FAILURE); - } - } -} - -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 *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - cmd.command = (char *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - showOnScreen(&cmd); - - // Read input. - char c; - while (1) { - c = getchar(); - - if (c < 0) { // For UTF-8 - int count = countPrefixOnes(c); - utf8_array[0] = c; - for (int k = 1; k < count; k++) { - c = 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 - shellWriteHistory(); - shellExit(); - 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); - taosMemoryFreeClear(cmd.buffer); - taosMemoryFreeClear(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 = getchar(); - switch (c) { - case '[': - c = 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 = getchar()) == '~') { - // Home key - positionCursorHome(&cmd); - } - break; - case '2': - if ((c = getchar()) == '~') { - // Insert key - } - break; - case '3': - if ((c = getchar()) == '~') { - // Delete key - deleteChar(&cmd); - } - break; - case '4': - if ((c = getchar()) == '~') { - // End key - positionCursorEnd(&cmd); - } - break; - case '5': - if ((c = getchar()) == '~') { - // Page up key - } - break; - case '6': - if ((c = 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 *shellThreadLoop(void *arg) { - if (indicator) { - getOldTerminalMode(); - indicator = 0; - } - - TAOS *con = (TAOS *)arg; - - setThreadName("shellThreadLoop"); - - taosThreadCleanupPush(shellCleanup, NULL); - - char *command = taosMemoryMalloc(MAX_COMMAND_SIZE); - if (command == NULL){ - tscError("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); - - taosMemoryFreeClear(command); - shellExit(); - - taosThreadCleanupPop(1); - - return NULL; -} - -void shellHistoryPath(char *history) { sprintf(history, "%s/%s", getpwuid(getuid())->pw_dir, 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 = taosMemoryMalloc(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; - } - - taosMemoryFree(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 shellCleanup(void *arg) { resetTerminalMode(); } - -void shellExit() { - resetTerminalMode(); - exit(EXIT_SUCCESS); -} diff --git a/tools/shell/src/backup/shellWindows.c b/tools/shell/src/backup/shellWindows.c deleted file mode 100644 index bf1c683d94..0000000000 --- a/tools/shell/src/backup/shellWindows.c +++ /dev/null @@ -1,325 +0,0 @@ -/******************************************************************* -* Copyright (c) 2017 by TAOS Technologies, Inc. -* All rights reserved. -* -* This file is proprietary and confidential to TAOS Technologies. -* No part of this file may be reproduced, stored, transmitted, -* disclosed or used in any form or by any means other than as -* expressly provided by the written permission from Jianhui Tao -* -* ****************************************************************/ - -#include -#include -#include -#include "../../../../include/client/taos.h" -#include "os.h" -#include "shellInt.h" -#include "shellCommand.h" - -extern char configDir[]; - -char WINCLIENT_VERSION[] = "Welcome to the TDengine shell from %s, Client Version:%s\n" - "Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n"; - -void printVersion() { - printf("version: %s\n", version); -} - -void printHelp() { - char indent[10] = " "; - printf("taos shell is used to test the TDengine database\n"); - - printf("%s%s\n", indent, "-h"); - printf("%s%s%s\n", indent, indent, "TDengine server FQDN to connect. The default host is localhost."); - printf("%s%s\n", indent, "-p"); - printf("%s%s%s\n", indent, indent, "The password to use when connecting to the server."); - printf("%s%s\n", indent, "-P"); - printf("%s%s%s\n", indent, indent, "The TCP/IP port number to use for the connection"); - printf("%s%s\n", indent, "-u"); - printf("%s%s%s\n", indent, indent, "The user name to use when connecting to the server."); - printf("%s%s\n", indent, "-A"); - printf("%s%s%s\n", indent, indent, "The user auth to use when connecting to the server."); - printf("%s%s\n", indent, "-c"); - printf("%s%s%s\n", indent, indent, "Configuration directory."); - printf("%s%s\n", indent, "-C"); - printf("%s%s%s\n", indent, indent, "Dump configuration."); - printf("%s%s\n", indent, "-s"); - printf("%s%s%s\n", indent, indent, "Commands to run without enter the shell."); - printf("%s%s\n", indent, "-r"); - printf("%s%s%s\n", indent, indent, "Output time as unsigned long.."); - printf("%s%s\n", indent, "-f"); - printf("%s%s%s\n", indent, indent, "Script to run without enter the shell."); - printf("%s%s\n", indent, "-d"); - printf("%s%s%s\n", indent, indent, "Database to use when connecting to the server."); - printf("%s%s\n", indent, "-t"); - printf("%s%s%s\n", indent, indent, "Time zone of the shell, default is local."); - printf("%s%s\n", indent, "-n"); - printf("%s%s%s\n", indent, indent, "Net role when network connectivity test, default is startup, options: client|server|rpc|startup|sync|speed|fqdn."); - printf("%s%s\n", indent, "-l"); - printf("%s%s%s\n", indent, indent, "Packet length used for net test, default is 1000 bytes."); - printf("%s%s\n", indent, "-N"); - printf("%s%s%s\n", indent, indent, "Packet numbers used for net test, default is 100."); - printf("%s%s\n", indent, "-S"); - printf("%s%s%s\n", indent, indent, "Packet type used for net test, default is TCP."); - printf("%s%s\n", indent, "-V"); - printf("%s%s%s\n", indent, indent, "Print program version."); - - exit(EXIT_SUCCESS); -} - -char g_password[SHELL_MAX_PASSWORD_LEN]; - -void shellParseArgs(int argc, char *argv[], SShellArgs *arguments) { - for (int i = 1; i < argc; i++) { - // for host - if (strcmp(argv[i], "-h") == 0) { - if (i < argc - 1) { - arguments->host = argv[++i]; - } else { - fprintf(stderr, "option -h requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for password - else if ((strncmp(argv[i], "-p", 2) == 0) - || (strncmp(argv[i], "--password", 10) == 0)) { - arguments->is_use_passwd = true; - strcpy(tsOsName, "Windows"); - printf(WINCLIENT_VERSION, tsOsName, taos_get_client_info()); - if ((strlen(argv[i]) == 2) - || (strncmp(argv[i], "--password", 10) == 0)) { - printf("Enter password: "); - taosSetConsoleEcho(false); - if (scanf("%s", g_password) > 1) { - fprintf(stderr, "password read error!\n"); - } - taosSetConsoleEcho(true); - getchar(); - } else { - tstrncpy(g_password, (char *)(argv[i] + 2), SHELL_MAX_PASSWORD_LEN); - } - arguments->password = g_password; - strcpy(argv[i], ""); - argc -= 1; - } - // for management port - else if (strcmp(argv[i], "-P") == 0) { - if (i < argc - 1) { - arguments->port = atoi(argv[++i]); - } else { - fprintf(stderr, "option -P requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for user - else if (strcmp(argv[i], "-u") == 0) { - if (i < argc - 1) { - arguments->user = argv[++i]; - } else { - fprintf(stderr, "option -u requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-A") == 0) { - if (i < argc - 1) { - arguments->auth = argv[++i]; - } else { - fprintf(stderr, "option -A requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-c") == 0) { - if (i < argc - 1) { - char *tmp = argv[++i]; - if (strlen(tmp) >= TSDB_FILENAME_LEN) { - fprintf(stderr, "config file path: %s overflow max len %d\n", tmp, TSDB_FILENAME_LEN - 1); - exit(EXIT_FAILURE); - } - strcpy(configDir, tmp); - } else { - fprintf(stderr, "Option -c requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-C") == 0) { - arguments->dump_config = true; - } else if (strcmp(argv[i], "-s") == 0) { - if (i < argc - 1) { - arguments->commands = argv[++i]; - } else { - fprintf(stderr, "option -s requires an argument\n"); - exit(EXIT_FAILURE); - } - } else if (strcmp(argv[i], "-r") == 0) { - arguments->is_raw_time = true; - } - // For temperory batch commands to run TODO - else if (strcmp(argv[i], "-f") == 0) { - if (i < argc - 1) { - strcpy(arguments->file, argv[++i]); - } else { - fprintf(stderr, "option -f requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // for default database - else if (strcmp(argv[i], "-d") == 0) { - if (i < argc - 1) { - arguments->database = argv[++i]; - } else { - fprintf(stderr, "option -d requires an argument\n"); - exit(EXIT_FAILURE); - } - } - // For time zone - else if (strcmp(argv[i], "-t") == 0) { - if (i < argc - 1) { - arguments->timezone = argv[++i]; - } else { - fprintf(stderr, "option -t requires an argument\n"); - exit(EXIT_FAILURE); - } - } - else if (strcmp(argv[i], "-n") == 0) { - if (i < argc - 1) { - arguments->netTestRole = argv[++i]; - } else { - fprintf(stderr, "option -n requires an argument\n"); - exit(EXIT_FAILURE); - } - } - else if (strcmp(argv[i], "-l") == 0) { - if (i < argc - 1) { - arguments->pktLen = atoi(argv[++i]); - } else { - fprintf(stderr, "option -l requires an argument\n"); - exit(EXIT_FAILURE); - } - } - else if (strcmp(argv[i], "-N") == 0) { - if (i < argc - 1) { - arguments->pktNum = atoi(argv[++i]); - } else { - fprintf(stderr, "option -N requires an argument\n"); - exit(EXIT_FAILURE); - } - } - else if (strcmp(argv[i], "-S") == 0) { - if (i < argc - 1) { - arguments->pktType = argv[++i]; - } else { - fprintf(stderr, "option -S requires an argument\n"); - exit(EXIT_FAILURE); - } - } - else if (strcmp(argv[i], "-V") == 0) { - printVersion(); - exit(EXIT_SUCCESS); - } - // For temperory command TODO - else if (strcmp(argv[i], "--help") == 0) { - printHelp(); - exit(EXIT_SUCCESS); - } else { - fprintf(stderr, "wrong options\n"); - printHelp(); - exit(EXIT_FAILURE); - } - } -} - -void shellPrintContinuePrompt() { printf("%s", CONTINUE_PROMPT); } - -void shellPrintPrompt() { printf("%s", PROMPT_HEADER); } - -void updateBuffer(Command *cmd) { - if (shellRegexMatch(cmd->buffer, "(\\s+$)|(^$)", REG_EXTENDED)) strcat(cmd->command, " "); - strcat(cmd->buffer, cmd->command); - - memset(cmd->command, 0, MAX_COMMAND_SIZE); - cmd->cursorOffset = 0; -} - -int isReadyGo(Command *cmd) { - char *total = taosMemoryMalloc(MAX_COMMAND_SIZE); - memset(total, 0, MAX_COMMAND_SIZE); - sprintf(total, "%s%s", cmd->buffer, cmd->command); - - char *reg_str = - "(^.*;\\s*$)|(^\\s*$)|(^\\s*exit\\s*$)|(^\\s*q\\s*$)|(^\\s*quit\\s*$)|(^" - "\\s*clear\\s*$)"; - if (shellRegexMatch(total, reg_str, REG_EXTENDED | REG_ICASE)) { - taosMemoryFree(total); - return 1; - } - - taosMemoryFree(total); - return 0; -} - -void insertChar(Command *cmd, char c) { - // TODO: Check if the length enough. - if (cmd->cursorOffset >= MAX_COMMAND_SIZE) { - fprintf(stdout, "sql is larger than %d bytes", MAX_COMMAND_SIZE); - return; - } - - cmd->command[cmd->cursorOffset++] = c; -} - -int32_t shellReadCommand(TAOS *con, char command[]) { - Command cmd; - memset(&cmd, 0, sizeof(cmd)); - cmd.buffer = (char *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - cmd.command = (char *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - - // Read input. - char c; - while (1) { - c = getchar(); - - switch (c) { - case '\n': - case '\r': - if (isReadyGo(&cmd)) { - sprintf(command, "%s%s", cmd.buffer, cmd.command); - taosMemoryFree(cmd.buffer); - cmd.buffer = NULL; - taosMemoryFree(cmd.command); - cmd.command = NULL; - return 0; - } else { - shellPrintContinuePrompt(); - updateBuffer(&cmd); - } - break; - default: - insertChar(&cmd, c); - } - } - - return 0; -} - -void *shellThreadLoop(void *arg) { - TAOS *con = (TAOS *)arg; - char *command = taosMemoryMalloc(MAX_COMMAND_SIZE); - if (command == NULL) return NULL; - - int32_t err = 0; - - do { - memset(command, 0, MAX_COMMAND_SIZE); - shellPrintPrompt(); - - // Read command from shell. - err = shellReadCommand(con, command); - if (err) { - break; - } - } while (shellRunCommand(con, command) == 0); - - return NULL; -} - -void shellHistoryPath(char *history) { sprintf(history, "C:/TDengine/%s", HISTORY_FILE); } - -void shellExit() { exit(EXIT_SUCCESS); } diff --git a/tools/shell/src/shellArguments.c b/tools/shell/src/shellArguments.c new file mode 100644 index 0000000000..8960d7e36d --- /dev/null +++ b/tools/shell/src/shellArguments.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * 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 . + */ + +#include "shellInt.h" + +#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32) || defined(_TD_DARWIN_64) +void shellPrintHelp() { + char indent[10] = " "; + printf("taos shell is used to test the TDengine database\n"); + printf("%s%s\n", indent, "-h"); + printf("%s%s%s\n", indent, indent, "TDengine server FQDN to connect. The default host is localhost."); + printf("%s%s\n", indent, "-P"); + printf("%s%s%s\n", indent, indent, "The TCP/IP port number to use for the connection"); + printf("%s%s\n", indent, "-u"); + printf("%s%s%s\n", indent, indent, "The user name to use when connecting to the server."); + printf("%s%s\n", indent, "-p"); + printf("%s%s%s\n", indent, indent, "The password to use when connecting to the server."); + printf("%s%s\n", indent, "-a"); + printf("%s%s%s\n", indent, indent, "The user auth to use when connecting to the server."); + printf("%s%s\n", indent, "-A"); + printf("%s%s%s\n", indent, indent, "Generate auth string from password."); + printf("%s%s\n", indent, "-c"); + printf("%s%s%s\n", indent, indent, "Configuration directory."); + printf("%s%s\n", indent, "-C"); + printf("%s%s%s\n", indent, indent, "Dump configuration."); + printf("%s%s\n", indent, "-s"); + printf("%s%s%s\n", indent, indent, "Commands to run without enter the shell."); + printf("%s%s\n", indent, "-r"); + printf("%s%s%s\n", indent, indent, "Output time as unsigned long.."); + printf("%s%s\n", indent, "-f"); + printf("%s%s%s\n", indent, indent, "Script to run without enter the shell."); + printf("%s%s\n", indent, "-d"); + printf("%s%s%s\n", indent, indent, "Database to use when connecting to the server."); + printf("%s%s\n", indent, "-k"); + printf("%s%s%s\n", indent, indent, "Check the service status."); + printf("%s%s\n", indent, "-t"); + printf("%s%s%s\n", indent, indent, "Check the details of the service status."); + printf("%s%s\n", indent, "-w"); + printf("%s%s%s\n", indent, indent, "Set the default binary display width."); + printf("%s%s\n", indent, "-n"); + printf("%s%s%s\n", indent, indent, "Net role when network connectivity test, options: client|server."); + printf("%s%s\n", indent, "-l"); + printf("%s%s%s\n", indent, indent, "Packet length used for net test, default is 1000 bytes."); + printf("%s%s\n", indent, "-N"); + printf("%s%s%s\n", indent, indent, "Packet numbers used for net test, default is 100."); + printf("%s%s\n", indent, "-V"); + printf("%s%s%s\n", indent, indent, "Print program version."); +} + +void shellParseArgsInWindows(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + if (i < argc - 1) { + arguments->host = argv[++i]; + } else { + fprintf(stderr, "option -h requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-P") == 0) { + if (i < argc - 1) { + arguments->port = atoi(argv[++i]); + } else { + fprintf(stderr, "option -P requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-u") == 0) { + if (i < argc - 1) { + arguments->user = argv[++i]; + } else { + fprintf(stderr, "option -u requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if ((strncmp(argv[i], "-p", 2) == 0) || (strncmp(argv[i], "--password", 10) == 0)) { + continue; + } else if (strcmp(argv[i], "-a") == 0) { + if (i < argc - 1) { + arguments->auth = argv[++i]; + } else { + fprintf(stderr, "option -a requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-A") == 0) { + arguments->is_gen_auth = true; + } else if (strcmp(argv[i], "-c") == 0) { + if (i < argc - 1) { + arguments->cfgdir = argv[++i]; + } else { + fprintf(stderr, "Option -c requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-C") == 0) { + arguments->is_dump_config = true; + } else if (strcmp(argv[i], "-s") == 0) { + if (i < argc - 1) { + arguments->commands = argv[++i]; + } else { + fprintf(stderr, "option -s requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-r") == 0) { + arguments->is_raw_time = true; + } else if (strcmp(argv[i], "-f") == 0) { + if (i < argc - 1) { + strcpy(arguments->file, argv[++i]); + } else { + fprintf(stderr, "option -f requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-d") == 0) { + if (i < argc - 1) { + arguments->database = argv[++i]; + } else { + fprintf(stderr, "option -d requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-k") == 0) { + arguments->is_check = true; + } else if (strcmp(argv[i], "-t") == 0) { + arguments->is_startup = true; + } else if (strcmp(argv[i], "-w") == 0) { + if (i < argc - 1) { + arguments->displayWidth = argv[++i]; + } else { + fprintf(stderr, "option -w requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-n") == 0) { + if (i < argc - 1) { + arguments->netTestRole = argv[++i]; + } else { + fprintf(stderr, "option -n requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-l") == 0) { + if (i < argc - 1) { + arguments->pktLen = atoi(argv[++i]); + } else { + fprintf(stderr, "option -l requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-N") == 0) { + if (i < argc - 1) { + arguments->pktNum = atoi(argv[++i]); + } else { + fprintf(stderr, "option -N requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-S") == 0) { + if (i < argc - 1) { + arguments->pktType = argv[++i]; + } else { + fprintf(stderr, "option -S requires an argument\n"); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[i], "-V") == 0) { + arguments->is_version = true; + } else if (strcmp(argv[i], "--help") == 0) { + arguments->is_help = true; + } else { + fprintf(stderr, "wrong options\n"); + arguments->is_help = true; + } + } +} + +#else + +#include +#include +const char *argp_program_version = version; +const char *argp_program_bug_address = ""; + +static struct argp_option shellOptions[] = { + {"host", 'h', "HOST", 0, "TDengine server FQDN to connect. The default host is localhost."}, + {"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."}, + {"password", 'p', "PASSWORD", 0, "The password to use when connecting to the server."}, + {"auth", 'a', "AUTH", 0, "The auth string to use when connecting to the server."}, + {"generate-auth", 'A', 0, 0, "Generate auth string from password."}, + {"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."}, + {"database", 'd', "DATABASE", 0, "Database to use when connecting to the server."}, + {"check", 'k', 0, 0, "Check the service status."}, + {"startup", 't', 0, 0, "Check the details of the service status."}, + {"display-width", 'w', 0, 0, "Set the default binary display width."}, + {"netrole", 'n', "NETROLE", 0, "Net role when network connectivity test, options: client|server."}, + {"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."}, + {"version", 'V', 0, 0, "Print client version number."}, + {0}, +}; + +static error_t shellParseOpt(int32_t key, char *arg, struct argp_state *state) { + SShellArgs *arguments = &shell.args; + wordexp_t full_path = {0}; + + switch (key) { + case 'h': + arguments->host = arg; + break; + case 'P': + arguments->port = atoi(arg); + break; + case 'u': + arguments->user = arg; + break; + case 'p': + break; + case 'a': + arguments->auth = arg; + break; + case 'A': + arguments->is_gen_auth = true; + break; + case 'c': + arguments->cfgdir = arg; + break; + case 'C': + arguments->is_dump_config = true; + break; + case 's': + arguments->commands = arg; + break; + case 'r': + arguments->is_raw_time = true; + break; + case 'f': + arguments->file = arg; + break; + case 'd': + arguments->database = arg; + break; + case 'k': + arguments->is_check = true; + break; + case 't': + arguments->is_startup = true; + break; + case 'w': + arguments->displayWidth = atoi(arg); + break; + case 'n': + arguments->netrole = arg; + break; + case 'l': + arguments->pktLen = atoi(arg); + break; + case 'N': + arguments->pktNum = atoi(arg); + break; + case 'V': + arguments->is_version = true; + break; + case 1: + arguments->abort = 1; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp shellArgp = {shellOptions, shellParseOpt, "", ""}; + +static void shellParseArgsInLinux(int argc, char *argv[]) { + argp_program_version = shell.info.programVersion; + argp_parse(&shellArgp, argc, argv, 0, 0, NULL); +} + +#endif + +static void shellInitArgs(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + if ((strncmp(argv[i], "-p", 2) == 0) || (strncmp(argv[i], "--password", 10) == 0)) { + printf(shell.info.clientVersion, tsOsName, taos_get_client_info()); + if ((strlen(argv[i]) == 2) || (strncmp(argv[i], "--password", 10) == 0)) { + printf("Enter password: "); + taosSetConsoleEcho(false); + if (scanf("%20s", shell.args.password) > 1) { + fprintf(stderr, "password reading error\n"); + } + taosSetConsoleEcho(true); + if (EOF == getchar()) { + fprintf(stderr, "getchar() return EOF\n"); + } + } else { + tstrncpy(shell.args.password, (char *)(argv[i] + 2), sizeof(shell.args.password)); + strcpy(argv[i], "-p"); + } + } + } + if (strlen(shell.args.password) == 0) { + tstrncpy(shell.args.password, TSDB_DEFAULT_PASS, sizeof(shell.args.password)); + } + + shell.args.pktLen = 1024; + shell.args.pktNum = 100; + shell.args.displayWidth = SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH; + shell.args.user = TSDB_DEFAULT_USER; +} + +static int32_t shellCheckArgs() { return 0; } + +int32_t shellParseArgs(int32_t argc, char *argv[]) { + shellInitArgs(argc, argv); + shell.info.clientVersion = + "Welcome to the TDengine shell from %s, Client Version:%s\n" + "Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n"; + shell.info.promptHeader = "taos> "; + shell.info.promptContinue = " -> "; + shell.info.promptSize = 6; + snprintf(shell.info.programVersion, sizeof(shell.info.programVersion), "version: %s\n", version); + +#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32) + shell.info.osname = "Windows"; + snprintf(shell.history.file, TSDB_FILENAME_LEN, "C:/TDengine/%s", SHELL_HISTORY_FILE); + shellParseArgsInLinuxAndDarwin(); +#elif defined(_TD_DARWIN_64) + shell.info.osname = "Darwin"; + snprintf(shell.history.file, TSDB_FILENAME_LEN, "%s/%s", getpwuid(getuid())->pw_dir, SHELL_HISTORY_FILE); + shellParseArgsInLinuxAndDarwin(); +#else + shell.info.osname = "Linux"; + snprintf(shell.history.file, TSDB_FILENAME_LEN, "%s/%s", getenv("HOME"), SHELL_HISTORY_FILE); + shellParseArgsInLinux(argc, argv); +#endif + + if (shell.args.abort) { + return -1; + } + + return shellCheckArgs(); +} diff --git a/tools/shell/src/shellCommand.c b/tools/shell/src/shellCommand.c index f564b5421b..c34ee7a22c 100644 --- a/tools/shell/src/shellCommand.c +++ b/tools/shell/src/shellCommand.c @@ -14,222 +14,60 @@ */ #define __USE_XOPEN - -#include "shellCommand.h" -#include "os.h" #include "shellInt.h" +#define LEFT 1 +#define RIGHT 2 +#define UP 3 +#define DOWN 4 +#define PSIZE shell.info.promptSize typedef struct { - char widthInString; - char widthOnScreen; -} UTFCodeInfo; + char *buffer; + char *command; + uint32_t commandSize; + uint32_t bufferSize; + uint32_t cursorOffset; + uint32_t screenOffset; + uint32_t endOffset; +} SShellCmd; -int countPrefixOnes(unsigned char c) { - unsigned char mask = 127; - mask = ~mask; - int ret = 0; - while ((c & mask) != 0) { - ret++; - c <<= 1; - } +static int32_t shellCountPrefixOnes(uint8_t c); +static void shellGetPrevCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width); +static void shellGetNextCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width); +static void shellInsertChar(SShellCmd *cmd, char *c, int size); +static void shellBackspaceChar(SShellCmd *cmd); +static void shellClearLineBefore(SShellCmd *cmd); +static void shellClearLineAfter(SShellCmd *cmd); +static void shellDeleteChar(SShellCmd *cmd); +static void shellMoveCursorLeft(SShellCmd *cmd); +static void shellMoveCursorRight(SShellCmd *cmd); +static void shellPositionCursorHome(SShellCmd *cmd); +static void shellPositionCursorEnd(SShellCmd *cmd); +static void shellPrintChar(char c, int32_t times); +static void shellPositionCursor(int32_t step, int32_t direction); +static void shellUpdateBuffer(SShellCmd *cmd); +static int32_t shellIsReadyGo(SShellCmd *cmd); +static void shellGetMbSizeInfo(const char *str, int32_t *size, int32_t *width); +static void shellResetCommand(SShellCmd *cmd, const char s[]); +static void shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos); +static void shellShowOnScreen(SShellCmd *cmd); - return ret; -} - -void getPrevCharSize(const char *str, int pos, int *size, int *width) { - assert(pos > 0); - - TdWchar wc; - *size = 0; - *width = 0; - - while (--pos >= 0) { - *size += 1; - - if (str[pos] > 0 || countPrefixOnes((unsigned char)str[pos]) > 1) break; - } - - int rc = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); - assert(rc == *size); - - *width = taosWcharWidth(wc); -} - -void getNextCharSize(const char *str, int pos, int *size, int *width) { - assert(pos >= 0); - - TdWchar wc; - *size = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); - *width = taosWcharWidth(wc); -} - -void insertChar(Command *cmd, char *c, int size) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - TdWchar wc; - if (taosMbToWchar(&wc, c, size) < 0) return; - - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - /* update the buffer */ - memmove(cmd->command + cmd->cursorOffset + size, cmd->command + cmd->cursorOffset, - cmd->commandSize - cmd->cursorOffset); - memcpy(cmd->command + cmd->cursorOffset, c, size); - /* update the values */ - cmd->commandSize += size; - cmd->cursorOffset += size; - cmd->screenOffset += taosWcharWidth(wc); - cmd->endOffset += taosWcharWidth(wc); - showOnScreen(cmd); -} - -void backspaceChar(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset > 0) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - int size = 0; - int width = 0; - getPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width); - memmove(cmd->command + cmd->cursorOffset - size, cmd->command + cmd->cursorOffset, - cmd->commandSize - cmd->cursorOffset); - cmd->commandSize -= size; - cmd->cursorOffset -= size; - cmd->screenOffset -= width; - cmd->endOffset -= width; - showOnScreen(cmd); - } -} - -void clearLineBefore(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - memmove(cmd->command, cmd->command + cmd->cursorOffset, cmd->commandSize - cmd->cursorOffset); - cmd->commandSize -= cmd->cursorOffset; - cmd->cursorOffset = 0; - cmd->screenOffset = 0; - cmd->endOffset = cmd->commandSize; - showOnScreen(cmd); -} - -void clearLineAfter(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - cmd->commandSize -= cmd->endOffset - cmd->cursorOffset; - cmd->endOffset = cmd->cursorOffset; - showOnScreen(cmd); -} - -void deleteChar(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset < cmd->commandSize) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - int size = 0; - int width = 0; - getNextCharSize(cmd->command, cmd->cursorOffset, &size, &width); - memmove(cmd->command + cmd->cursorOffset, cmd->command + cmd->cursorOffset + size, - cmd->commandSize - cmd->cursorOffset - size); - cmd->commandSize -= size; - cmd->endOffset -= width; - showOnScreen(cmd); - } -} - -void moveCursorLeft(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset > 0) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - int size = 0; - int width = 0; - getPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width); - cmd->cursorOffset -= size; - cmd->screenOffset -= width; - showOnScreen(cmd); - } -} - -void moveCursorRight(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset < cmd->commandSize) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - int size = 0; - int width = 0; - getNextCharSize(cmd->command, cmd->cursorOffset, &size, &width); - cmd->cursorOffset += size; - cmd->screenOffset += width; - showOnScreen(cmd); - } -} - -void positionCursorHome(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset > 0) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - cmd->cursorOffset = 0; - cmd->screenOffset = 0; - showOnScreen(cmd); - } -} - -void positionCursorEnd(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - if (cmd->cursorOffset < cmd->commandSize) { - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - cmd->cursorOffset = cmd->commandSize; - cmd->screenOffset = cmd->endOffset; - showOnScreen(cmd); - } -} - -void printChar(char c, int times) { - for (int i = 0; i < times; i++) { - fprintf(stdout, "%c", c); - } - fflush(stdout); -} - -void positionCursor(int step, int direction) { - if (step > 0) { - if (direction == LEFT) { - fprintf(stdout, "\033[%dD", step); - } else if (direction == RIGHT) { - fprintf(stdout, "\033[%dC", step); - } else if (direction == UP) { - fprintf(stdout, "\033[%dA", step); - } else if (direction == DOWN) { - fprintf(stdout, "\033[%dB", step); - } - fflush(stdout); - } -} - -void updateBuffer(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); +#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32) +static void shellPrintContinuePrompt() { printf("%s", shell.args.promptContinue); } +static void shellPrintPrompt() { printf("%s", shell.args.promptHeader); } +void shellUpdateBuffer(SShellCmd *cmd) { if (shellRegexMatch(cmd->buffer, "(\\s+$)|(^$)", REG_EXTENDED)) strcat(cmd->command, " "); strcat(cmd->buffer, cmd->command); - cmd->bufferSize += cmd->commandSize; - memset(cmd->command, 0, MAX_COMMAND_SIZE); + memset(cmd->command, 0, SHELL_MAX_COMMAND_SIZE); cmd->cursorOffset = 0; - cmd->screenOffset = 0; - cmd->commandSize = 0; - cmd->endOffset = 0; - showOnScreen(cmd); } -int isReadyGo(Command *cmd) { - assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - - char *total = (char *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - memset(cmd->command + cmd->commandSize, 0, MAX_COMMAND_SIZE - cmd->commandSize); +int shellIsReadyGo(SShellCmd *cmd) { + char *total = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE); + memset(total, 0, SHELL_MAX_COMMAND_SIZE); sprintf(total, "%s%s", cmd->buffer, cmd->command); char *reg_str = @@ -244,28 +82,531 @@ int isReadyGo(Command *cmd) { return 0; } -void getMbSizeInfo(const char *str, int *size, int *width) { - TdWchar *wc = (TdWchar *)taosMemoryCalloc(sizeof(TdWchar), MAX_COMMAND_SIZE); +void shellInsertChar(SShellCmd *cmd, char c) { + if (cmd->cursorOffset >= SHELL_MAX_COMMAND_SIZE) { + fprintf(stdout, "sql is larger than %d bytes", SHELL_MAX_COMMAND_SIZE); + return; + } + cmd->command[cmd->cursorOffset++] = c; +} + +int32_t shellReadCommand(char command[]) { + SShellCmd cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.buffer = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE); + cmd.command = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE); + + // Read input. + char c; + while (1) { + c = getchar(); + + switch (c) { + case '\n': + case '\r': + if (shellIsReadyGo(&cmd)) { + sprintf(command, "%s%s", cmd.buffer, cmd.command); + taosMemoryFree(cmd.buffer); + cmd.buffer = NULL; + taosMemoryFree(cmd.command); + cmd.command = NULL; + return 0; + } else { + shellPrintContinuePrompt(); + shellUpdateBuffer(&cmd); + } + break; + default: + shellInsertChar(&cmd, c); + } + } + + return 0; +} + +#else + +int32_t shellCountPrefixOnes(uint8_t c) { + uint8_t mask = 127; + mask = ~mask; + int32_t ret = 0; + while ((c & mask) != 0) { + ret++; + c <<= 1; + } + + return ret; +} + +void shellGetPrevCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) { + assert(pos > 0); + + TdWchar wc; + *size = 0; + *width = 0; + + while (--pos >= 0) { + *size += 1; + + if (str[pos] > 0 || shellCountPrefixOnes((uint8_t)str[pos]) > 1) break; + } + + int32_t rc = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); + assert(rc == *size); + + *width = taosWcharWidth(wc); +} + +void shellGetNextCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) { + assert(pos >= 0); + + TdWchar wc; + *size = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); + *width = taosWcharWidth(wc); +} + +void shellInsertChar(SShellCmd *cmd, char *c, int32_t size) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + TdWchar wc; + if (taosMbToWchar(&wc, c, size) < 0) return; + + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + /* update the buffer */ + memmove(cmd->command + cmd->cursorOffset + size, cmd->command + cmd->cursorOffset, + cmd->commandSize - cmd->cursorOffset); + memcpy(cmd->command + cmd->cursorOffset, c, size); + /* update the values */ + cmd->commandSize += size; + cmd->cursorOffset += size; + cmd->screenOffset += taosWcharWidth(wc); + cmd->endOffset += taosWcharWidth(wc); + shellShowOnScreen(cmd); +} + +void shellBackspaceChar(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset > 0) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + int32_t size = 0; + int32_t width = 0; + shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width); + memmove(cmd->command + cmd->cursorOffset - size, cmd->command + cmd->cursorOffset, + cmd->commandSize - cmd->cursorOffset); + cmd->commandSize -= size; + cmd->cursorOffset -= size; + cmd->screenOffset -= width; + cmd->endOffset -= width; + shellShowOnScreen(cmd); + } +} + +void shellClearLineBefore(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + memmove(cmd->command, cmd->command + cmd->cursorOffset, cmd->commandSize - cmd->cursorOffset); + cmd->commandSize -= cmd->cursorOffset; + cmd->cursorOffset = 0; + cmd->screenOffset = 0; + cmd->endOffset = cmd->commandSize; + shellShowOnScreen(cmd); +} + +void shellClearLineAfter(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + cmd->commandSize -= cmd->endOffset - cmd->cursorOffset; + cmd->endOffset = cmd->cursorOffset; + shellShowOnScreen(cmd); +} + +void shellDeleteChar(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset < cmd->commandSize) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + int32_t size = 0; + int32_t width = 0; + shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width); + memmove(cmd->command + cmd->cursorOffset, cmd->command + cmd->cursorOffset + size, + cmd->commandSize - cmd->cursorOffset - size); + cmd->commandSize -= size; + cmd->endOffset -= width; + shellShowOnScreen(cmd); + } +} + +void shellMoveCursorLeft(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset > 0) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + int32_t size = 0; + int32_t width = 0; + shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width); + cmd->cursorOffset -= size; + cmd->screenOffset -= width; + shellShowOnScreen(cmd); + } +} + +void shellMoveCursorRight(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset < cmd->commandSize) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + int32_t size = 0; + int32_t width = 0; + shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width); + cmd->cursorOffset += size; + cmd->screenOffset += width; + shellShowOnScreen(cmd); + } +} + +void shellPositionCursorHome(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset > 0) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + cmd->cursorOffset = 0; + cmd->screenOffset = 0; + shellShowOnScreen(cmd); + } +} + +void shellPositionCursorEnd(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (cmd->cursorOffset < cmd->commandSize) { + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + cmd->cursorOffset = cmd->commandSize; + cmd->screenOffset = cmd->endOffset; + shellShowOnScreen(cmd); + } +} + +void shellPrintChar(char c, int32_t times) { + for (int32_t i = 0; i < times; i++) { + fprintf(stdout, "%c", c); + } + fflush(stdout); +} + +void shellPositionCursor(int32_t step, int32_t direction) { + if (step > 0) { + if (direction == LEFT) { + fprintf(stdout, "\033[%dD", step); + } else if (direction == RIGHT) { + fprintf(stdout, "\033[%dC", step); + } else if (direction == UP) { + fprintf(stdout, "\033[%dA", step); + } else if (direction == DOWN) { + fprintf(stdout, "\033[%dB", step); + } + fflush(stdout); + } +} + +void shellUpdateBuffer(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + if (shellRegexMatch(cmd->buffer, "(\\s+$)|(^$)", REG_EXTENDED)) strcat(cmd->command, " "); + strcat(cmd->buffer, cmd->command); + cmd->bufferSize += cmd->commandSize; + + memset(cmd->command, 0, SHELL_MAX_COMMAND_SIZE); + cmd->cursorOffset = 0; + cmd->screenOffset = 0; + cmd->commandSize = 0; + cmd->endOffset = 0; + shellShowOnScreen(cmd); +} + +int32_t shellIsReadyGo(SShellCmd *cmd) { + assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); + + char *total = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE); + memset(cmd->command + cmd->commandSize, 0, SHELL_MAX_COMMAND_SIZE - cmd->commandSize); + sprintf(total, "%s%s", cmd->buffer, cmd->command); + + char *reg_str = + "(^.*;\\s*$)|(^\\s*$)|(^\\s*exit\\s*$)|(^\\s*q\\s*$)|(^\\s*quit\\s*$)|(^" + "\\s*clear\\s*$)"; + if (shellRegexMatch(total, reg_str, REG_EXTENDED | REG_ICASE)) { + taosMemoryFree(total); + return 1; + } + + taosMemoryFree(total); + return 0; +} + +void shellGetMbSizeInfo(const char *str, int32_t *size, int32_t *width) { + TdWchar *wc = (TdWchar *)taosMemoryCalloc(sizeof(TdWchar), SHELL_MAX_COMMAND_SIZE); *size = strlen(str); - taosMbsToWchars(wc, str, MAX_COMMAND_SIZE); - *width = taosWcharsWidth(wc, MAX_COMMAND_SIZE); + taosMbsToWchars(wc, str, SHELL_MAX_COMMAND_SIZE); + *width = taosWcharsWidth(wc, SHELL_MAX_COMMAND_SIZE); taosMemoryFree(wc); } -void resetCommand(Command *cmd, const char s[]) { +void shellResetCommand(SShellCmd *cmd, const char s[]) { assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset); - clearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size); - memset(cmd->buffer, 0, MAX_COMMAND_SIZE); - memset(cmd->command, 0, MAX_COMMAND_SIZE); - strncpy(cmd->command, s, MAX_COMMAND_SIZE); - int size = 0; - int width = 0; - getMbSizeInfo(s, &size, &width); + shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE); + memset(cmd->buffer, 0, SHELL_MAX_COMMAND_SIZE); + memset(cmd->command, 0, SHELL_MAX_COMMAND_SIZE); + strncpy(cmd->command, s, SHELL_MAX_COMMAND_SIZE); + int32_t size = 0; + int32_t width = 0; + shellGetMbSizeInfo(s, &size, &width); cmd->bufferSize = 0; cmd->commandSize = size; cmd->cursorOffset = size; cmd->screenOffset = width; cmd->endOffset = width; - showOnScreen(cmd); + shellShowOnScreen(cmd); } + +void shellClearScreen(int32_t ecmd_pos, int32_t 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; + } + + int32_t cursor_x = cursor_pos / w.ws_col; + int32_t cursor_y = cursor_pos % w.ws_col; + int32_t command_x = ecmd_pos / w.ws_col; + shellPositionCursor(cursor_y, LEFT); + shellPositionCursor(command_x - cursor_x, DOWN); + fprintf(stdout, "\033[2K"); + for (int32_t i = 0; i < command_x; i++) { + shellPositionCursor(1, UP); + fprintf(stdout, "\033[2K"); + } + fflush(stdout); +} + +void shellShowOnScreen(SShellCmd *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; + int32_t size = 0; + + // Print out the command. + char *total_string = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE); + memset(total_string, '\0', SHELL_MAX_COMMAND_SIZE); + if (strcmp(cmd->buffer, "") == 0) { + sprintf(total_string, "%s%s", shell.info.promptHeader, cmd->command); + } else { + sprintf(total_string, "%s%s", shell.info.promptContinue, cmd->command); + } + + int32_t remain_column = w.ws_col; + for (char *str = total_string; size < cmd->commandSize + PSIZE;) { + int32_t ret = taosMbToWchar(&wc, str, MB_CUR_MAX); + if (ret < 0) break; + size += ret; + /* assert(size >= 0); */ + int32_t 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; + } + + taosMemoryFree(total_string); + + // Position the cursor + int32_t cursor_pos = cmd->screenOffset + PSIZE; + int32_t ecmd_pos = cmd->endOffset + PSIZE; + + int32_t cursor_x = cursor_pos / w.ws_col; + int32_t cursor_y = cursor_pos % w.ws_col; + // int32_t cursor_y = cursor % w.ws_col; + int32_t command_x = ecmd_pos / w.ws_col; + int32_t command_y = ecmd_pos % w.ws_col; + // int32_t command_y = (command.size() + PSIZE) % w.ws_col; + shellPositionCursor(command_y, LEFT); + shellPositionCursor(command_x, UP); + shellPositionCursor(cursor_x, DOWN); + shellPositionCursor(cursor_y, RIGHT); + fflush(stdout); +} + +int32_t shellReadCommand(char *command) { + SShellHistory *pHistory = &shell.history; + SShellCmd cmd = {0}; + uint32_t hist_counter = pHistory->hend; + char utf8_array[10] = "\0"; + + cmd.buffer = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE); + cmd.command = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE); + shellShowOnScreen(&cmd); + + // Read input. + char c; + while (1) { + c = (char)getchar(); // getchar() return an 'int32_t' value + + if (c == EOF) { + return c; + } + + if (c < 0) { // For UTF-8 + int32_t count = shellCountPrefixOnes(c); + utf8_array[0] = c; + for (int32_t k = 1; k < count; k++) { + c = (char)getchar(); + utf8_array[k] = c; + } + shellInsertChar(&cmd, utf8_array, count); + } else if (c < '\033') { + // Ctrl keys. TODO: Implement ctrl combinations + switch (c) { + case 1: // ctrl A + shellPositionCursorHome(&cmd); + break; + case 3: + printf("\n"); + shellResetCommand(&cmd, ""); + kill(0, SIGINT); + break; + case 4: // EOF or Ctrl+D + printf("\n"); + return -1; + case 5: // ctrl E + shellPositionCursorEnd(&cmd); + break; + case 8: + shellBackspaceChar(&cmd); + break; + case '\n': + case '\r': + printf("\n"); + if (shellIsReadyGo(&cmd)) { + sprintf(command, "%s%s", cmd.buffer, cmd.command); + taosMemoryFreeClear(cmd.buffer); + taosMemoryFreeClear(cmd.command); + return 0; + } else { + shellUpdateBuffer(&cmd); + } + break; + case 11: // Ctrl + K; + shellClearLineAfter(&cmd); + break; + case 12: // Ctrl + L; + system("clear"); + shellShowOnScreen(&cmd); + break; + case 21: // Ctrl + U; + shellClearLineBefore(&cmd); + break; + } + } else if (c == '\033') { + c = (char)getchar(); + switch (c) { + case '[': + c = (char)getchar(); + switch (c) { + case 'A': // Up arrow + if (hist_counter != pHistory->hstart) { + hist_counter = (hist_counter + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE; + shellResetCommand(&cmd, (pHistory->hist[hist_counter] == NULL) ? "" : pHistory->hist[hist_counter]); + } + break; + case 'B': // Down arrow + if (hist_counter != pHistory->hend) { + int32_t next_hist = (hist_counter + 1) % SHELL_MAX_HISTORY_SIZE; + + if (next_hist != pHistory->hend) { + shellResetCommand(&cmd, (pHistory->hist[next_hist] == NULL) ? "" : pHistory->hist[next_hist]); + } else { + shellResetCommand(&cmd, ""); + } + hist_counter = next_hist; + } + break; + case 'C': // Right arrow + shellMoveCursorRight(&cmd); + break; + case 'D': // Left arrow + shellMoveCursorLeft(&cmd); + break; + case '1': + if ((c = (char)getchar()) == '~') { + // Home key + shellPositionCursorHome(&cmd); + } + break; + case '2': + if ((c = (char)getchar()) == '~') { + // Insert key + } + break; + case '3': + if ((c = (char)getchar()) == '~') { + // Delete key + shellDeleteChar(&cmd); + } + break; + case '4': + if ((c = (char)getchar()) == '~') { + // End key + shellPositionCursorEnd(&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 + shellPositionCursorHome(&cmd); + break; + case 70: + // End key + shellPositionCursorEnd(&cmd); + break; + } + break; + } + } else if (c == 0x7f) { + // press delete key + shellBackspaceChar(&cmd); + } else { + shellInsertChar(&cmd, &c, 1); + } + } + + return 0; +} + +#endif \ No newline at end of file diff --git a/tools/shell/src/shellEngine.c b/tools/shell/src/shellEngine.c index d8d157c887..b0928e1e4e 100644 --- a/tools/shell/src/shellEngine.c +++ b/tools/shell/src/shellEngine.c @@ -17,118 +17,35 @@ #define _GNU_SOURCE #define _XOPEN_SOURCE #define _DEFAULT_SOURCE - -#include "os.h" #include "shellInt.h" -#include "shellCommand.h" -#include "taosdef.h" -#include "taoserror.h" -#include "tconfig.h" -#include "tglobal.h" -#include "ttypes.h" -#include "tutil.h" +static bool shellIsEmptyCommand(const char *cmd); +static int32_t shellRunSingleCommand(char *command); +static int32_t shellRunCommand(char *command); +static void shellRunSingleCommandImp(char *command); +static char *shellFormatTimestamp(char *buf, int64_t val, int32_t precision); +static void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, + int32_t precision); +static int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres); +static void shellPrintNChar(const char *str, int32_t length, int32_t width); +static void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision); +static int32_t shellVerticalPrintResult(TAOS_RES *tres); +static int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision); +static void shellPrintHeader(TAOS_FIELD *fields, int32_t *width, int32_t num_fields); +static int32_t shellHorizontalPrintResult(TAOS_RES *tres); +static int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical); +static void shellReadHistory(); +static void shellWriteHistory(); +static void shellPrintError(TAOS_RES *tres, int64_t st); +static bool shellIsCommentLine(char *line); +static void shellSourceFile(const char *file); +static void shellGetGrantInfo(); +static void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context); +static void shellCleanup(void *arg); +static void *shellCancelHandler(void *arg); +static void *shellThreadLoop(void *arg); -/**************** Global variables ****************/ -#ifdef _TD_POWER_ -char CLIENT_VERSION[] = - "Welcome to the PowerDB shell from %s, Client Version:%s\n" - "Copyright (c) 2020 by PowerDB, Inc. All rights reserved.\n\n"; -char PROMPT_HEADER[] = "power> "; - -char CONTINUE_PROMPT[] = " -> "; -int prompt_size = 7; -#elif (_TD_TQ_ == true) -char CLIENT_VERSION[] = - "Welcome to the TQ shell from %s, Client Version:%s\n" - "Copyright (c) 2020 by TQ, Inc. All rights reserved.\n\n"; -char PROMPT_HEADER[] = "tq> "; - -char CONTINUE_PROMPT[] = " -> "; -int prompt_size = 4; -#else -char CLIENT_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 PROMPT_HEADER[] = "taos> "; - -char CONTINUE_PROMPT[] = " -> "; -int prompt_size = 6; -#endif - -int64_t result = 0; -SShellHistory history; - -#define DEFAULT_MAX_BINARY_DISPLAY_WIDTH 30 -extern int32_t tsMaxBinaryDisplayWidth; -extern TAOS *taos_connect_auth(const char *ip, const char *user, const char *auth, const char *db, uint16_t port); - -/* - * FUNCTION: Initialize the shell. - */ -TAOS *shellInit(SShellArgs *_args) { - printf("\n"); - if (!_args->is_use_passwd) { -#ifdef TD_WINDOWS - strcpy(tsOsName, "Windows"); -#elif defined(TD_DARWIN) - strcpy(tsOsName, "Darwin"); -#endif - printf(CLIENT_VERSION, tsOsName, taos_get_client_info()); - } - - fflush(stdout); - - // set options before initializing - if (_args->timezone != NULL) { - taos_options(TSDB_OPTION_TIMEZONE, _args->timezone); - } - - if (!_args->is_use_passwd) { - _args->password = TSDB_DEFAULT_PASS; - } - - if (_args->user == NULL) { - _args->user = TSDB_DEFAULT_USER; - } - - // Connect to the database. - TAOS *con = NULL; - if (_args->auth == NULL) { - con = taos_connect(_args->host, _args->user, _args->password, _args->database, _args->port); - } else { - con = taos_connect_auth(_args->host, _args->user, _args->auth, _args->database, _args->port); - } - - if (con == NULL) { - fflush(stdout); - return con; - } - - /* Read history TODO : release resources here*/ - shellReadHistory(); - - // Check if it is temperory run - if (_args->commands != NULL || _args->file[0] != 0) { - if (_args->commands != NULL) { - printf("%s%s\n", PROMPT_HEADER, _args->commands); - shellRunCommand(con, _args->commands); - } - - if (_args->file[0] != 0) { - shellSourceFile(con, _args->file); - } - - taos_close(con); - shellWriteHistory(); - exit(EXIT_SUCCESS); - } - - - return con; -} - -static bool isEmptyCommand(const char *cmd) { +bool shellIsEmptyCommand(const char *cmd) { for (char c = *cmd++; c != 0; c = *cmd++) { if (c != ' ' && c != '\t' && c != ';') { return false; @@ -137,37 +54,30 @@ static bool isEmptyCommand(const char *cmd) { return true; } -static int32_t shellRunSingleCommand(TAOS *con, char *command) { - /* If command is empty just return */ - if (isEmptyCommand(command)) { +int32_t shellRunSingleCommand(char *command) { + if (shellIsEmptyCommand(command)) { return 0; } - // Analyse the command. if (shellRegexMatch(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) { - taos_close(con); shellWriteHistory(); -#ifdef WINDOWS - exit(EXIT_SUCCESS); -#endif return -1; } if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) { - // If clear the screen. system("clear"); return 0; } if (shellRegexMatch(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$", - REG_EXTENDED | REG_ICASE)) { + REG_EXTENDED | REG_ICASE)) { strtok(command, " \t"); strtok(NULL, " \t"); char *p = strtok(NULL, " \t"); if (strcasecmp(p, "default") == 0) { - tsMaxBinaryDisplayWidth = DEFAULT_MAX_BINARY_DISPLAY_WIDTH; + shell.args.displayWidth = SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH; } else { - tsMaxBinaryDisplayWidth = atoi(p); + shell.args.displayWidth = atoi(p); } return 0; } @@ -178,32 +88,31 @@ static int32_t shellRunSingleCommand(TAOS *con, char *command) { assert(c_ptr != NULL); c_ptr = strtok(NULL, " ;"); assert(c_ptr != NULL); - shellSourceFile(con, c_ptr); + shellSourceFile(c_ptr); return 0; } - shellRunCommandImp(con, command); + shellRunSingleCommandImp(command); return 0; } -int32_t shellRunCommand(TAOS *con, char *command) { - /* If command is empty just return */ - if (isEmptyCommand(command)) { +int32_t shellRunCommand(char *command) { + if (shellIsEmptyCommand(command)) { return 0; } - /* Update the history vector. */ - if (history.hstart == history.hend || - history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL || - strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) { - if (history.hist[history.hend] != NULL) { - taosMemoryFreeClear(history.hist[history.hend]); + SShellHistory *pHistory = &shell.history; + if (pHistory->hstart == pHistory->hend || + pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE] == NULL || + strcmp(command, pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE]) != 0) { + if (pHistory->hist[pHistory->hend] != NULL) { + taosMemoryFreeClear(pHistory->hist[pHistory->hend]); } - history.hist[history.hend] = strdup(command); + pHistory->hist[pHistory->hend] = strdup(command); - history.hend = (history.hend + 1) % MAX_HISTORY_SIZE; - if (history.hend == history.hstart) { - history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE; + pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE; + if (pHistory->hend == pHistory->hstart) { + pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE; } } @@ -255,7 +164,7 @@ int32_t shellRunCommand(TAOS *con, char *command) { if (c == ';' && quote == 0) { c = *p; *p = 0; - if (shellRunSingleCommand(con, cmd) < 0) { + if (shellRunSingleCommand(cmd) < 0) { return -1; } *p = c; @@ -264,20 +173,10 @@ int32_t shellRunCommand(TAOS *con, char *command) { } *p = 0; - return shellRunSingleCommand(con, cmd); + return shellRunSingleCommand(cmd); } -void freeResultWithRid(int64_t rid) { -#if 0 - SSqlObj* pSql = taosAcquireRef(tscObjRef, rid); - if(pSql){ - taos_free_result(pSql); - taosReleaseRef(tscObjRef, rid); - } -#endif -} - -void shellRunCommandImp(TAOS *con, char command[]) { +void shellRunSingleCommandImp(char *command) { int64_t st, et; wordexp_t full_path; char *sptr = NULL; @@ -291,12 +190,8 @@ void shellRunCommandImp(TAOS *con, char command[]) { *cptr = '\0'; } - if (wordexp(sptr + 2, &full_path, 0) != 0) { - fprintf(stderr, "ERROR: invalid filename: %s\n", sptr + 2); - return; - } + fname = sptr + 2; *sptr = '\0'; - fname = full_path.we_wordv[0]; } if ((sptr = strstr(command, "\\G")) != NULL) { @@ -311,20 +206,19 @@ void shellRunCommandImp(TAOS *con, char command[]) { st = taosGetTimestampUs(); - TAOS_RES *pSql = taos_query(con, command); + TAOS_RES *pSql = taos_query(shell.conn, command); if (taos_errno(pSql)) { shellPrintError(pSql, st); return; } - int64_t oresult = atomic_load_64(&result); + int64_t oresult = atomic_load_64(&shell.result); if (shellRegexMatch(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) { fprintf(stdout, "Database changed.\n\n"); fflush(stdout); - atomic_store_64(&result, 0); - freeResultWithRid(oresult); + atomic_store_64(&shell.result, 0); taos_free_result(pSql); return; @@ -332,12 +226,11 @@ void shellRunCommandImp(TAOS *con, char command[]) { TAOS_FIELD *pFields = taos_fetch_fields(pSql); if (pFields != NULL) { // select and show kinds of commands - int error_no = 0; + int32_t error_no = 0; - int numOfRows = shellDumpResult(pSql, fname, &error_no, printMode); + int32_t numOfRows = shellDumpResult(pSql, fname, &error_no, printMode); if (numOfRows < 0) { - atomic_store_64(&result, 0); - freeResultWithRid(oresult); + atomic_store_64(&shell.result, 0); return; } @@ -349,7 +242,7 @@ void shellRunCommandImp(TAOS *con, char command[]) { } taos_free_result(pSql); } else { - int num_rows_affacted = taos_affected_rows(pSql); + int32_t num_rows_affacted = taos_affected_rows(pSql); taos_free_result(pSql); et = taosGetTimestampUs(); printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6); @@ -361,41 +254,11 @@ void shellRunCommandImp(TAOS *con, char command[]) { wordfree(&full_path); } - atomic_store_64(&result, 0); - freeResultWithRid(oresult); + atomic_store_64(&shell.result, 0); } -/* Function to do regular expression check */ -int shellRegexMatch(const char *s, const char *reg, int cflags) { - regex_t regex; - char msgbuf[100] = {0}; - - /* Compile regular expression */ - if (regcomp(®ex, reg, cflags) != 0) { - fprintf(stderr, "Fail to compile regex"); - shellExit(); - } - - /* Execute regular expression */ - int reti = regexec(®ex, s, 0, NULL, 0); - if (!reti) { - regfree(®ex); - return 1; - } else if (reti == REG_NOMATCH) { - regfree(®ex); - return 0; - } else { - regerror(reti, ®ex, msgbuf, sizeof(msgbuf)); - fprintf(stderr, "Regex match failed: %s\n", msgbuf); - regfree(®ex); - shellExit(); - } - - return 0; -} - -static char *formatTimestamp(char *buf, int64_t val, int precision) { - if (args.is_raw_time) { +char *shellFormatTimestamp(char *buf, int64_t val, int32_t precision) { + if (shell.args.is_raw_time) { sprintf(buf, "%" PRId64, val); return buf; } @@ -413,13 +276,14 @@ static char *formatTimestamp(char *buf, int64_t val, int precision) { ms = val % 1000; } - /* comment out as it make testcases like select_with_tags.sim fail. + /* + comment out as it make testcases like select_with_tags.sim fail. but in windows, this may cause the call to localtime crash if tt < 0, need to find a better solution. if (tt < 0) { tt = 0; } - */ + */ #ifdef WINDOWS if (tt < 0) tt = 0; @@ -449,7 +313,7 @@ static char *formatTimestamp(char *buf, int64_t val, int precision) { return buf; } -static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int precision) { +void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int32_t precision) { if (val == NULL) { taosFprintfFile(pFile, "%s", TSDB_DATA_NULL_STR); return; @@ -485,7 +349,7 @@ static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, taosFprintfFile(pFile, "\'%s\'", buf); break; case TSDB_DATA_TYPE_TIMESTAMP: - formatTimestamp(buf, *(int64_t *)val, precision); + shellFormatTimestamp(buf, *(int64_t *)val, precision); taosFprintfFile(pFile, "'%s'", buf); break; default: @@ -493,35 +357,23 @@ static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, } } -static int dumpResultToFile(const char *fname, TAOS_RES *tres) { +int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres) { TAOS_ROW row = taos_fetch_row(tres); if (row == NULL) { return 0; } - wordexp_t full_path; - - if (wordexp((char *)fname, &full_path, 0) != 0) { - fprintf(stderr, "ERROR: invalid file name: %s\n", fname); - return -1; - } - - // FILE *fp = fopen(full_path.we_wordv[0], "w"); - TdFilePtr pFile = - taosOpenFile(full_path.we_wordv[0], TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM); + TdFilePtr pFile = taosOpenFile(fname, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM); if (pFile == NULL) { - fprintf(stderr, "ERROR: failed to open file: %s\n", full_path.we_wordv[0]); - wordfree(&full_path); + fprintf(stderr, "ERROR: failed to open file: %s\n", fname); return -1; } - wordfree(&full_path); - - int num_fields = taos_num_fields(tres); TAOS_FIELD *fields = taos_fetch_fields(tres); - int precision = taos_result_precision(tres); + int32_t num_fields = taos_num_fields(tres); + int32_t precision = taos_result_precision(tres); - for (int col = 0; col < num_fields; col++) { + for (int32_t col = 0; col < num_fields; col++) { if (col > 0) { taosFprintfFile(pFile, ","); } @@ -529,14 +381,14 @@ static int dumpResultToFile(const char *fname, TAOS_RES *tres) { } taosFprintfFile(pFile, "\n"); - int numOfRows = 0; + int32_t numOfRows = 0; do { int32_t *length = taos_fetch_lengths(tres); - for (int i = 0; i < num_fields; i++) { + for (int32_t i = 0; i < num_fields; i++) { if (i > 0) { taosFprintfFile(pFile, "\n"); } - dumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision); + shellDumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision); } taosFprintfFile(pFile, "\n"); @@ -544,19 +396,19 @@ static int dumpResultToFile(const char *fname, TAOS_RES *tres) { row = taos_fetch_row(tres); } while (row != NULL); - result = 0; + shell.result = 0; taosCloseFile(&pFile); return numOfRows; } -static void shellPrintNChar(const char *str, int length, int width) { +void shellPrintNChar(const char *str, int32_t length, int32_t width) { TdWchar tail[3]; - int pos = 0, cols = 0, totalCols = 0, tailLen = 0; + int32_t pos = 0, cols = 0, totalCols = 0, tailLen = 0; while (pos < length) { TdWchar wc; - int bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); + int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); if (bytes == 0) { break; } @@ -566,9 +418,9 @@ static void shellPrintNChar(const char *str, int length, int width) { } #ifdef WINDOWS - int w = bytes; + int32_t w = bytes; #else - int w = taosWcharWidth(wc); + int32_t w = taosWcharWidth(wc); #endif if (w <= 0) { continue; @@ -594,7 +446,7 @@ static void shellPrintNChar(const char *str, int length, int width) { if (totalCols > width) { // width could be 1 or 2, so printf("...") cannot be used - for (int i = 0; i < 3; i++) { + for (int32_t i = 0; i < 3; i++) { if (cols >= width) { break; } @@ -602,7 +454,7 @@ static void shellPrintNChar(const char *str, int length, int width) { ++cols; } } else { - for (int i = 0; i < tailLen; i++) { + for (int32_t i = 0; i < tailLen; i++) { printf("%lc", tail[i]); } cols = totalCols; @@ -613,9 +465,9 @@ static void shellPrintNChar(const char *str, int length, int width) { } } -static void printField(const char *val, TAOS_FIELD *field, int width, int32_t length, int precision) { +void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision) { if (val == NULL) { - int w = width; + int32_t w = width; if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) { w = 0; } @@ -666,7 +518,7 @@ static void printField(const char *val, TAOS_FIELD *field, int width, int32_t le shellPrintNChar(val, length, width); break; case TSDB_DATA_TYPE_TIMESTAMP: - formatTimestamp(buf, *(int64_t *)val, precision); + shellFormatTimestamp(buf, *(int64_t *)val, precision); printf("%s", buf); break; default: @@ -674,30 +526,19 @@ static void printField(const char *val, TAOS_FIELD *field, int width, int32_t le } } -bool isSelectQuery(TAOS_RES *tres) { -#if 0 - char *sql = tscGetSqlStr(tres); - - if (shellRegexMatch(sql, "^[\t ]*select[ \t]*", REG_EXTENDED | REG_ICASE)) { - return true; - } -#endif - return false; -} - -static int verticalPrintResult(TAOS_RES *tres) { +int32_t shellVerticalPrintResult(TAOS_RES *tres) { TAOS_ROW row = taos_fetch_row(tres); if (row == NULL) { return 0; } - int num_fields = taos_num_fields(tres); + int32_t num_fields = taos_num_fields(tres); TAOS_FIELD *fields = taos_fetch_fields(tres); - int precision = taos_result_precision(tres); + int32_t precision = taos_result_precision(tres); - int maxColNameLen = 0; - for (int col = 0; col < num_fields; col++) { - int len = (int)strlen(fields[col].name); + int32_t maxColNameLen = 0; + for (int32_t col = 0; col < num_fields; col++) { + int32_t len = (int32_t)strlen(fields[col].name); if (len > maxColNameLen) { maxColNameLen = len; } @@ -705,25 +546,25 @@ static int verticalPrintResult(TAOS_RES *tres) { uint64_t resShowMaxNum = UINT64_MAX; - if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /*&& !tscIsQueryWithLimit(tres)*/) { - resShowMaxNum = DEFAULT_RES_SHOW_NUM; + if (shell.args.commands == NULL && shell.args.file[0] == 0) { + resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM; } - int numOfRows = 0; - int showMore = 1; + int32_t numOfRows = 0; + int32_t showMore = 1; do { if (numOfRows < resShowMaxNum) { printf("*************************** %d.row ***************************\n", numOfRows + 1); int32_t *length = taos_fetch_lengths(tres); - for (int i = 0; i < num_fields; i++) { + for (int32_t i = 0; i < num_fields; i++) { TAOS_FIELD *field = fields + i; - int padding = (int)(maxColNameLen - strlen(field->name)); + int32_t padding = (int32_t)(maxColNameLen - strlen(field->name)); printf("%*.s%s: ", padding, " ", field->name); - printField((const char *)row[i], field, 0, length[i], precision); + shellPrintField((const char *)row[i], field, 0, length[i], precision); putchar('\n'); } } else if (showMore) { @@ -739,8 +580,8 @@ static int verticalPrintResult(TAOS_RES *tres) { return numOfRows; } -static int calcColWidth(TAOS_FIELD *field, int precision) { - int width = (int)strlen(field->name); +int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) { + int32_t width = (int32_t)strlen(field->name); switch (field->type) { case TSDB_DATA_TYPE_BOOL: @@ -769,23 +610,23 @@ static int calcColWidth(TAOS_FIELD *field, int precision) { return TMAX(25, width); case TSDB_DATA_TYPE_BINARY: - if (field->bytes > tsMaxBinaryDisplayWidth) { - return TMAX(tsMaxBinaryDisplayWidth, width); + if (field->bytes > shell.args.displayWidth) { + return TMAX(shell.args.displayWidth, width); } else { return TMAX(field->bytes, width); } case TSDB_DATA_TYPE_NCHAR: { int16_t bytes = field->bytes * TSDB_NCHAR_SIZE; - if (bytes > tsMaxBinaryDisplayWidth) { - return TMAX(tsMaxBinaryDisplayWidth, width); + if (bytes > shell.args.displayWidth) { + return TMAX(shell.args.displayWidth, width); } else { return TMAX(bytes, width); } } case TSDB_DATA_TYPE_TIMESTAMP: - if (args.is_raw_time) { + if (shell.args.is_raw_time) { return TMAX(14, width); } if (precision == TSDB_TIME_PRECISION_NANO) { @@ -803,55 +644,55 @@ static int calcColWidth(TAOS_FIELD *field, int precision) { return 0; } -static void printHeader(TAOS_FIELD *fields, int *width, int num_fields) { - int rowWidth = 0; - for (int col = 0; col < num_fields; col++) { +void shellPrintHeader(TAOS_FIELD *fields, int32_t *width, int32_t num_fields) { + int32_t rowWidth = 0; + for (int32_t col = 0; col < num_fields; col++) { TAOS_FIELD *field = fields + col; - int padding = (int)(width[col] - strlen(field->name)); - int left = padding / 2; + int32_t padding = (int32_t)(width[col] - strlen(field->name)); + int32_t left = padding / 2; printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " "); rowWidth += width[col] + 3; } putchar('\n'); - for (int i = 0; i < rowWidth; i++) { + for (int32_t i = 0; i < rowWidth; i++) { putchar('='); } putchar('\n'); } -static int horizontalPrintResult(TAOS_RES *tres) { +int32_t shellHorizontalPrintResult(TAOS_RES *tres) { TAOS_ROW row = taos_fetch_row(tres); if (row == NULL) { return 0; } - int num_fields = taos_num_fields(tres); + int32_t num_fields = taos_num_fields(tres); TAOS_FIELD *fields = taos_fetch_fields(tres); - int precision = taos_result_precision(tres); + int32_t precision = taos_result_precision(tres); - int width[TSDB_MAX_COLUMNS]; - for (int col = 0; col < num_fields; col++) { - width[col] = calcColWidth(fields + col, precision); + int32_t width[TSDB_MAX_COLUMNS]; + for (int32_t col = 0; col < num_fields; col++) { + width[col] = shellCalcColWidth(fields + col, precision); } - printHeader(fields, width, num_fields); + shellPrintHeader(fields, width, num_fields); uint64_t resShowMaxNum = UINT64_MAX; - if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /* && !tscIsQueryWithLimit(tres)*/) { - resShowMaxNum = DEFAULT_RES_SHOW_NUM; + if (shell.args.commands == NULL && shell.args.file[0] == 0) { + resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM; } - int numOfRows = 0; - int showMore = 1; + int32_t numOfRows = 0; + int32_t showMore = 1; do { int32_t *length = taos_fetch_lengths(tres); if (numOfRows < resShowMaxNum) { - for (int i = 0; i < num_fields; i++) { + for (int32_t i = 0; i < num_fields; i++) { putchar(' '); - printField((const char *)row[i], fields + i, width[i], length[i], precision); + shellPrintField((const char *)row[i], fields + i, width[i], length[i], precision); putchar(' '); putchar('|'); } @@ -869,14 +710,14 @@ static int horizontalPrintResult(TAOS_RES *tres) { return numOfRows; } -int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) { - int numOfRows = 0; +int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical) { + int32_t numOfRows = 0; if (fname != NULL) { - numOfRows = dumpResultToFile(fname, tres); + numOfRows = shellDumpResultToFile(fname, tres); } else if (vertical) { - numOfRows = verticalPrintResult(tres); + numOfRows = shellVerticalPrintResult(tres); } else { - numOfRows = horizontalPrintResult(tres); + numOfRows = shellHorizontalPrintResult(tres); } *error_no = taos_errno(tres); @@ -884,35 +725,20 @@ int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) { } void shellReadHistory() { - // Initialize history - memset(history.hist, 0, sizeof(char *) * MAX_HISTORY_SIZE); - history.hstart = 0; - history.hend = 0; - char *line = NULL; - int read_size = 0; - - char f_history[TSDB_FILENAME_LEN]; - shellHistoryPath(f_history); - - // FILE *f = fopen(f_history, "r"); - TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_READ | TD_FILE_STREAM); - if (pFile == NULL) { -#ifndef WINDOWS - if (errno != ENOENT) { - fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno)); - } -#endif - return; - } + SShellHistory *pHistory = &shell.history; + TdFilePtr pFile = taosOpenFile(pHistory->file, TD_FILE_READ | TD_FILE_STREAM); + if (pFile == NULL) return; + char *line = NULL; + int32_t read_size = 0; while ((read_size = taosGetLineFile(pFile, &line)) != -1) { line[read_size - 1] = '\0'; - history.hist[history.hend] = strdup(line); + pHistory->hist[pHistory->hend] = strdup(line); - history.hend = (history.hend + 1) % MAX_HISTORY_SIZE; + pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE; - if (history.hend == history.hstart) { - history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE; + if (pHistory->hend == pHistory->hstart) { + pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE; } } @@ -921,71 +747,41 @@ void shellReadHistory() { } void shellWriteHistory() { - char f_history[TSDB_FILENAME_LEN]; - shellHistoryPath(f_history); + SShellHistory *pHistory = &shell.history; + TdFilePtr pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM); + if (pFile == NULL) return; - // FILE *f = fopen(f_history, "w"); - TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM); - if (pFile == NULL) { -#ifndef WINDOWS - fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno)); -#endif - return; - } - - for (int i = history.hstart; i != history.hend;) { - if (history.hist[i] != NULL) { - taosFprintfFile(pFile, "%s\n", history.hist[i]); - taosMemoryFreeClear(history.hist[i]); + for (int32_t i = pHistory->hstart; i != pHistory->hend;) { + if (pHistory->hist[i] != NULL) { + taosFprintfFile(pFile, "%s\n", pHistory->hist[i]); + taosMemoryFreeClear(pHistory->hist[i]); } - i = (i + 1) % MAX_HISTORY_SIZE; + i = (i + 1) % SHELL_MAX_HISTORY_SIZE; } taosCloseFile(&pFile); } void shellPrintError(TAOS_RES *tres, int64_t st) { int64_t et = taosGetTimestampUs(); - atomic_store_ptr(&result, 0); + atomic_store_ptr(&shell.result, 0); fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6); taos_free_result(tres); } -int isCommentLine(char *line) { - if (line == NULL) return 1; - +bool shellIsCommentLine(char *line) { + if (line == NULL) return true; return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED); } -void shellSourceFile(TAOS *con, char *fptr) { - wordexp_t full_path; - int read_len = 0; - char *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1); - size_t cmd_len = 0; - char *line = NULL; +void shellSourceFile(const char *file) { + int32_t read_len = 0; + char *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1); + size_t cmd_len = 0; + char *line = NULL; - if (wordexp(fptr, &full_path, 0) != 0) { - fprintf(stderr, "ERROR: illegal file name\n"); - taosMemoryFree(cmd); - return; - } - - char *fname = full_path.we_wordv[0]; - - /* - if (access(fname, F_OK) != 0) { - fprintf(stderr, "ERROR: file %s is not exist\n", fptr); - - wordfree(&full_path); - taosMemoryFree(cmd); - return; - } - */ - - // FILE *f = fopen(fname, "r"); - TdFilePtr pFile = taosOpenFile(fname, TD_FILE_READ | TD_FILE_STREAM); + TdFilePtr pFile = taosOpenFile(file, TD_FILE_READ | TD_FILE_STREAM); if (pFile == NULL) { - fprintf(stderr, "ERROR: failed to open file %s\n", fname); - wordfree(&full_path); + fprintf(stderr, "ERROR: failed to open file %s\n", file); taosMemoryFree(cmd); return; } @@ -994,7 +790,7 @@ void shellSourceFile(TAOS *con, char *fptr) { if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue; line[--read_len] = '\0'; - if (read_len == 0 || isCommentLine(line)) { // line starts with # + if (read_len == 0 || shellIsCommentLine(line)) { // line starts with # continue; } @@ -1006,36 +802,34 @@ void shellSourceFile(TAOS *con, char *fptr) { } memcpy(cmd + cmd_len, line, read_len); - printf("%s%s\n", PROMPT_HEADER, cmd); - shellRunCommand(con, cmd); + printf("%s%s\n", shell.info.promptHeader, cmd); + shellRunCommand(cmd); memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN); cmd_len = 0; } taosMemoryFree(cmd); if (line != NULL) taosMemoryFree(line); - wordfree(&full_path); taosCloseFile(&pFile); } -void shellGetGrantInfo(void *con) { - return; -#if 0 +void shellGetGrantInfo() { char sql[] = "show grants"; - TAOS_RES* tres = taos_query(con, sql); + TAOS_RES *tres = taos_query(shell.conn, sql); - int code = taos_errno(tres); + int32_t code = taos_errno(tres); if (code != TSDB_CODE_SUCCESS) { - if (code == TSDB_CODE_COM_OPS_NOT_SUPPORT) { - fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(con)); + if (code == TSDB_CODE_OPS_NOT_SUPPORT) { + fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(shell.conn)); } else { - fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(con), taos_errstr(con)); + fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(shell.conn), + taos_errstr(shell.conn)); } return; } - int num_fields = taos_field_count(tres); + int32_t num_fields = taos_field_count(tres); if (num_fields == 0) { fprintf(stderr, "\nInvalid grant information.\n"); exit(0); @@ -1046,7 +840,7 @@ void shellGetGrantInfo(void *con) { } TAOS_FIELD *fields = taos_fetch_fields(tres); - TAOS_ROW row = taos_fetch_row(tres); + TAOS_ROW row = taos_fetch_row(tres); if (row == NULL) { fprintf(stderr, "\nFailed to get grant information from server. Abort.\n"); exit(0); @@ -1061,15 +855,120 @@ void shellGetGrantInfo(void *con) { memcpy(expired, row[2], fields[2].bytes); if (strcmp(expiretime, "unlimited") == 0) { - fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con)); + fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, + taos_get_server_info(shell.conn)); } else { - fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will expire at %s.\n", serverVersion, taos_get_server_info(con), expiretime); + fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will expire at %s.\n", serverVersion, + taos_get_server_info(shell.conn), expiretime); } - result = NULL; + shell.result = 0; taos_free_result(tres); } fprintf(stdout, "\n"); -#endif +} + +void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&shell.cancelSem); } + +void shellCleanup(void *arg) { taosResetTerminalMode(); } + +void *shellCancelHandler(void *arg) { + setThreadName("shellCancelHandler"); + while (1) { + if (tsem_wait(&shell.cancelSem) != 0) { + taosMsleep(10); + continue; + } + + taosResetTerminalMode(); + printf("\nReceive ctrl+c or other signal, quit shell.\n"); + // shellExit(); + } + + return NULL; +} + +void *shellThreadLoop(void *arg) { + setThreadName("shellThreadLoop"); + taosGetOldTerminalMode(); + taosThreadCleanupPush(shellCleanup, NULL); + + char *command = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE); + if (command == NULL) { + printf("failed to malloc command\n"); + return NULL; + } + + do { + memset(command, 0, SHELL_MAX_COMMAND_SIZE); + taosSetTerminalMode(); + + if (shellReadCommand(command) != 0) { + break; + } + + taosResetTerminalMode(); + } while (shellRunCommand(command) == 0); + + taosMemoryFreeClear(command); + taosThreadCleanupPop(1); + return NULL; +} + +int32_t shellExecute() { + printf(shell.info.clientVersion, shell.info.osname, taos_get_client_info()); + fflush(stdout); + + SShellArgs *pArgs = &shell.args; + if (shell.args.auth == NULL) { + shell.conn = taos_connect(pArgs->host, pArgs->user, pArgs->password, pArgs->database, pArgs->port); + } else { + shell.conn = taos_connect_auth(pArgs->host, pArgs->user, pArgs->auth, pArgs->database, pArgs->port); + } + + if (shell.conn == NULL) { + fflush(stdout); + return -1; + } + + if (pArgs->commands != NULL || pArgs->file[0] != 0) { + if (pArgs->commands != NULL) { + printf("%s%s\n", shell.info.promptHeader, pArgs->commands); + char *cmd = strdup(pArgs->commands); + shellRunCommand(cmd); + taosMemoryFree(cmd); + } + + if (pArgs->file[0] != 0) { + shellSourceFile(pArgs->file); + } + + taos_close(shell.conn); + shellWriteHistory(); + return 0; + } + + if (tsem_init(&shell.cancelSem, 0, 0) != 0) { + printf("failed to create cancel semphore\n"); + return -1; + } + + TdThread spid = {0}; + taosThreadCreate(&spid, NULL, shellCancelHandler, NULL); + + taosSetSignal(SIGTERM, shellQueryInterruptHandler); + taosSetSignal(SIGINT, shellQueryInterruptHandler); + taosSetSignal(SIGHUP, shellQueryInterruptHandler); + taosSetSignal(SIGABRT, shellQueryInterruptHandler); + + shellGetGrantInfo(shell.conn); + shellReadHistory(); + + while (1) { + taosThreadCreate(&shell.pid, NULL, shellThreadLoop, shell.conn); + taosThreadJoin(shell.pid, NULL); + } + + return 0; } diff --git a/tools/shell/src/shellMain.c b/tools/shell/src/shellMain.c index 7c7d80cb30..366702c6fc 100644 --- a/tools/shell/src/shellMain.c +++ b/tools/shell/src/shellMain.c @@ -14,682 +14,52 @@ */ #define __USE_XOPEN -#include "shellCommand.h" -#include "tglobal.h" +#include "shellInt.h" -#include -#include -#include - -#define OPT_ABORT 1 /* abort */ - -int indicator = 1; - -void insertChar(Command *cmd, char *c, int size); -void taosNetTest(char *role, char *host, int32_t port, int32_t pkgLen, int32_t pkgNum, char *pkgType); -const char *argp_program_version = version; -const char *argp_program_bug_address = ""; -static char doc[] = ""; -static char args_doc[] = ""; - -TdThread pid; -static tsem_t cancelSem; -extern void taos_init(); - -static struct argp_option options[] = { - {"host", 'h', "HOST", 0, "TDengine server FQDN to connect. The default host is localhost."}, - {"password", 'p', NULL, 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', NULL, 0, "Dump configuration."}, - {"commands", 's', "COMMANDS", 0, "Commands to run without enter the shell."}, - {"raw-time", 'r', NULL, 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."}, - {"status", 't', NULL, 0, "Check the service status."}, - {"verbose", 'v', NULL, 0, "Check the details of the service status."}, - {"netrole", 'n', "NETROLE", 0, "Net role when network connectivity test, default is startup, options: client|server|rpc|startup|sync|speed|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."}, -// Shuduo: 3.0 does not support UDP any more -// {"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. */ - SShellArgs *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 't': - arguments->status = true; - break; - case 'v': - arguments->verbose = true; - 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[], SShellArgs *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; - } - } -} - -int32_t shellParseArgs(int argc, char *argv[], SShellArgs *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 - } - - return 0; -} - -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 *)taosMemoryCalloc(1, MAX_COMMAND_SIZE); - cmd.command = (char *)taosMemoryCalloc(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 - shellWriteHistory(); - shellExit(); - 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); - taosMemoryFreeClear(cmd.buffer); - taosMemoryFreeClear(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 *shellThreadLoop(void *arg) { - if (indicator) { - getOldTerminalMode(); - indicator = 0; - } - - TAOS *con = (TAOS *)arg; - - setThreadName("shellThreadLoop"); - - taosThreadCleanupPush(shellCleanup, NULL); - - char *command = taosMemoryMalloc(MAX_COMMAND_SIZE); - if (command == NULL) { - printf("failed to malloc command\n"); - 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); - - taosMemoryFreeClear(command); - shellExit(); - - taosThreadCleanupPop(1); - - return NULL; -} - -void shellHistoryPath(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 = taosMemoryMalloc(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; - } - - taosMemoryFree(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 shellCleanup(void *arg) { resetTerminalMode(); } - -void shellExit() { - taos_cleanup(); - exit(EXIT_SUCCESS); -} - -void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&cancelSem); } - -void *cancelHandler(void *arg) { - setThreadName("cancelHandler"); - - while (1) { - if (tsem_wait(&cancelSem) != 0) { - taosMsleep(10); - continue; - } - - resetTerminalMode(); - printf("\nReceive ctrl+c or other signal, quit shell.\n"); - shellExit(); - } - - return NULL; -} - -int checkVersion() { - if (sizeof(int8_t) != 1) { - printf("taos int8 size is %d(!= 1)", (int)sizeof(int8_t)); - return 0; - } - if (sizeof(int16_t) != 2) { - printf("taos int16 size is %d(!= 2)", (int)sizeof(int16_t)); - return 0; - } - if (sizeof(int32_t) != 4) { - printf("taos int32 size is %d(!= 4)", (int)sizeof(int32_t)); - return 0; - } - if (sizeof(int64_t) != 8) { - printf("taos int64 size is %d(!= 8)", (int)sizeof(int64_t)); - return 0; - } - return 1; -} - -// Global configurations -SShellArgs args = { - .host = NULL, - .user = NULL, - .database = NULL, - .timezone = NULL, - .is_raw_time = false, - .is_use_passwd = false, - .dump_config = false, - .file = "\0", - .dir = "\0", - .threadNum = 5, - .commands = NULL, - .pktLen = 1000, - .pktNum = 100, - .pktType = "TCP", - .netTestRole = NULL, -#ifndef TD_WINDOWS - .password = NULL, -#endif -}; - -void shellDumpConfig() { - if (!args.dump_config) return; - - SConfig *pCfg = taosGetCfg(); - if (NULL == pCfg) { - printf("TDengine read global config failed!\n"); - exit(EXIT_FAILURE); - } - cfgDumpCfg(pCfg, 0, 1); - shellExit(); -} - -void shellTestNetWork() { - if (args.netTestRole && args.netTestRole[0] != 0) { - taosNetTest(args.netTestRole, args.host, args.port, args.pktLen, args.pktNum, args.pktType); - shellExit(); - } -} - -void shellCheckServerStatus() { - if (!args.status && !args.verbose) return; - - TSDB_SERVER_STATUS code; - do { - char details[1024] = {0}; - code = taos_check_server_status(args.host, args.port, details, args.verbose ? 1024 : 0); - switch (code) { - case TSDB_SRV_STATUS_UNAVAILABLE: - printf("0: unavailable\n"); - break; - case TSDB_SRV_STATUS_NETWORK_OK: - printf("1: network ok\n"); - break; - case TSDB_SRV_STATUS_SERVICE_OK: - printf("2: service ok\n"); - break; - case TSDB_SRV_STATUS_SERVICE_DEGRADED: - printf("3: service degraded\n"); - break; - case TSDB_SRV_STATUS_EXTING: - printf("4: exiting\n"); - break; - } - if (strlen(details) != 0) { - printf("%s\n\n", details); - } - if (code == TSDB_SRV_STATUS_NETWORK_OK && args.verbose) { - taosMsleep(1000); - } else { - break; - } - } while (1); - - shellExit(); -} - -void shellExecute() { - TAOS *con = shellInit(&args); - if (con == NULL) { - shellExit(); - } - - if (tsem_init(&cancelSem, 0, 0) != 0) { - printf("failed to create cancel semphore\n"); - shellExit(); - } - - TdThread spid; - taosThreadCreate(&spid, NULL, cancelHandler, NULL); - - taosSetSignal(SIGTERM, shellQueryInterruptHandler); - taosSetSignal(SIGINT, shellQueryInterruptHandler); - taosSetSignal(SIGHUP, shellQueryInterruptHandler); - taosSetSignal(SIGABRT, shellQueryInterruptHandler); - - shellGetGrantInfo(con); - - while (1) { - taosThreadCreate(&pid, NULL, shellThreadLoop, con); - taosThreadJoin(pid, NULL); - } -} +SShellObj shell = {0}; int main(int argc, char *argv[]) { - if (!checkVersion()) shellExit(); + if (shellCheckIntSize() != 0) { + return 0; + } - shellParseArgs(argc, argv, &args); + if (shellParseArgs(argc, argv) != 0) { + return 0; + } + + if (shell.args.is_version) { + shellPrintVersion(); + return 0; + } + + if (shell.args.is_gen_auth) { + shellGenerateAuth(); + return 0; + } + + if (shell.args.is_help) { + return 0; + } taos_init(); - shellDumpConfig(); - shellCheckServerStatus(); - shellTestNetWork(); - shellExecute(); - return 0; + if (shell.args.is_dump_config) { + shellDumpConfig(); + taos_cleanup(); + return 0; + } + + if (shell.args.is_startup || shell.args.is_check) { + shellCheckServerStatus(); + taos_cleanup(); + return 0; + } + + if (strcmp(shell.args.netrole, "client") == 0 || strcmp(shell.args.netrole, "server") == 0) { + shellTestNetWork(); + taos_cleanup(); + return 0; + } + + return shellExecute(); } diff --git a/tools/shell/src/shellTest.c b/tools/shell/src/shellNettest.c similarity index 97% rename from tools/shell/src/shellTest.c rename to tools/shell/src/shellNettest.c index 04b99ebc75..528f030f1c 100644 --- a/tools/shell/src/shellTest.c +++ b/tools/shell/src/shellNettest.c @@ -13,27 +13,32 @@ * along with this program. If not, see . */ -#define _DEFAULT_SOURCE +#define _GNU_SOURCE +#include "shellInt.h" + +void shellTestNetWork() {} + +#if 0 #define ALLOW_FORBID_FUNC #include "os.h" -#include "taosdef.h" -#include "tmsg.h" -#include "taoserror.h" -#include "tlog.h" -#include "tglobal.h" -#include "trpc.h" #include "rpcHead.h" -#include "tchecksum.h" #include "syncMsg.h" +#include "taosdef.h" +#include "taoserror.h" +#include "tchecksum.h" +#include "tglobal.h" +#include "tlog.h" +#include "tmsg.h" +#include "trpc.h" #include "osSocket.h" -#define MAX_PKG_LEN (64 * 1000) +#define MAX_PKG_LEN (64 * 1000) #define MAX_SPEED_PKG_LEN (1024 * 1024 * 1024) #define MIN_SPEED_PKG_LEN 1024 #define MAX_SPEED_PKG_NUM 10000 #define MIN_SPEED_PKG_NUM 1 -#define BUFFER_SIZE (MAX_PKG_LEN + 1024) +#define BUFFER_SIZE (MAX_PKG_LEN + 1024) extern int tsRpcMaxUdpSize; @@ -321,9 +326,8 @@ static int32_t taosNetCheckUdpPort(STestInfo *info) { iDataNum = recvfrom(clientSocket, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&serverAddr, &sin_size); if (iDataNum < 0 || iDataNum != info->pktLen) { - uError("UDP: received ack:%d bytes(expect:%d) from port:%d since %s", iDataNum, info->pktLen, info->port, strerror(errno)); - taosCloseSocket(&pSocket); - return -1; + uError("UDP: received ack:%d bytes(expect:%d) from port:%d since %s", iDataNum, info->pktLen, info->port, + strerror(errno)); taosCloseSocket(&pSocket); return -1; } taosCloseSocket(&pSocket); @@ -408,7 +412,7 @@ static void taosNetTestServer(char *host, int32_t startPort, int32_t pkgLen) { static void taosNetCheckSpeed(char *host, int32_t port, int32_t pkgLen, int32_t pkgNum, char *pkgType) { #if 0 - + // record config int32_t compressTmp = tsCompressMsgSize; int32_t maxUdpSize = tsRpcMaxUdpSize; @@ -468,11 +472,13 @@ static void taosNetCheckSpeed(char *host, int32_t port, int32_t pkgLen, uint64_t endTime = taosGetTimestampUs(); uint64_t el = endTime - startTime; - printf("progress:%5d/%d\tstatus:%d\tcost:%8.2lf ms\tspeed:%8.2lf MB/s\n", i, pkgNum, code, el/1000.0, pkgLen/(el/1000000.0)/1024.0/1024.0); + printf("progress:%5d/%d\tstatus:%d\tcost:%8.2lf ms\tspeed:%8.2lf MB/s\n", i, pkgNum, code, el/1000.0, + pkgLen/(el/1000000.0)/1024.0/1024.0); } int64_t endT = taosGetTimestampUs(); uint64_t elT = endT - startT; - printf("\ntotal succ:%5d/%d\tcost:%8.2lf ms\tspeed:%8.2lf MB/s\n", totalSucc, pkgNum, elT/1000.0, pkgLen/(elT/1000000.0)/1024.0/1024.0*totalSucc); + printf("\ntotal succ:%5d/%d\tcost:%8.2lf ms\tspeed:%8.2lf MB/s\n", totalSucc, pkgNum, elT/1000.0, + pkgLen/(elT/1000000.0)/1024.0/1024.0*totalSucc); rpcClose(pRpcConn); @@ -512,3 +518,5 @@ void taosNetTest(char *role, char *host, int32_t port, int32_t pkgLen, int32_t p tsLogEmbedded = 0; } + +#endif \ No newline at end of file diff --git a/tools/shell/src/shellUtil.c b/tools/shell/src/shellUtil.c new file mode 100644 index 0000000000..2a3dea07e0 --- /dev/null +++ b/tools/shell/src/shellUtil.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * 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 . + */ + +#define _BSD_SOURCE +#define _GNU_SOURCE +#define _XOPEN_SOURCE +#define _DEFAULT_SOURCE + +#include "shellInt.h" + +bool shellRegexMatch(const char *s, const char *reg, int32_t cflags) { + regex_t regex; + char msgbuf[100] = {0}; + + /* Compile regular expression */ + if (regcomp(®ex, reg, cflags) != 0) { + fprintf(stderr, "Fail to compile regex"); + return false; + } + + /* Execute regular expression */ + int32_t reti = regexec(®ex, s, 0, NULL, 0); + if (!reti) { + regfree(®ex); + return true; + } else if (reti == REG_NOMATCH) { + regfree(®ex); + return false; + } else { + regerror(reti, ®ex, msgbuf, sizeof(msgbuf)); + fprintf(stderr, "Regex match failed: %s\n", msgbuf); + regfree(®ex); + return false; + } + + return false; +} + +int32_t shellCheckIntSize() { + if (sizeof(int8_t) != 1) { + printf("taos int8 size is %d(!= 1)", (int)sizeof(int8_t)); + return -1; + } + if (sizeof(int16_t) != 2) { + printf("taos int16 size is %d(!= 2)", (int)sizeof(int16_t)); + return -1; + } + if (sizeof(int32_t) != 4) { + printf("taos int32 size is %d(!= 4)", (int)sizeof(int32_t)); + return -1; + } + if (sizeof(int64_t) != 8) { + printf("taos int64 size is %d(!= 8)", (int)sizeof(int64_t)); + return -1; + } + return 0; +} + +void shellPrintVersion() { printf("version: %s\n", version); } + +void shellGenerateAuth() {} + +void shellDumpConfig() { + SConfig *pCfg = taosGetCfg(); + if (pCfg == NULL) { + printf("TDengine read global config failed!\n"); + } else { + cfgDumpCfg(pCfg, 1, 1); + } +} + +void shellCheckServerStatus() { + TSDB_SERVER_STATUS code; + + do { + char details[1024] = {0}; + code = taos_check_server_status(shell.args.host, shell.args.port, details, 1024); + switch (code) { + case TSDB_SRV_STATUS_UNAVAILABLE: + printf("0: unavailable\n"); + break; + case TSDB_SRV_STATUS_NETWORK_OK: + printf("1: network ok\n"); + break; + case TSDB_SRV_STATUS_SERVICE_OK: + printf("2: service ok\n"); + break; + case TSDB_SRV_STATUS_SERVICE_DEGRADED: + printf("3: service degraded\n"); + break; + case TSDB_SRV_STATUS_EXTING: + printf("4: exiting\n"); + break; + } + if (strlen(details) != 0) { + printf("%s\n\n", details); + } + if (code == TSDB_SRV_STATUS_NETWORK_OK && shell.args.is_startup) { + taosMsleep(1000); + } else { + break; + } + } while (1); +}