ArduinoLibs
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Shell.cpp
1 /*
2  * Copyright (C) 2016 Southern Storm Software, Pty Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "Shell.h"
24 #include "LoginShell.h"
25 #include <string.h>
26 #include <stddef.h>
27 
105 // Modes for line editing (flags).
106 #define LINEMODE_NORMAL 0x01
107 #define LINEMODE_ECHO 0x02
108 #define LINEMODE_USERNAME 0x04
109 #define LINEMODE_PASSWORD 0x08
110 #define LINEMODE_PROMPT 0x10
111 #define LINEMODE_DELAY 0x20
112 
113 // Delay to insert after a failed login to slow down brute force attacks (ms).
114 #define LOGIN_SHELL_DELAY 3000
115 
123  : curStart(0)
124  , curLen(0)
125  , curMax(sizeof(buffer))
126  , history(0)
127  , historyWrite(0)
128  , historyRead(0)
129  , historySize(0)
130  , prom("$ ")
131  , isClient(false)
132  , lineMode(LINEMODE_NORMAL | LINEMODE_ECHO)
133  , uid(-1)
134  , timer(0)
135 {
136 }
137 
142 {
143  clearHistory();
144  delete [] history;
145 }
146 
176 bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode)
177 {
178  if (!beginShell(stream, maxHistory, mode))
179  return false;
180  isClient = false;
181  return true;
182 }
183 
208 bool Shell::begin(Client &client, size_t maxHistory, Terminal::Mode mode)
209 {
210  if (!beginShell(client, maxHistory, mode))
211  return false;
212  isClient = true;
213  return true;
214 }
215 
219 bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
220 {
221  // Initialize the Terminal base class with the underlying stream.
222  Terminal::begin(stream, mode);
223 
224  // Create the history buffer.
225  bool ok = true;
226  delete [] history;
227  historySize = sizeof(buffer) * maxHistory;
228  if (maxHistory) {
229  history = new char [historySize];
230  if (history) {
231  memset(history, 0, historySize);
232  } else {
233  maxHistory = 0;
234  historySize = 0;
235  ok = false;
236  }
237  } else {
238  history = 0;
239  }
240 
241  // Clear other variables.
242  curStart = 0;
243  curLen = 0;
244  curMax = sizeof(buffer);
245  historyWrite = 0;
246  historyRead = 0;
247  uid = -1;
248 
249  // Begins the login session.
250  beginSession();
251  return ok;
252 }
253 
263 {
264  Terminal::end();
265  clearHistory();
266  delete [] history;
267  curStart = 0;
268  curLen = 0;
269  curMax = sizeof(buffer);
270  history = 0;
271  historyWrite = 0;
272  historyRead = 0;
273  historySize = 0;
274  isClient = false;
275  lineMode = LINEMODE_NORMAL | LINEMODE_ECHO;
276  uid = -1;
277 }
278 
281 // Standard builtin command names.
282 static char const builtin_cmd_exit[] PROGMEM = "exit";
283 static char const builtin_cmd_help[] PROGMEM = "help";
284 static char const builtin_cmd_help_alt[] PROGMEM = "?";
285 
295 {
296  // If the stream is a TCP client, then check for disconnection.
297  if (isClient && !((Client *)stream())->connected()) {
298  end();
299  return;
300  }
301 
302  // If the login delay is active, then suppress all input.
303  if (lineMode & LINEMODE_DELAY) {
304  if ((millis() - timer) >= LOGIN_SHELL_DELAY) {
305  lineMode &= ~LINEMODE_DELAY;
306  timer = 0;
307  } else {
308  readKey();
309  return;
310  }
311  }
312 
313  // Print the prompt if necessary.
314  if (lineMode & LINEMODE_PROMPT)
315  printPrompt();
316 
317  // Read the next key and bail out if none. We only process a single
318  // key each time we enter this function to prevent other tasks in the
319  // system from becoming starved of time resources if the bytes are
320  // arriving rapidly from the underyling stream.
321  int key = readKey();
322  if (key == -1)
323  return;
324 
325  // Process the key.
326  switch (key) {
327  case KEY_BACKSPACE:
328  // Backspace over the last character.
329  clearCharacters(1);
330  break;
331 
332  case KEY_RETURN:
333  // CR, LF, or CRLF pressed, so execute the current command.
334  execute();
335  break;
336 
337  case 0x15:
338  // CTRL-U - clear the entire command.
339  clearCharacters(curLen);
340  break;
341 
342  case 0x04:
343  // CTRL-D - equivalent to the "exit" command.
344  if (lineMode & LINEMODE_NORMAL)
345  executeBuiltin(builtin_cmd_exit);
346  break;
347 
348  case KEY_UP_ARROW:
349  // Go back one item in the command history.
350  if ((lineMode & LINEMODE_NORMAL) != 0 &&
351  history && historyRead > 0) {
352  changeHistory(true);
353  }
354  break;
355 
356  case KEY_DOWN_ARROW:
357  // Go forward one item in the command history.
358  if ((lineMode & LINEMODE_NORMAL) != 0 &&
359  history && historyRead < historyWrite) {
360  changeHistory(false);
361  }
362  break;
363 
364  case KEY_F1:
365  // F1 is equivalent to the "help" command.
366  if (lineMode & LINEMODE_NORMAL)
367  executeBuiltin(builtin_cmd_help);
368  break;
369 
370  case KEY_UNICODE: {
371  // Add the Unicode code point to the buffer if it will fit.
372  long code = unicodeKey();
373  size_t size = Terminal::utf8Length(code);
374  if (size && (curLen + size) < (curMax - 1)) {
375  Terminal::utf8Format((uint8_t *)(buffer + curLen), code);
376  if (lineMode & LINEMODE_ECHO)
377  write((uint8_t *)(buffer + curLen), size);
378  curLen += size;
379  }
380  } break;
381 
382  default:
383  if (key >= 0x20 && key <= 0x7E) {
384  // Printable ASCII character - echo and add it to the buffer.
385  if (curLen < (curMax - 1)) {
386  if (lineMode & LINEMODE_ECHO)
387  write((uint8_t)key);
388  buffer[curLen++] = (char)key;
389  }
390  }
391  break;
392  }
393 }
394 
395 #if defined(__AVR__)
396 
397 // String compare of two strings in program memory.
398 static int progmem_strcmp(const char *str1, const char *str2)
399 {
400  uint8_t ch1, ch2;
401  for (;;) {
402  ch1 = pgm_read_byte((const uint8_t *)str1);
403  ch2 = pgm_read_byte((const uint8_t *)str2);
404  if (!ch1) {
405  if (ch2)
406  return -1;
407  else
408  break;
409  } else if (!ch2) {
410  return 1;
411  } else if (ch1 != ch2) {
412  return ((int)ch1) - ((int)ch2);
413  }
414  ++str1;
415  ++str2;
416  }
417  return 0;
418 }
419 
420 #else
421 
422 #define progmem_strcmp(str1,str2) (strcmp((str1), (str2)))
423 
424 #endif
425 
426 // Reads the "name" field from a command information block in program memory.
427 static const char *readInfoName(const ShellCommandInfo *info)
428 {
429 #if defined(__AVR__)
430  return (const char *)pgm_read_word
431  (((const uint8_t *)info) + offsetof(ShellCommandInfo, name));
432 #else
433  return info->name;
434 #endif
435 }
436 
437 // Reads the "help" field from a command information block in program memory.
438 static const char *readInfoHelp(const ShellCommandInfo *info)
439 {
440 #if defined(__AVR__)
441  return (const char *)pgm_read_word
442  (((const uint8_t *)info) + offsetof(ShellCommandInfo, help));
443 #else
444  return info->help;
445 #endif
446 }
447 
448 // Reads the "func" field from a command information block in program memory.
449 static ShellCommandFunc readInfoFunc(const ShellCommandInfo *info)
450 {
451 #if defined(__AVR__)
452  if (sizeof(ShellCommandFunc) == 2) {
453  return (ShellCommandFunc)pgm_read_word
454  (((const uint8_t *)info) + offsetof(ShellCommandInfo, func));
455  } else {
456  return (ShellCommandFunc)pgm_read_dword
457  (((const uint8_t *)info) + offsetof(ShellCommandInfo, func));
458  }
459 #else
460  return info->func;
461 #endif
462 }
463 
464 static ShellCommandRegister *firstCmd = 0;
465 
472 void Shell::registerCommand(ShellCommandRegister *cmd)
473 {
474  // Insert the command into the list in alphanumeric order.
475  // We cannot rely upon the construction order to sort the list for us.
476  ShellCommandRegister *prev = 0;
477  ShellCommandRegister *current = firstCmd;
478  while (current != 0) {
479  if (progmem_strcmp(readInfoName(cmd->info), readInfoName(current->info)) < 0)
480  break;
481  prev = current;
482  current = current->next;
483  }
484  if (prev)
485  prev->next = cmd;
486  else
487  firstCmd = cmd;
488  cmd->next = current;
489 }
490 
543 {
544  // Find the command with the maximum length.
545  ShellCommandRegister *current = firstCmd;
546  size_t maxLen = 0;
547  size_t len;
548  while (current != 0) {
549  len = strlen_P(readInfoName(current->info));
550  if (len > maxLen)
551  maxLen = len;
552  current = current->next;
553  }
554  maxLen += 2;
555 
556  // Print the commands with the help strings aligned on the right.
557  current = firstCmd;
558  while (current != 0) {
559  writeProgMem(readInfoName(current->info));
560  len = maxLen - strlen_P(readInfoName(current->info));
561  while (len > 0) {
562  write(' ');
563  --len;
564  }
565  writeProgMem(readInfoHelp(current->info));
566  println();
567  current = current->next;
568  }
569 }
570 
578 {
579  Stream *stream = this->stream();
580  uid = -1;
581  if (isClient) {
582  end();
583  ((Client *)stream)->stop();
584  } else {
585  clearHistory();
586  println();
587  beginSession();
588  }
589 }
590 
595 {
596  // No login support in the base class, so enter normal mode immediately.
597  lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
598 }
599 
604 {
605  if (prom)
606  print(prom);
607  lineMode &= ~LINEMODE_PROMPT;
608 }
609 
614 {
615  // Terminate the current line.
616  println();
617 
618  // Make sure the command is properly NUL-terminated.
619  buffer[curLen] = '\0';
620 
621  // If we have a history stack and the new command is different from
622  // the previous command, then copy the command into the stack.
623  if (history && curLen > curStart) {
624  char *prevCmd;
625  bool newCmd = true;
626  if (historyWrite > 0) {
627  prevCmd = (char *)memrchr(history, '\0', historyWrite - 1);
628  if (prevCmd)
629  ++prevCmd;
630  else
631  prevCmd = history;
632  if (strcmp(prevCmd, buffer + curStart) == 0)
633  newCmd = false;
634  }
635  if (newCmd) {
636  size_t len = curLen - curStart;
637  while ((len + 1) > (historySize - historyWrite)) {
638  // History stack is full. Pop older entries to get some room.
639  prevCmd = (char *)memchr(history, '\0', historyWrite);
640  if (prevCmd) {
641  size_t histLen = historyWrite - ((prevCmd + 1) - history);
642  memmove(history, prevCmd + 1, histLen);
643  historyWrite = histLen;
644  } else {
645  historyWrite = 0;
646  break;
647  }
648  }
649  memcpy(history + historyWrite, buffer + curStart, len);
650  historyWrite += len;
651  history[historyWrite++] = '\0';
652  }
653  }
654 
655  // Reset the history read position to the top of the stack.
656  historyRead = historyWrite;
657 
658  // Break the command up into arguments and populate the argument array.
659  ShellArguments argv(buffer + curStart, curLen - curStart);
660 
661  // Clear the line buffer.
662  curLen = curStart;
663 
664  // Execute the command.
665  if (argv.count() > 0) {
666  if (!execute(argv)) {
667  // Could not find a matching command, try the builtin "help".
668  const char *argv0 = argv[0];
669  if (!strcmp_P(argv0, builtin_cmd_help) ||
670  !strcmp_P(argv0, builtin_cmd_help_alt)) {
671  help();
672  } else if (!strcmp_P(argv0, builtin_cmd_exit)) {
673  exit();
674  } else {
675  static char const unknown_cmd[] PROGMEM = "Unknown command: ";
676  writeProgMem(unknown_cmd);
677  print(argv0);
678  println();
679  }
680  }
681  }
682 
683  // Prepare to print the prompt for the next command.
684  lineMode |= LINEMODE_PROMPT;
685 }
686 
694 bool Shell::execute(const ShellArguments &argv)
695 {
696  const char *argv0 = argv[0];
697  ShellCommandRegister *current = firstCmd;
698  while (current != 0) {
699  if (!strcmp_P(argv0, readInfoName(current->info))) {
700  ShellCommandFunc func = readInfoFunc(current->info);
701  (*func)(*this, argv.count(), argv);
702  return true;
703  }
704  current = current->next;
705  }
706  return false;
707 }
708 
714 void Shell::executeBuiltin(const char *cmd)
715 {
716  clearCharacters(curLen);
717  curLen = strlen_P(cmd);
718  strncpy_P(buffer + curStart, cmd, curLen);
719  write((const uint8_t *)(buffer + curStart), curLen);
720  curLen += curStart;
721  execute();
722 }
723 
729 void Shell::clearCharacters(size_t len)
730 {
731  // If the characters are hidden, then there's nothing to backspace over.
732  if (!(lineMode & LINEMODE_ECHO))
733  return;
734 
735  // Backspace over all characters in the buffer.
736  while (len > 0 && curLen > curStart) {
737  uint8_t ch = (uint8_t)(buffer[curLen - 1]);
738  if (ch < 0x80) {
739  backspace();
740  } else {
741  // UTF-8 character sequence. Back up some more and
742  // determine the value of the Unicode code point.
743  long code = (ch & 0x3F);
744  uint8_t shift = 6;
745  while (curLen > 1) {
746  --curLen;
747  ch = (uint8_t)(buffer[curLen - 1]);
748  if ((ch & 0xC0) != 0x80)
749  break;
750  code |= ((long)(ch & 0x3F)) << shift;
751  shift += 6;
752  }
753  if ((ch & 0xE0) == 0xC0)
754  ch &= 0x1F;
755  else if ((ch & 0xF0) == 0xE0)
756  ch &= 0x0F;
757  else
758  ch &= 0x07;
759  code |= ((long)ch) << shift;
760 
761  // If the character is wide, we need to emit two backspaces.
762  if (isWideCharacter(code))
763  backspace();
764  backspace();
765  }
766  --len;
767  --curLen;
768  }
769 }
770 
777 void Shell::changeHistory(bool up)
778 {
779  char *cmd;
780  if (up) {
781  cmd = (char *)memrchr(history, '\0', historyRead - 1);
782  if (cmd)
783  historyRead = (size_t)(cmd - history + 1);
784  else
785  historyRead = 0;
786  } else {
787  cmd = (char *)memchr(history + historyRead, '\0', historyWrite - historyRead);
788  if (cmd)
789  historyRead = (size_t)(cmd - history + 1);
790  else
791  historyRead = historyWrite;
792  }
793  clearCharacters(curLen);
794  if (historyRead < historyWrite) {
795  cmd = history + historyRead;
796  curLen = strlen(cmd);
797  if (curLen > (curMax - curStart))
798  curLen = curMax - curStart;
799  memcpy(buffer + curStart, cmd, curLen);
800  write((uint8_t *)cmd, curLen);
801  curLen += curStart;
802  }
803 }
804 
811 void Shell::clearHistory()
812 {
813  if (history)
814  memset(history, 0, historySize);
815  historyRead = 0;
816  historyWrite = 0;
817  memset(buffer, 0, sizeof(buffer));
818 }
819 
853 ShellArguments::ShellArguments(char *buffer, size_t len)
854  : line(buffer)
855  , size(0)
856  , argc(0)
857  , currentIndex(0)
858  , currentPosn(0)
859 {
860  // Break the command up into arguments and add NUL terminators.
861  size_t posn = 0;
862  size_t outposn = 0;
863  char quote = 0;
864  while (posn < len) {
865  char ch = buffer[posn];
866  if (ch == ' ') {
867  ++posn;
868  continue;
869  }
870  ++argc;
871  do {
872  ch = buffer[posn];
873  if (ch == '"' || ch == '\'') {
874  if (quote == ch) {
875  quote = 0;
876  ++posn;
877  continue;
878  } else if (!quote) {
879  quote = ch;
880  ++posn;
881  continue;
882  }
883  } else if (!quote && ch == ' ') {
884  break;
885  }
886  buffer[outposn++] = ch;
887  ++posn;
888  } while (posn < len);
889  buffer[outposn++] = '\0';
890  if (posn < len)
891  ++posn;
892  }
893  size = outposn;
894 }
895 
927 const char *ShellArguments::operator[](int index) const
928 {
929  if (index < 0 || index >= argc) {
930  // Argument index is out of range.
931  return 0;
932  } else if (index == currentIndex) {
933  // We already found this argument last time.
934  return line + currentPosn;
935  } else {
936  // Search forwards or backwards for the next argument.
937  const char *temp;
938  while (index > currentIndex) {
939  temp = (const char *)memchr
940  (line + currentPosn, '\0', size - currentPosn);
941  if (!temp)
942  return 0;
943  currentPosn = ((size_t)(temp - line)) + 1;
944  ++currentIndex;
945  }
946  while (index < currentIndex) {
947  temp = (const char *)memrchr(line, '\0', currentPosn - 1);
948  if (temp)
949  currentPosn = ((size_t)(temp - line)) + 1;
950  else
951  currentPosn = 0;
952  --currentIndex;
953  }
954  return line + currentPosn;
955  }
956 }
957 
959 {
960  lineMode = LINEMODE_USERNAME | LINEMODE_ECHO | LINEMODE_PROMPT;
961  curStart = 0;
962  curLen = 0;
963  curMax = sizeof(buffer) / 2;
964 }
965 
1008 {
1009  static char const loginString[] PROGMEM = "login: ";
1010  static char const passwordString[] PROGMEM = "Password: ";
1011  if (lineMode & LINEMODE_NORMAL) {
1012  // Print the prompt for normal command entry.
1013  if (prom)
1014  print(prom);
1015 
1016  // Normal commands occupy the full command buffer.
1017  curStart = 0;
1018  curLen = 0;
1019  curMax = sizeof(buffer);
1020  } else if (lineMode & LINEMODE_USERNAME) {
1021  // Print the machine name and the login prompt.
1022  if (machName) {
1023  print(machName);
1024  write((uint8_t)' ');
1025  }
1026  writeProgMem(loginString);
1027 
1028  // Login name is placed into the first half of the line buffer.
1029  curStart = 0;
1030  curLen = 0;
1031  curMax = sizeof(buffer) / 2;
1032  } else if (lineMode & LINEMODE_PASSWORD) {
1033  // Print the password prompt.
1034  writeProgMem(passwordString);
1035 
1036  // Password is placed into the second half of the line buffer.
1037  curStart = sizeof(buffer) / 2;
1038  curLen = curStart;
1039  curMax = sizeof(buffer);
1040  }
1041  lineMode &= ~LINEMODE_PROMPT;
1042 }
1043 
1044 // Default password checking function. This is not a very good security check!
1045 static int defaultPasswordCheckFunc(const char *username, const char *password)
1046 {
1047  static char const defaultUsername[] PROGMEM = "root";
1048  static char const defaultPassword[] PROGMEM = "arduino";
1049  if (!strcmp_P(username, defaultUsername) &&
1050  !strcmp_P(password, defaultPassword)) {
1051  return 0;
1052  } else {
1053  return -1;
1054  }
1055 }
1056 
1058 {
1059  if (lineMode & LINEMODE_NORMAL) {
1060  // Normal command execution.
1061  Shell::execute();
1062  } else if (lineMode & LINEMODE_USERNAME) {
1063  // Prompting for the login username.
1064  buffer[curLen] = '\0';
1065  lineMode = LINEMODE_PASSWORD | LINEMODE_PROMPT;
1066  println();
1067  } else if (lineMode & LINEMODE_PASSWORD) {
1068  // Prompting for the login password.
1069  buffer[curLen] = '\0';
1070  println();
1071 
1072  // Check the user name and password.
1073  int userid;
1074  if (checkFunc)
1075  userid = checkFunc(buffer, buffer + sizeof(buffer) / 2);
1076  else
1077  userid = defaultPasswordCheckFunc(buffer, buffer + sizeof(buffer) / 2);
1078 
1079  // Clear the user name and password from memory after they are checked.
1080  memset(buffer, 0, sizeof(buffer));
1081 
1082  // Go to either normal mode or back to username mode.
1083  if (userid >= 0) {
1084  uid = userid;
1085  lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
1086  } else {
1087  lineMode = LINEMODE_USERNAME | LINEMODE_ECHO |
1088  LINEMODE_PROMPT | LINEMODE_DELAY;
1089  timer = millis();
1090  }
1091  }
1092 }
const char * operator[](int index) const
Gets a specific argument for the command.
Definition: Shell.cpp:927
virtual void beginSession()
Begins a login session.
Definition: Shell.cpp:594
void help()
Displays help for all supported commands.
Definition: Shell.cpp:542
long unicodeKey() const
Gets the Unicode version of the last key returned by readKey().
Definition: Terminal.h:68
bool begin(Stream &stream, size_t maxHistory=0, Terminal::Mode mode=Serial)
Begin shell handling on an underlying character stream.
Definition: Shell.cpp:176
virtual size_t write(uint8_t c)
Writes a single byte to the underlying stream.
Definition: Terminal.cpp:286
virtual void printPrompt()
Prints the current prompt string.
Definition: Shell.cpp:603
static void registerCommand(ShellCommandRegister *cmd)
Registers a command with the shell.
Definition: Shell.cpp:472
void begin(Stream &stream, Mode mode=Serial)
Begins terminal operations on an underlying stream.
Definition: Terminal.cpp:183
void exit()
Exit from the shell back to the login prompt.
Definition: Shell.cpp:577
void backspace()
Backspaces over the last character.
Definition: Terminal.cpp:949
virtual void printPrompt()
Prints the current prompt string.
Definition: Shell.cpp:1007
void loop()
Performs regular activities on the shell.
Definition: Shell.cpp:294
Shell()
Constructs a new Shell instance.
Definition: Shell.cpp:122
Mode
Mode to operate in, Serial or Telnet.
Definition: Terminal.h:42
Stream * stream() const
Returns a pointer to the underlying Stream, or NULL if the stream has not been set with begin() yet...
Definition: Terminal.h:51
virtual void beginSession()
Begins a login session.
Definition: Shell.cpp:958
ShellCommandFunc
Type of functions that provide shell command handlers.
int count() const
Returns the number of arguments, including the name of the command.
Definition: Shell.h:127
void writeProgMem(const char *str)
Writes a static string that is stored in program memory.
Definition: Terminal.cpp:314
virtual void execute()
Executes the command in the buffer.
Definition: Shell.cpp:613
static size_t utf8Format(uint8_t *buffer, long code)
Formats a Unicode code point in a buffer in the UTF-8 encoding.
Definition: Terminal.cpp:1334
static size_t utf8Length(long code)
Determines the length of a Unicode code point in the UTF-8 encoding.
Definition: Terminal.cpp:1302
virtual void execute()
Executes the command in the buffer.
Definition: Shell.cpp:1057
void end()
Ends terminal operations on an underlying stream.
Definition: Terminal.cpp:198
int readKey()
Reads the next key that was typed on this terminal.
Definition: Terminal.cpp:387
Convenience class that encapsulates an array of shell command arguments.
Definition: Shell.h:119
void end()
Ends shell processing on the underlying stream.
Definition: Shell.cpp:262
static bool isWideCharacter(long code)
Determine if a Unicode character is wide.
Definition: Terminal.cpp:1247
virtual ~Shell()
Destroys this Shell object.
Definition: Shell.cpp:141
int userid() const
Gets the user identifier for the currently logged in user, or -1 if there is no user logged in curren...
Definition: Shell.h:79