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

#define __USE_XOPEN
S
Shengliang Guan 已提交
17
#include "shellInt.h"
H
hzcheng 已提交
18

19 20 21 22 23
#define LEFT  1
#define RIGHT 2
#define UP    3
#define DOWN  4
#define PSIZE shell.info.promptSize
S
Shengliang Guan 已提交
24

H
hzcheng 已提交
25
typedef struct {
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
  char    *buffer;
  char    *command;
  uint32_t commandSize;
  uint32_t bufferSize;
  uint32_t cursorOffset;
  uint32_t screenOffset;
  uint32_t endOffset;
} SShellCmd;

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);

int32_t shellCountPrefixOnes(uint8_t c) {
  uint8_t mask = 127;
H
hzcheng 已提交
58
  mask = ~mask;
59
  int32_t ret = 0;
H
hzcheng 已提交
60 61 62 63 64 65 66 67
  while ((c & mask) != 0) {
    ret++;
    c <<= 1;
  }

  return ret;
}

68
void shellGetPrevCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) {
H
hzcheng 已提交
69 70
  assert(pos > 0);

wafwerar's avatar
wafwerar 已提交
71
  TdWchar wc;
H
hzcheng 已提交
72 73 74 75 76 77
  *size = 0;
  *width = 0;

  while (--pos >= 0) {
    *size += 1;

78
    if (str[pos] > 0 || shellCountPrefixOnes((uint8_t)str[pos]) > 1) break;
H
hzcheng 已提交
79 80
  }

wmmhello's avatar
wmmhello 已提交
81 82
  taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
  // assert(rc == *size); // it will be core, if str is encode by utf8 and taos charset is gbk
H
hzcheng 已提交
83

wafwerar's avatar
wafwerar 已提交
84
  *width = taosWcharWidth(wc);
H
hzcheng 已提交
85 86
}

87
void shellGetNextCharSize(const char *str, int32_t pos, int32_t *size, int32_t *width) {
H
hzcheng 已提交
88 89
  assert(pos >= 0);

wafwerar's avatar
wafwerar 已提交
90 91 92
  TdWchar wc;
  *size = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
  *width = taosWcharWidth(wc);
H
hzcheng 已提交
93 94
}

95
void shellInsertChar(SShellCmd *cmd, char *c, int32_t size) {
H
hzcheng 已提交
96 97
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

wafwerar's avatar
wafwerar 已提交
98 99
  TdWchar wc;
  if (taosMbToWchar(&wc, c, size) < 0) return;
H
hzcheng 已提交
100

101
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
H
hzcheng 已提交
102 103 104 105 106 107 108
  /* 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;
wafwerar's avatar
wafwerar 已提交
109 110
  cmd->screenOffset += taosWcharWidth(wc);
  cmd->endOffset += taosWcharWidth(wc);
111 112
#ifdef WINDOWS
#else
113
  shellShowOnScreen(cmd);
114
#endif
H
hzcheng 已提交
115 116
}

117
void shellBackspaceChar(SShellCmd *cmd) {
H
hzcheng 已提交
118 119 120
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset > 0) {
121 122 123 124
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
    int32_t size = 0;
    int32_t width = 0;
    shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
H
hzcheng 已提交
125 126 127 128 129 130
    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;
131
    shellShowOnScreen(cmd);
H
hzcheng 已提交
132 133 134
  }
}

135
void shellClearLineBefore(SShellCmd *cmd) {
136 137
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

138
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
S
Shengliang Guan 已提交
139
  memmove(cmd->command, cmd->command + cmd->cursorOffset, cmd->commandSize - cmd->cursorOffset);
140 141 142 143
  cmd->commandSize -= cmd->cursorOffset;
  cmd->cursorOffset = 0;
  cmd->screenOffset = 0;
  cmd->endOffset = cmd->commandSize;
144
  shellShowOnScreen(cmd);
145 146
}

147
void shellClearLineAfter(SShellCmd *cmd) {
148 149
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

150
  shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
151 152
  cmd->commandSize -= cmd->endOffset - cmd->cursorOffset;
  cmd->endOffset = cmd->cursorOffset;
153
  shellShowOnScreen(cmd);
154 155
}

156
void shellDeleteChar(SShellCmd *cmd) {
H
hzcheng 已提交
157 158 159
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset < cmd->commandSize) {
160 161 162 163
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
    int32_t size = 0;
    int32_t width = 0;
    shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width);
H
hzcheng 已提交
164 165 166 167
    memmove(cmd->command + cmd->cursorOffset, cmd->command + cmd->cursorOffset + size,
            cmd->commandSize - cmd->cursorOffset - size);
    cmd->commandSize -= size;
    cmd->endOffset -= width;
168
    shellShowOnScreen(cmd);
H
hzcheng 已提交
169 170 171
  }
}

172
void shellMoveCursorLeft(SShellCmd *cmd) {
H
hzcheng 已提交
173 174 175
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset > 0) {
176 177 178 179
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
    int32_t size = 0;
    int32_t width = 0;
    shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
H
hzcheng 已提交
180 181
    cmd->cursorOffset -= size;
    cmd->screenOffset -= width;
182
    shellShowOnScreen(cmd);
H
hzcheng 已提交
183 184 185
  }
}

186
void shellMoveCursorRight(SShellCmd *cmd) {
H
hzcheng 已提交
187 188 189
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset < cmd->commandSize) {
190 191 192 193
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
    int32_t size = 0;
    int32_t width = 0;
    shellGetNextCharSize(cmd->command, cmd->cursorOffset, &size, &width);
H
hzcheng 已提交
194 195
    cmd->cursorOffset += size;
    cmd->screenOffset += width;
196
    shellShowOnScreen(cmd);
H
hzcheng 已提交
197 198 199
  }
}

200
void shellPositionCursorHome(SShellCmd *cmd) {
H
hzcheng 已提交
201 202 203
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset > 0) {
204
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
H
hzcheng 已提交
205 206
    cmd->cursorOffset = 0;
    cmd->screenOffset = 0;
207
    shellShowOnScreen(cmd);
H
hzcheng 已提交
208 209 210
  }
}

211
void shellPositionCursorEnd(SShellCmd *cmd) {
H
hzcheng 已提交
212 213 214
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

  if (cmd->cursorOffset < cmd->commandSize) {
215
    shellClearScreen(cmd->endOffset + PSIZE, cmd->screenOffset + PSIZE);
H
hzcheng 已提交
216 217
    cmd->cursorOffset = cmd->commandSize;
    cmd->screenOffset = cmd->endOffset;
218
    shellShowOnScreen(cmd);
H
hzcheng 已提交
219 220 221
  }
}

222 223
void shellPrintChar(char c, int32_t times) {
  for (int32_t i = 0; i < times; i++) {
H
hzcheng 已提交
224 225 226 227 228
    fprintf(stdout, "%c", c);
  }
  fflush(stdout);
}

229
void shellPositionCursor(int32_t step, int32_t direction) {
H
hzcheng 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243
  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);
  }
}

244
void shellUpdateBuffer(SShellCmd *cmd) {
H
hzcheng 已提交
245 246
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

247
  if (shellRegexMatch(cmd->buffer, "(\\s+$)|(^$)", REG_EXTENDED)) strcat(cmd->command, " ");
H
hzcheng 已提交
248 249 250
  strcat(cmd->buffer, cmd->command);
  cmd->bufferSize += cmd->commandSize;

251
  memset(cmd->command, 0, SHELL_MAX_COMMAND_SIZE);
H
hzcheng 已提交
252 253 254 255
  cmd->cursorOffset = 0;
  cmd->screenOffset = 0;
  cmd->commandSize = 0;
  cmd->endOffset = 0;
256
  shellShowOnScreen(cmd);
H
hzcheng 已提交
257 258
}

259
int32_t shellIsReadyGo(SShellCmd *cmd) {
H
hzcheng 已提交
260 261
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

262 263
  char *total = (char *)taosMemoryCalloc(1, SHELL_MAX_COMMAND_SIZE);
  memset(cmd->command + cmd->commandSize, 0, SHELL_MAX_COMMAND_SIZE - cmd->commandSize);
H
hzcheng 已提交
264 265 266
  sprintf(total, "%s%s", cmd->buffer, cmd->command);

  char *reg_str =
S
Shengliang Guan 已提交
267 268
      "(^.*;\\s*$)|(^\\s*$)|(^\\s*exit\\s*$)|(^\\s*q\\s*$)|(^\\s*quit\\s*$)|(^"
      "\\s*clear\\s*$)";
269
  if (shellRegexMatch(total, reg_str, REG_EXTENDED | REG_ICASE)) {
wafwerar's avatar
wafwerar 已提交
270
    taosMemoryFree(total);
H
hzcheng 已提交
271 272 273
    return 1;
  }

wafwerar's avatar
wafwerar 已提交
274
  taosMemoryFree(total);
H
hzcheng 已提交
275 276 277
  return 0;
}

278 279
void shellGetMbSizeInfo(const char *str, int32_t *size, int32_t *width) {
  TdWchar *wc = (TdWchar *)taosMemoryCalloc(sizeof(TdWchar), SHELL_MAX_COMMAND_SIZE);
H
hzcheng 已提交
280
  *size = strlen(str);
281 282
  taosMbsToWchars(wc, str, SHELL_MAX_COMMAND_SIZE);
  *width = taosWcharsWidth(wc, SHELL_MAX_COMMAND_SIZE);
wafwerar's avatar
wafwerar 已提交
283
  taosMemoryFree(wc);
H
hzcheng 已提交
284 285
}

286
void shellResetCommand(SShellCmd *cmd, const char s[]) {
H
hzcheng 已提交
287 288
  assert(cmd->cursorOffset <= cmd->commandSize && cmd->endOffset >= cmd->screenOffset);

289 290 291 292 293 294 295
  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);
H
hzcheng 已提交
296 297 298 299 300
  cmd->bufferSize = 0;
  cmd->commandSize = size;
  cmd->cursorOffset = size;
  cmd->screenOffset = width;
  cmd->endOffset = width;
301
  shellShowOnScreen(cmd);
H
hzcheng 已提交
302
}
303

304 305 306 307 308 309 310 311

void shellGetScreenSize(int32_t *ws_col, int32_t *ws_row) {
#ifdef WINDOWS
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
  if (ws_col != NULL) *ws_col = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  if (ws_row != NULL) *ws_row = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
#else
312 313 314
  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");
315 316 317 318 319
    if (ws_col != NULL) *ws_col = 120;
    if (ws_row != NULL) *ws_row = 30;
  } else {
    if (ws_col != NULL) *ws_col = w.ws_col;
    if (ws_row != NULL) *ws_row = w.ws_row;
320
  }
321 322 323 324 325 326
#endif
}

void shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos) {
  int32_t ws_col;
  shellGetScreenSize(&ws_col, NULL);
327

328 329 330
  int32_t cursor_x = cursor_pos / ws_col;
  int32_t cursor_y = cursor_pos % ws_col;
  int32_t command_x = ecmd_pos / ws_col;
331 332 333 334 335 336 337 338
  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);
H
hzcheng 已提交
339
}
340 341

void shellShowOnScreen(SShellCmd *cmd) {
342 343
  int32_t ws_col;
  shellGetScreenSize(&ws_col, NULL);
344 345 346 347 348 349 350 351 352 353 354 355

  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);
  }
356
  int32_t remain_column = ws_col;
357 358 359 360 361 362 363 364 365 366 367 368
  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);
369
        remain_column = ws_col;
370 371
      } else {
        printf("\n\r%lc", wc);
372
        remain_column = ws_col - width;
373 374 375 376 377 378 379 380 381 382 383
      }
    }

    str = total_string + size;
  }

  taosMemoryFree(total_string);
  // Position the cursor
  int32_t cursor_pos = cmd->screenOffset + PSIZE;
  int32_t ecmd_pos = cmd->endOffset + PSIZE;

384 385 386 387 388 389
  int32_t cursor_x = cursor_pos / ws_col;
  int32_t cursor_y = cursor_pos % ws_col;
  // int32_t cursor_y = cursor % ws_col;
  int32_t command_x = ecmd_pos / ws_col;
  int32_t command_y = ecmd_pos % ws_col;
  // int32_t command_y = (command.size() + PSIZE) % ws_col;
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
  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, "");
433 434 435 436 437
          #ifdef WINDOWS
            raise(SIGINT);
          #else
            kill(0, SIGINT);
          #endif
438 439 440 441 442 443 444 445 446 447 448 449
          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':
450 451
        #ifdef WINDOWS 
        #else
452
          printf("\n");
453
        #endif
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
          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;
}