• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #4991

17 Mar 2026 07:57AM UTC coverage: 69.756% (+0.4%) from 69.348%
#4991

push

travis-ci

web-flow
merge: from main to 3.0 branch #34807

14 of 16 new or added lines in 5 files covered. (87.5%)

3928 existing lines in 138 files now uncovered.

192146 of 275455 relevant lines covered (69.76%)

137208686.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

30.69
/tools/shell/src/shellCommand.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define __USE_XOPEN
17
#include "shellInt.h"
18
#include "shellAuto.h"
19

20
#define LEFT  1
21
#define RIGHT 2
22
#define UP    3
23
#define DOWN  4
24
#define PSIZE shell.info.promptSize
25
#define SHELL_INPUT_MAX_COMMAND_SIZE 10000
26

27

28
static void    shellGetNextCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width);
29

30
static void    shellBackspaceChar(SShellCmd *cmd);
31
static void    shellClearLineBefore(SShellCmd *cmd);
32
static void    shellClearLineAfter(SShellCmd *cmd);
33
static void    shellDeleteChar(SShellCmd *cmd);
34
static void    shellMoveCursorLeft(SShellCmd *cmd);
35
static void    shellMoveCursorRight(SShellCmd *cmd);
36
static void    shellPositionCursorHome(SShellCmd *cmd);
37
static void    shellPositionCursorEnd(SShellCmd *cmd);
38
static void    shellPrintChar(char c, int32_t times);
39
static void    shellPositionCursor(int32_t step, int32_t direction);
40
static void    shellUpdateBuffer(SShellCmd *cmd);
41
static bool    shellIsReadyGo(SShellCmd *cmd);
42
static void    shellGetMbSizeInfo(const char *str, int32_t *size, int32_t *width);
43
static void    shellResetCommand(SShellCmd *cmd, const char s[]);
44
void           shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos);
45
void           shellShowOnScreen(SShellCmd *cmd);
46
void           shellGetPrevCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width);
47
void           shellInsertChar(SShellCmd *cmd, char *c, int size);
48
void           shellInsertString(SShellCmd *cmd, char *str, int size);
49

50
int32_t shellCountPrefixOnes(uint8_t c) {
1,056✔
51
  uint8_t mask = 0x80;
1,056✔
52
  int32_t count = 0;
1,056✔
53
  while ((c & mask) != 0) {
4,032✔
54
    count++;
2,976✔
55
    mask >>= 1;
2,976✔
56
  }
57

58
  return (count == 0) ? 1 : count;
1,056✔
59
}
60

61
void shellGetPrevCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) {
×
62
  if (pos <= 0) return;
×
63

64
  TdWchar wc;
×
65
  *size = 0;
×
66
  *width = 0;
×
67

68
  while (--pos >= 0) {
×
69
    *size += 1;
×
70
    if ((str[pos] & 0x80) == 0 || shellCountPrefixOnes((uint8_t)str[pos]) > 1) {
×
71
      break;
72
    }
73
  }
74

75
  taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
×
76
  // ASSERT(rc == *size); // it will be core, if str is encode by utf8 and taos charset is gbk
77

78
  *width = taosWcharWidth(wc);
×
79
}
80

81
void shellGetNextCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) {
×
82
  if(pos < 0) return;
×
83

84
  TdWchar wc;
×
85
  *size = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
×
86
  *width = taosWcharWidth(wc);
×
87
}
88

89
void shellInsertChar(SShellCmd *cmd, char *c, int32_t size) {
16,906✔
90
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
16,906✔
91

92
  TdWchar wc;
16,906✔
93
  if (taosMbToWchar(&wc, c, size) < 0) return;
16,906✔
94

95
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
16,906✔
96
  /* update the buffer */
97
  memmove(cmd->command + cmd->cursorOffset + size, cmd->command + cmd->cursorOffset,
16,906✔
98
          cmd->commandSize - cmd->cursorOffset);
16,906✔
99
  memcpy(cmd->command + cmd->cursorOffset, c, size);
16,906✔
100
  /* update the values */
101
  cmd->commandSize += size;
16,906✔
102
  cmd->cursorOffset += size;
16,906✔
103
  cmd->screenOffset += taosWcharWidth(wc);
16,906✔
104
  cmd->endOffset += taosWcharWidth(wc);
16,906✔
105

106
  // set string end
107
  cmd->command[cmd->commandSize] = 0;
16,906✔
108
#ifdef WINDOWS
109
#else
110
  shellShowOnScreen(cmd);
16,906✔
111
#endif
112
}
113

114
// insert string . count is str char count
115
void shellInsertStr(SShellCmd *cmd, char *str, int32_t size) {
×
116
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
117
  /* update the buffer */
118
  memmove(cmd->command + cmd->cursorOffset + size, cmd->command + cmd->cursorOffset,
×
119
          cmd->commandSize - cmd->cursorOffset);
×
120
  memcpy(cmd->command + cmd->cursorOffset, str, size);
×
121
  /* update the values */
122
  cmd->commandSize += size;
×
123
  cmd->cursorOffset += size;
×
124
  cmd->screenOffset += size;
×
125
  cmd->endOffset += size;
×
126

127
  // set string end
128
  cmd->command[cmd->commandSize] = 0;
×
129
#ifdef WINDOWS
130
#else
131
  shellShowOnScreen(cmd);
×
132
#endif
133
}
×
134

135
void shellBackspaceChar(SShellCmd *cmd) {
×
136
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
137

138
  if (cmd->cursorOffset > 0) {
×
139
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
140
    int32_t size = 0;
×
141
    int32_t width = 0;
×
142
    shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
×
143
    memmove(cmd->command + cmd->cursorOffset - size, cmd->command + cmd->cursorOffset,
×
144
            cmd->commandSize - cmd->cursorOffset);
×
145
    cmd->commandSize -= size;
×
146
    cmd->cursorOffset -= size;
×
147
    cmd->screenOffset -= width;
×
148
    cmd->endOffset -= width;
×
149
    // set string end
150
    cmd->command[cmd->commandSize] = 0;
×
151
    shellShowOnScreen(cmd);
×
152
  }
153
}
154

155
void shellClearLineBefore(SShellCmd *cmd) {
×
156
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
157

158
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
159
  memmove(cmd->command, cmd->command + cmd->cursorOffset, cmd->commandSize - cmd->cursorOffset);
×
160
  cmd->commandSize -= cmd->cursorOffset;
×
161
  cmd->cursorOffset = 0;
×
162
  cmd->screenOffset = 0;
×
163
  cmd->endOffset = cmd->commandSize;
×
164
  // set string end
165
  cmd->command[cmd->commandSize] = 0;
×
166
  shellShowOnScreen(cmd);
×
167
}
168

169
void shellClearLineAfter(SShellCmd *cmd) {
×
170
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
171

172
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
173
  cmd->commandSize -= cmd->endOffset - cmd->cursorOffset;
×
174
  cmd->endOffset = cmd->cursorOffset;
×
175
  shellShowOnScreen(cmd);
×
176
}
177

178
void shellDeleteChar(SShellCmd *cmd) {
×
179
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
180

181
  if (cmd->cursorOffset < cmd->commandSize) {
×
182
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
183
    int32_t size = 0;
×
184
    int32_t width = 0;
×
185
    shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width);
×
186
    memmove(cmd->command + cmd->cursorOffset, cmd->command + cmd->cursorOffset + size,
×
187
            cmd->commandSize - cmd->cursorOffset - size);
×
188
    cmd->commandSize -= size;
×
189
    cmd->endOffset -= width;
×
190
    // set string end
191
    cmd->command[cmd->commandSize] = 0;
×
192
    shellShowOnScreen(cmd);
×
193
  }
194
}
195

196
void shellMoveCursorLeft(SShellCmd *cmd) {
×
197
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
198

199
  if (cmd->cursorOffset > 0) {
×
200
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
201
    int32_t size = 0;
×
202
    int32_t width = 0;
×
203
    shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
×
204
    cmd->cursorOffset -= size;
×
205
    cmd->screenOffset -= width;
×
206
    shellShowOnScreen(cmd);
×
207
  }
208
}
209

210
void shellMoveCursorRight(SShellCmd *cmd) {
×
211
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
212

213
  if (cmd->cursorOffset < cmd->commandSize) {
×
214
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
215
    int32_t size = 0;
×
216
    int32_t width = 0;
×
217
    shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width);
×
218
    cmd->cursorOffset += size;
×
219
    cmd->screenOffset += width;
×
220
    shellShowOnScreen(cmd);
×
221
  }
222
}
223

224
void shellPositionCursorHome(SShellCmd *cmd) {
×
225
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
226

227
  if (cmd->cursorOffset > 0) {
×
228
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
229
    cmd->cursorOffset = 0;
×
230
    cmd->screenOffset = 0;
×
231
    shellShowOnScreen(cmd);
×
232
  }
233
}
234

235
void positionCursorMiddle(SShellCmd *cmd) {
×
236
  if (cmd->endOffset > 0) {
×
237
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
238
    cmd->cursorOffset = cmd->commandSize/2;
×
239
    cmd->screenOffset = cmd->endOffset/2;
×
240
    shellShowOnScreen(cmd);
×
241
  }
242
}
×
243

244
void shellPositionCursorEnd(SShellCmd *cmd) {
×
245
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
246

247
  if (cmd->cursorOffset < cmd->commandSize) {
×
248
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
249
    cmd->cursorOffset = cmd->commandSize;
×
250
    cmd->screenOffset = cmd->endOffset;
×
251
    shellShowOnScreen(cmd);
×
252
  }
253
}
254

255
void shellPrintChar(char c, int32_t times) {
×
256
  for (int32_t i = 0; i < times; i++) {
×
257
    fprintf(stdout, "%c", c);
×
258
  }
259
  fflush(stdout);
×
260
}
×
261

262
void shellPositionCursor(int32_t step, int32_t direction) {
104,428✔
263
#ifndef WINDOWS
264
  if (step > 0) {
104,428✔
265
    if (direction == LEFT) {
52,214✔
266
      fprintf(stdout, "\033[%dD", step);
34,560✔
267
    } else if (direction == RIGHT) {
17,654✔
268
      fprintf(stdout, "\033[%dC", step);
17,654✔
269
    } else if (direction == UP) {
×
270
      fprintf(stdout, "\033[%dA", step);
×
271
    } else if (direction == DOWN) {
×
272
      fprintf(stdout, "\033[%dB", step);
×
273
    }
274
    fflush(stdout);
52,214✔
275
  }
276
#endif
277
}
104,428✔
278

279
void shellUpdateBuffer(SShellCmd *cmd) {
×
280
  /* Validate command state to avoid inconsistent offsets */
UNCOV
281
  if (cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
282

283
  /* Capacity of the buffers */
284
  size_t cap = (size_t)SHELL_MAX_COMMAND_SIZE;
×
UNCOV
285
  size_t buf_len = strlen(cmd->buffer);
×
286
  size_t cmd_len = (size_t)cmd->commandSize;
×
287

288
  /* If regex condition is met, try to append one space to cmd->command safely.
289
     This mirrors the original intent but avoids buffer overflow. */
290
  if (shellRegexMatch(cmd->buffer, "(\\s+$)|(^$)", REG_EXTENDED)) {
×
291
    if (cmd_len + 1 < cap) {
×
UNCOV
292
      cmd->command[cmd_len] = ' ';
×
UNCOV
293
      cmd->command[cmd_len + 1] = '\0';
×
UNCOV
294
      cmd_len += 1;
×
UNCOV
295
      cmd->commandSize = (int32_t)cmd_len;
×
296
    }
297
    /* If there is no room to append the space, skip appending it to keep memory safe. */
298
  }
299

300
  /* Append as much of cmd->command into cmd->buffer as fits safely. */
UNCOV
301
  if (buf_len < cap - 1 && cmd_len > 0) {
×
UNCOV
302
    size_t available = cap - 1 - buf_len; /* leave room for null terminator */
×
UNCOV
303
    size_t to_copy = cmd_len <= available ? cmd_len : available;
×
UNCOV
304
    memcpy(cmd->buffer + buf_len, cmd->command, to_copy);
×
UNCOV
305
    cmd->buffer[buf_len + to_copy] = '\0';
×
UNCOV
306
    cmd->bufferSize = (int32_t)(buf_len + to_copy);
×
307
  }
308

309
  /* Clear the command buffer and reset cursor/screen offsets */
310
  memset(cmd->command, 0, cap);
×
UNCOV
311
  cmd->cursorOffset = 0;
×
UNCOV
312
  cmd->screenOffset = 0;
×
313
  cmd->commandSize = 0;
×
314
  cmd->endOffset = 0;
×
315

316
  /* Refresh the on-screen representation */
317
  shellShowOnScreen(cmd);
×
318
}
319

320
bool shellIsReadyGo(SShellCmd *cmd) {
519✔
321
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return false;
519✔
322

323
  char *total = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE);
519✔
324
  memset(cmd->command + cmd->commandSize, 0, SHELL_MAX_COMMAND_SIZE - cmd->commandSize);
519✔
325
  sprintf(total, "%s%s", cmd->buffer, cmd->command);
519✔
326

327
  char *reg_str =
519✔
328
      "(^.*;\\s*$)|(^\\s*$)|(^\\s*exit\\s*$)|(^\\s*q\\s*$)|(^\\s*quit\\s*$)|(^"
329
      "\\s*clear\\s*$)";
330
  if (shellRegexMatch(total, reg_str, REG_EXTENDED | REG_ICASE)) {
519✔
331
    taosMemoryFree(total);
519✔
332
    return true;
519✔
333
  }
334

335
  taosMemoryFree(total);
×
336
  return false;
×
337
}
338

UNCOV
339
void shellGetMbSizeInfo(const char *str, int32_t *size, int32_t *width) {
×
UNCOV
340
  TdWchar *wc = (TdWchar *)taosMemoryCalloc(sizeof(TdWchar), SHELL_MAX_COMMAND_SIZE);
×
UNCOV
341
  *size = strlen(str);
×
UNCOV
342
  taosMbsToWchars(wc, str, SHELL_MAX_COMMAND_SIZE);
×
UNCOV
343
  *width = taosWcharsWidth(wc, SHELL_MAX_COMMAND_SIZE);
×
UNCOV
344
  taosMemoryFree(wc);
×
UNCOV
345
}
×
346

UNCOV
347
void shellResetCommand(SShellCmd *cmd, const char s[]) {
×
UNCOV
348
  if(cmd->cursorOffset > cmd->commandSize || cmd->endOffset < cmd->screenOffset) return;
×
349

UNCOV
350
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
×
UNCOV
351
  memset(cmd->buffer, 0, SHELL_MAX_COMMAND_SIZE);
×
UNCOV
352
  memset(cmd->command, 0, SHELL_MAX_COMMAND_SIZE);
×
UNCOV
353
  strncpy(cmd->command, s, SHELL_MAX_COMMAND_SIZE);
×
UNCOV
354
  int32_t size = 0;
×
UNCOV
355
  int32_t width = 0;
×
UNCOV
356
  shellGetMbSizeInfo(s, &size, &width);
×
UNCOV
357
  cmd->bufferSize = 0;
×
UNCOV
358
  cmd->commandSize = size;
×
UNCOV
359
  cmd->cursorOffset = size;
×
UNCOV
360
  cmd->screenOffset = width;
×
UNCOV
361
  cmd->endOffset = width;
×
UNCOV
362
  shellShowOnScreen(cmd);
×
363
}
364

365

366
void shellGetScreenSize(int32_t *ws_col, int32_t *ws_row) {
34,560✔
367
#ifdef WINDOWS
368
  CONSOLE_SCREEN_BUFFER_INFO csbi;
369
  GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
370
  if (ws_col != NULL) *ws_col = csbi.srWindow.Right - csbi.srWindow.Left + 1;
371
  if (ws_row != NULL) *ws_row = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
372
#elif defined(TD_ASTRA)
373
  if (ws_col != NULL) *ws_col = 120;
374
  if (ws_row != NULL) *ws_row = 30;
375
#else
376
  struct winsize w;
34,560✔
377
  if (ioctl(0, TIOCGWINSZ, &w) < 0 || w.ws_col == 0 || w.ws_row == 0) {
34,560✔
378
    // fprintf(stderr, "No stream device, and use default value(col 120, row 30)\r\n");
379
    if (ws_col != NULL) *ws_col = 120;
24,758✔
380
    if (ws_row != NULL) *ws_row = 30;
24,758✔
381
  } else {
382
    if (ws_col != NULL) *ws_col = w.ws_col;
9,802✔
383
    if (ws_row != NULL) *ws_row = w.ws_row;
9,802✔
384
  }
385
#endif
386
}
34,560✔
387

388
void shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos) {
16,906✔
389
  int32_t ws_col;
16,906✔
390
  shellGetScreenSize(&ws_col, NULL);
16,906✔
391

392
  int32_t cursor_x = cursor_pos / ws_col;
16,906✔
393
  int32_t cursor_y = cursor_pos % ws_col;
16,906✔
394
  int32_t command_x = ecmd_pos / ws_col;
16,906✔
395
  shellPositionCursor(cursor_y, LEFT);
16,906✔
396
  shellPositionCursor(command_x - cursor_x, DOWN);
16,906✔
397
#ifndef WINDOWS
398
  fprintf(stdout, "\033[2K");
16,906✔
399
#endif
400
  for (int32_t i = 0; i < command_x; i++) {
16,906✔
UNCOV
401
    shellPositionCursor(1, UP);
×
402
  #ifndef WINDOWS
UNCOV
403
    fprintf(stdout, "\033[2K");
×
404
  #endif
405
  }
406
  fflush(stdout);
16,906✔
407
}
16,906✔
408

409
void shellShowOnScreen(SShellCmd *cmd) {
17,654✔
410
  int32_t ws_col;
17,654✔
411
  shellGetScreenSize(&ws_col, NULL);
17,654✔
412

413
  TdWchar wc;
17,654✔
414
  int32_t size = 0;
17,654✔
415

416
  // Print out the command.
417
  char *total_string = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE);
17,654✔
418
  memset(total_string, '\0', SHELL_MAX_COMMAND_SIZE);
17,654✔
419
  if (strcmp(cmd->buffer, "") == 0) {
17,654✔
420
    sprintf(total_string, "%s%s", shell.info.promptHeader, cmd->command);
17,654✔
421
  } else {
UNCOV
422
    sprintf(total_string, "%s%s", shell.info.promptContinue, cmd->command);
×
423
  }
424
  int32_t remain_column = ws_col;
17,654✔
425
  for (char *str = total_string; size < cmd->commandSize + PSIZE;) {
479,442✔
426
    int32_t ret = taosMbToWchar(&wc, str, MB_CUR_MAX);
461,788✔
427
    if (ret < 0) break;
461,788✔
428
    size += ret;
461,788✔
429
    /* ASSERT(size >= 0); */
430
    int32_t width = taosWcharWidth(wc);
461,788✔
431
    if (remain_column > width) {
461,788✔
432
      printf("%lc", wc);
461,788✔
433
      remain_column -= width;
461,788✔
434
    } else {
UNCOV
435
      if (remain_column == width) {
×
UNCOV
436
        printf("%lc\n\r", wc);
×
UNCOV
437
        remain_column = ws_col;
×
438
      } else {
UNCOV
439
        printf("\n\r%lc", wc);
×
UNCOV
440
        remain_column = ws_col - width;
×
441
      }
442
    }
443

444
    str = total_string + size;
461,788✔
445
  }
446

447
  taosMemoryFree(total_string);
17,654✔
448
  // Position the cursor
449
  int32_t cursor_pos = cmd->screenOffset + PSIZE;
17,654✔
450
  int32_t ecmd_pos = cmd->endOffset + PSIZE;
17,654✔
451

452
  int32_t cursor_x = cursor_pos / ws_col;
17,654✔
453
  int32_t cursor_y = cursor_pos % ws_col;
17,654✔
454
  // int32_t cursor_y = cursor % ws_col;
455
  int32_t command_x = ecmd_pos / ws_col;
17,654✔
456
  int32_t command_y = ecmd_pos % ws_col;
17,654✔
457
  // int32_t command_y = (command.size() + PSIZE) % ws_col;
458
  shellPositionCursor(command_y, LEFT);
17,654✔
459
  shellPositionCursor(command_x, UP);
17,654✔
460
  shellPositionCursor(cursor_x, DOWN);
17,654✔
461
  shellPositionCursor(cursor_y, RIGHT);
17,654✔
462
  fflush(stdout);
17,654✔
463
}
17,654✔
464

465
char taosGetConsoleChar() {
17,654✔
466
#ifdef WINDOWS
467
  static void *console = NULL;
468
  if (console == NULL) {
469
    console = GetStdHandle(STD_INPUT_HANDLE);
470
  }
471
  static TdWchar buf[SHELL_INPUT_MAX_COMMAND_SIZE];
472
  static char mbStr[5];
473
  static unsigned long bufLen = 0;
474
  static uint16_t bufIndex = 0, mbStrIndex = 0, mbStrLen = 0;
475
  CONSOLE_READCONSOLE_CONTROL inputControl={ sizeof(CONSOLE_READCONSOLE_CONTROL), 0, 0, 0 };
476
  while (bufLen == 0) {
477
    ReadConsoleW(console, buf, SHELL_INPUT_MAX_COMMAND_SIZE, &bufLen, &inputControl);
478
    if (bufLen > 0 && buf[0] == 0) bufLen = 0;
479
    bufIndex = 0;
480
  }
481
  if (mbStrLen == 0){
482
    if (buf[bufIndex] == '\r') {
483
      bufIndex++;
484
    }
485
    mbStrLen = WideCharToMultiByte(CP_UTF8, 0, &buf[bufIndex], 1, mbStr, sizeof(mbStr), NULL, NULL);
486
    mbStrIndex = 0;
487
    bufIndex++;
488
  }
489
  mbStrIndex++;
490
  if (mbStrIndex == mbStrLen) {
491
    mbStrLen = 0;
492
    if (bufIndex == bufLen) {
493
      bufLen = 0;
494
    }
495
  }
496
  return mbStr[mbStrIndex-1];
497
#else
498
  return (char)getchar();  // getchar() return an 'int32_t' value
17,654✔
499
#endif
500
}
501

502
int32_t shellReadCommand(char *command) {
748✔
503
  SShellHistory *pHistory = &shell.history;
748✔
504
  SShellCmd      cmd = {0};
748✔
505
  uint32_t       hist_counter = pHistory->hend;
748✔
506
  char           utf8_array[10] = "\0";
748✔
507

508
  cmd.buffer = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE);
748✔
509
  if (cmd.buffer == NULL) {
748✔
UNCOV
510
    return terrno;
×
511
  }
512
  cmd.command = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE);
748✔
513
  if (cmd.command == NULL) {
748✔
514
    taosMemoryFreeClear(cmd.buffer);
×
515
    return terrno;
×
516
  }
517

518
  shellShowOnScreen(&cmd);
748✔
519

520
  // Read input.
521
  char c;
748✔
522
  while (1) {
523
    c = taosGetConsoleChar();
17,654✔
524

525
    if (c == (char)EOF) {
17,654✔
526
      taosMemoryFreeClear(cmd.buffer);
229✔
527
      taosMemoryFreeClear(cmd.command);
229✔
528
      return c;
229✔
529
    }
530

531
    if ((c & 0x80) != 0) { // For UTF-8
17,425✔
532
      int32_t count = shellCountPrefixOnes(c);
×
533
      utf8_array[0] = c;
×
534
      for (int32_t k = 1; k < count && k < sizeof(utf8_array); k++) {
×
535
        c = taosGetConsoleChar();
×
536
        utf8_array[k] = c;
×
537
      }
538
      shellInsertChar(&cmd, utf8_array, count);
×
UNCOV
539
      pressOtherKey(c);
×
540
    } else if (c == TAB_KEY) {
17,425✔
541
      // press TAB key
UNCOV
542
      pressTabKey(&cmd);
×
543
    } else if (c < '\033') {
17,425✔
544
      pressOtherKey(c);
519✔
545
      // Ctrl keys.  TODO: Implement ctrl combinations
546
      switch (c) {
519✔
UNCOV
547
        case 0:
×
UNCOV
548
          break;
×
UNCOV
549
        case 1:  // ctrl A
×
UNCOV
550
          shellPositionCursorHome(&cmd);
×
551
          break;
×
UNCOV
552
        case 3:
×
553
          printf("\r\n");
×
554
          shellResetCommand(&cmd, "");
×
555
          #ifdef WINDOWS
556
            raise(SIGINT);
557
          #else
UNCOV
558
            kill(0, SIGINT);
×
559
          #endif
UNCOV
560
          break;
×
561
        case 4:  // EOF or Ctrl+D
×
UNCOV
562
          taosResetTerminalMode();
×
UNCOV
563
          printf("\r\n");
×
UNCOV
564
          taosMemoryFreeClear(cmd.buffer);
×
UNCOV
565
          taosMemoryFreeClear(cmd.command);
×
566
          return -1;
×
567
        case 5:  // ctrl E
×
568
          shellPositionCursorEnd(&cmd);
×
569
          break;
×
570
        case 8:
×
571
          shellBackspaceChar(&cmd);
×
572
          break;
×
573
        case '\n':
519✔
574
        case '\r':
575
        #ifdef WINDOWS 
576
        #else
577
          printf("\r\n");
519✔
578
        #endif
579
          if (shellIsReadyGo(&cmd)) {
519✔
580
            sprintf(command, "%s%s", cmd.buffer, cmd.command);
519✔
581
            taosMemoryFreeClear(cmd.buffer);
519✔
582
            taosMemoryFreeClear(cmd.command);
519✔
583
            return 0;
519✔
584
          } else {
585
            shellUpdateBuffer(&cmd);
×
586
          }
587
          break;
×
UNCOV
588
        case 11:  // Ctrl + K;
×
589
          shellClearLineAfter(&cmd);
×
590
          break;
×
591
        case 12:  // Ctrl + L;
×
592
#pragma GCC diagnostic push
593
#pragma GCC diagnostic ignored "-Wunused-result"
594
#ifndef TD_ASTRA
595
          system("clear");
×
596
#else
597
          printf("\033[2J\033[H");
598
#endif
599
#pragma GCC diagnostic pop
UNCOV
600
          shellShowOnScreen(&cmd);
×
601
          break;
×
602
        case 21:  // Ctrl + U;
×
603
          shellClearLineBefore(&cmd);
×
604
          break;
×
605
        case 23:  // Ctrl + W;
×
606
          positionCursorMiddle(&cmd);
×
607
          break;          
×
608
      }
609
    } else if (c == '\033') {
16,906✔
UNCOV
610
      pressOtherKey(c);
×
611
      c = taosGetConsoleChar();
×
UNCOV
612
      switch (c) {
×
613
        case '[':
×
614
          c = taosGetConsoleChar();
×
615
          switch (c) {
×
UNCOV
616
            case 'A':  // Up arrow
×
UNCOV
617
              hist_counter = (hist_counter + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE;
×
618
              if (pHistory->hist[hist_counter] == NULL) {
×
619
                hist_counter = (hist_counter + SHELL_MAX_HISTORY_SIZE + 1) % SHELL_MAX_HISTORY_SIZE;
×
620
              } else {
UNCOV
621
                shellResetCommand(&cmd, pHistory->hist[hist_counter]);
×
622
              }
UNCOV
623
              break;
×
624
            case 'B':  // Down arrow
×
625
              if (hist_counter != pHistory->hend) {
×
626
                int32_t next_hist = (hist_counter + 1) % SHELL_MAX_HISTORY_SIZE;
×
627

628
                if (next_hist != pHistory->hend) {
×
UNCOV
629
                  shellResetCommand(&cmd, (pHistory->hist[next_hist] == NULL) ? "" : pHistory->hist[next_hist]);
×
630
                } else {
631
                  shellResetCommand(&cmd, "");
×
632
                }
UNCOV
633
                hist_counter = next_hist;
×
634
              }
635
              break;
×
636
            case 'C':  // Right arrow
×
637
              shellMoveCursorRight(&cmd);
×
UNCOV
638
              break;
×
UNCOV
639
            case 'D':  // Left arrow
×
640
              shellMoveCursorLeft(&cmd);
×
641
              break;
×
UNCOV
642
            case '1':
×
643
              if ((c = taosGetConsoleChar()) == '~') {
×
644
                // Home key
645
                shellPositionCursorHome(&cmd);
×
646
              }
647
              break;
×
648
            case '2':
×
UNCOV
649
              if ((c = taosGetConsoleChar()) == '~') {
×
650
                // Insert key
651
              }
UNCOV
652
              break;
×
653
            case '3':
×
UNCOV
654
              if ((c = taosGetConsoleChar()) == '~') {
×
655
                // Delete key
UNCOV
656
                shellDeleteChar(&cmd);
×
657
              }
UNCOV
658
              break;
×
UNCOV
659
            case '4':
×
UNCOV
660
              if ((c = taosGetConsoleChar()) == '~') {
×
661
                // End key
UNCOV
662
                shellPositionCursorEnd(&cmd);
×
663
              }
UNCOV
664
              break;
×
UNCOV
665
            case '5':
×
UNCOV
666
              if ((c = taosGetConsoleChar()) == '~') {
×
667
                // Page up key
668
              }
UNCOV
669
              break;
×
UNCOV
670
            case '6':
×
UNCOV
671
              if ((c = taosGetConsoleChar()) == '~') {
×
672
                // Page down key
673
              }
UNCOV
674
              break;
×
UNCOV
675
            case 72:
×
676
              // Home key
UNCOV
677
              shellPositionCursorHome(&cmd);
×
UNCOV
678
              break;
×
UNCOV
679
            case 70:
×
680
              // End key
UNCOV
681
              shellPositionCursorEnd(&cmd);
×
UNCOV
682
              break;
×
683
          }
UNCOV
684
          break;
×
685
      }
686
    } else if (c == 0x7f) {
16,906✔
UNCOV
687
      pressOtherKey(c);
×
688
      // press delete key
UNCOV
689
      shellBackspaceChar(&cmd);
×
690
    } else {
691
      pressOtherKey(c);
16,906✔
692
      shellInsertChar(&cmd, &c, 1);
16,906✔
693
    }
694
  }
695

696
  return 0;
697
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc