void cConsole::scrollHistory(int dir) { // Changing position cmdHistoryPos += dir; if (cmdHistoryPos > (int)cmdHistory.size()) { cmdHistoryPos = (int)cmdHistory.size(); } else if (cmdHistoryPos < 0) { cmdHistoryPos = 0; } // Adding the command from history if (cmdHistoryPos < (int)cmdHistory.size() + 1) { clearInput(); if (cmdHistoryPos < (int)cmdHistory.size()) { addToInput(cmdHistory[cmdHistoryPos]); } } }
/** * Auto complete the word under the cursor. * * @param prompt The prompt to print. */ void autoComplete(struct Shell* self, const char* prompt) { int i, candidates = 0, len, last = 0; size_t promptLen, addedLen; char* fragment; // Let's find the start of the word before the cursor for (i = self->cursor - 1; i >= 0 && self->buffer[i] != ' '; i--); if (i == -1) { // We're at the first word i = 0; } else if (i != self->inputEnd) { // This isn't the first word, so the for loop will end at the space // We need to skip that space, and take what's after i++; } len = self->cursor - i; fragment = self->buffer + i; if (len == 0) { // Every command is fair game candidates = NUM_COMMANDS; } else { // Count possible matches // Keep record of the last one, incase only one is found for (i = 0; i < NUM_COMMANDS; i++) { if (strncmp(fragment, commands[i].name, len) == 0) { candidates++; last = i; } } } promptLen = strlen(prompt) + 7; if (candidates == 0) { //TODO: System speaker? return; } else if (candidates == 1) { //Complete it :D addedLen = strlen(commands[last].name) - len; // If the addedLen is 0 then the word is already complete! There's nothng to add if (addedLen != 0) { addToInput(self, promptLen, commands[last].name + len, addedLen); } } else { int cursorPos = self->cursor; // Show a list of possibles, using one line per 2 commands candidates = 0; for (i = 0; i < NUM_COMMANDS; i++) { if (strncmp(fragment, commands[i].name, len) == 0) { if ((candidates % 2) == 0) { putchar('\n'); moveCursorInRow(4); } else { moveCursorInRow(LINE_WIDTH / 2 + 4); } candidates++; printf("%s", commands[i].name); } } putchar('\n'); // We need to reprint the prompt and reposition the cursor afterwards printPrompt(self, prompt); printf("%s", self->buffer); self->cursor = self->inputEnd; updateCursor(self, promptLen, cursorPos); } }
/** * Parse & find the next command. * * @param prompt The prompt to print. * * @return The command to execute, if one was found, NULL otherwise. */ const Command* nextCommand(struct Shell* self, const char* prompt) { size_t promptLen = strlen(prompt) + 7; int i; char in; // We reset the input status. self->cursor = 0; self->inputEnd = 0; self->usingHistory = 0; struct History* history = &self->history; printPrompt(self, prompt); // We set the whole string to null for safety. memset(self->buffer, 0, BUFFER_SIZE); while ((in = getchar()) != '\n') { // Check for control sequences, these are things like arrow keys and such. if (in == '\033') { if (getchar() == CSI) { // We know this! Yay! switch (getchar()) { case 'A': // Up if (history->current != history->start) { history->current--; if (history->current < 0) { history->current = HISTORY_SIZE - 1; } self->inputEnd = replaceInput( self, promptLen, history->input[history->current] ); self->cursor = self->inputEnd; self->usingHistory = 1; } break; case 'B': // Down if (self->usingHistory) { if (history->current == history->end) { self->usingHistory = 0; self->inputEnd = replaceInput(self, promptLen, self->buffer); } else { history->current = (history->current + 1) % HISTORY_SIZE; self->inputEnd = replaceInput( self, promptLen, history->input[history->current] ); } self->cursor = self->inputEnd; } break; case 'C': // Right if (self->cursor == self->inputEnd) { break; } updateCursor(self, promptLen, self->cursor + 1); break; case 'D': // Left if (self->cursor == 0) { break; } updateCursor(self, promptLen, self->cursor - 1); break; case 'H': // Home updateCursor(self, promptLen, 0); break; case 'F': // End updateCursor(self, promptLen, self->inputEnd); break; case CSI: //This is a function key in = getchar(); // We don't support this anymore (not from here at least) break; } } } else if (in == '\t') { if (self->usingHistory) { chooseCurrentEntry(self); } autoComplete(self, prompt); } else if (in == '\b') { if (self->usingHistory) { chooseCurrentEntry(self); } if (self->cursor > 0) { int destPos = self->cursor - 1; self->inputEnd--; // Move back once to step on the previous text updateCursor(self, promptLen, self->cursor - 1); for (i = self->cursor; i < self->inputEnd; i++) { self->buffer[i] = self->buffer[i + 1]; } // Set a space in the end to make sure we erase previous text self->buffer[self->inputEnd] = ' '; // Print out printf("%s", self->buffer + self->cursor); // The input actually ends one after (the space we inserted) self->cursor = self->inputEnd + 1; updateCursor(self, promptLen, destPos); // Make sure the buffer is always null terminated self->buffer[self->inputEnd] = 0; } } else if (!isspace(in) || in == ' ') { if (self->usingHistory) { chooseCurrentEntry(self); } addToInput(self, promptLen, &in, 1); } } if (self->usingHistory) { // This means enter was pressed while browsing the history // So let's take the current history entry as the input memcpy(self->buffer, history->input[history->current], BUFFER_SIZE); } updateCursor(self, promptLen, self->inputEnd); putchar('\n'); addToHistory(history, self->buffer); return findCommand(self->buffer); }