[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Lost] [Patch] Tabcompletion für readline() und shell
+ libc: Tab-Completion fuer readline()
+ shell: Tab-Completion
Index: lib/readline.c
===================================================================
--- lib/readline.c (Revision 820)
+++ lib/readline.c (Arbeitskopie)
@@ -56,6 +56,12 @@
static list_t* history = NULL;
+/** Pointer auf den attemped_completion-Handler */
+__rl_attemped_completion_func_t* rl_attempted_completion_function = NULL;
+
+/** Pointer auf den Zeilenpuffer von Readline */
+char* rl_line_buffer;
+
/**
* Einzelnes Zeichen von der Tastatur einlesen
*
@@ -121,6 +127,68 @@
}
/**
+ * Wort verfollstaendigen
+ */
+static void complete_word(char* buffer, int* pos, int* len)
+{
+ int word_pos;
+ int match_count;
+ int word_len;
+ char** matches;
+ char* repl;
+ int repl_len;
+
+ // Zuerst das zu ergaenzende Wort heraussuchen (den Anfang)
+ for (word_pos = *pos; (buffer[word_pos] != ' ') && (word_pos > 0); word_pos--);
+ if (buffer[word_pos] == ' ') {
+ word_pos++;
+ }
+ word_len = *pos - word_pos;
+ //printf("wordl_len:%d %d\n\n", word_len, word_pos);
+
+ // Wort in einen eigenen Buffer kopieren
+ char word[word_len + 1];
+ strncpy(word, buffer + word_pos, word_len);
+ word[word_len] = 0;
+
+ matches = rl_attempted_completion_function(word, word_pos, *pos);
+
+ // Wir haben nochmal Glueck gehabt, es gibt nicht zu vervollstaendigen ;-)
+ if (matches == NULL) {
+ return;
+ }
+
+ // Und sonst tun wir im Moment nur was, wenns genau eine Moeglichkeit
+ // gibt.
+ for (match_count = 0; matches[match_count] != NULL; match_count++);
+
+ // TODO:
+ if (match_count > 1) {
+ return;
+ } else {
+ repl = matches[0];
+ repl_len = strlen(repl);
+ }
+
+ // Jetzt muess das Wort im Puffer ersetzt werden
+ if (*len + repl_len > BUFFER_SIZE) {
+ puts("Buffer zu klein :-/");
+ return;
+ }
+
+ // Text, der dem zu ersetztenden Wort folgt nach hinten schieben
+ memmove(buffer + word_pos + repl_len + 1, buffer + *pos, (*len - word_len) -
+ word_pos + 1);
+
+ // Wort ersetzen
+ memcpy(buffer + word_pos, repl, repl_len);
+
+ // Position und Laenge des Puffers korrigieren
+ *pos = word_pos + repl_len;
+ *len = *len + repl_len - word_len ;
+}
+
+/**
* Prompt anzeigen und Zeile von der Tastatur einlesen
*/
char* readline(const char* prompt)
@@ -138,6 +206,8 @@
bool enter = FALSE;
int history_pos = -1;
+ rl_line_buffer = buffer;
+
printf("%s", prompt);
fflush(stdout);
@@ -268,6 +338,22 @@
fflush(stdout);
break;
+
+ case '\t':
+ if (rl_attempted_completion_function != NULL) {
+ // An den Anfang der Zeile navigieren
+ char* format;
+ asprintf(&format, "\033[%dD", pos);
+
+ complete_word(buffer, &pos, &size);
+
+ printf("%s%s", format, buffer);
+ fflush(stdout);
+
+ free(format);
+ }
+ break;
+
default:
if (pos < BUFFER_SIZE - 1) {
inschar(buffer, pos, c);
Index: include/readline/readline.h
===================================================================
--- include/readline/readline.h (Revision 820)
+++ include/readline/readline.h (Arbeitskopie)
@@ -35,9 +35,18 @@
#ifndef _READLINE_H_
#define _READLINE_H_
+/** Typ fuer den attemped_completion-Handler. */
+typedef char** (__rl_attemped_completion_func_t)(const char* text, int start,
+ int end);
+
+/** Pointer auf den attemped_completion-Handler */
+extern __rl_attemped_completion_func_t* rl_attempted_completion_function;
+
+
/**
* Prompt anzeigen und Zeile von der Tastatur einlesen
*/
char* readline(const char* prompt);
+
#endif
Index: c/shell/shell.c
===================================================================
--- c/shell/shell.c (Revision 825)
+++ c/shell/shell.c (Arbeitskopie)
@@ -58,10 +58,6 @@
#define COMMAND_BUFFER_SIZE 255
-typedef struct shell_command_t {
- const char* name;
- int (*handler)(int argc, char* argv[], const char* args);
-} shell_command_t;
char shell_command_buffer[COMMAND_BUFFER_SIZE];
@@ -101,8 +97,9 @@
{"ln", &shell_command_ln},
{"rm", &shell_command_rm},
{"stat", &shell_command_stat},
- {"bench", &shell_command_bench},
+ {"bench", &shell_command_bench}
#endif
+ ,{NULL, NULL}
};
@@ -139,6 +136,7 @@
// Keine Argumente wurden uebergeben, also wird die shell interaktiv
// aufgerufen.
if (argc == 1) {
+ completion_init();
while (TRUE) {
shell_read_command();
shell_do_cmd(shell_command_buffer);
@@ -248,7 +246,7 @@
// Die Liste mit den Befehlen durchsuchen. Das wird solange gemacht, bis
// der NULL eintrag am Ende erreicht wird.
- for (i = 0; i < sizeof(shell_commands) / sizeof(shell_command_t); i++)
+ for (i = 0; (command = &shell_commands[i]) && (command->name); i++)
{
command = &shell_commands[i];
if (shell_match_command(command->name, cmdstring) == TRUE) {
Index: c/shell/completion.c
===================================================================
--- c/shell/completion.c (Revision 0)
+++ c/shell/completion.c (Revision 0)
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2008 The LOST Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the LOST Project
+ * by Antoine Kaufmann.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the LOST Project
+ * and its contributors.
+ * 4. Neither the name of the LOST Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sleep.h>
+#include <collections.h>
+#include <readline/readline.h>
+#include <dir.h>
+
+#include "shell.h"
+
+/**
+ * Completion-Handler fuer Readline
+ *
+ * @param word Wort, das vervollstaendigt werden soll
+ * @param start Position im rl_line_buffer
+ * @param end Endposition im rl_line_buffer
+ */
+static char** rl_completion(const char* word, int start, int end);
+
+/**
+ * Matches in Befehlen suchen
+ *
+ * @return Pointer auf Array oder NULL wenn keine Gefunden wurden
+ */
+static char** shell_command_matches(const char* word);
+
+/**
+ * Matches in Dateien
+ *
+ * @return Pointer auf Array oder NULL wenn keine Gefunden wurden
+ */
+static char** shell_file_matches(const char* word);
+
+
+/**
+ * Vervollstaendigung initialisieren
+ */
+void completion_init()
+{
+ rl_attempted_completion_function = rl_completion;
+}
+
+static char** rl_completion(const char* word, int start, int end)
+{
+ char** matches = NULL;
+
+ // Befehle matchen, wenn am Anfang der Zeile
+ if (start == 0) {
+ matches = shell_command_matches(word);
+ }
+
+ if (matches == NULL) {
+ matches = shell_file_matches(word);
+ }
+
+ return matches;
+}
+
+static char** build_matches_array(list_t* list)
+{
+ char** matches;
+ char* match;
+ int i;
+
+ matches = malloc(sizeof(char*) * (list_size(list) + 1));
+ for (i = 0; (match = list_get_element_at(list, i)); i++) {
+ matches[i] = match;
+ }
+ matches[i] = NULL;
+
+ return matches;
+}
+
+static char** shell_command_matches(const char* word)
+{
+ int word_len = strlen(word);
+ shell_command_t* cmd;
+ int i;
+ char** matches;
+ list_t* matches_list = list_create();
+
+ for (i = 0; (cmd = &shell_commands[i]) && cmd->name; i++) {
+ if (strncmp(cmd->name, word, word_len) == 0) {
+ size_t namelen = strlen(cmd->name);
+ char name[namelen + 2];
+ strcpy(name, cmd->name);
+
+ // Wir wollen ein schoenes Leerzeichen nach dem Befehl
+ name[namelen] = ' ';
+ name[namelen + 1] = 0;
+
+ list_push(matches_list, strdup(name));
+ }
+ }
+
+ if (list_size(matches_list) == 0) {
+ list_destroy(matches_list);
+ return NULL;
+ }
+
+ matches = build_matches_array(matches_list);
+
+ list_destroy(matches_list);
+
+ return matches;
+}
+
+static char** shell_file_matches(const char* word)
+{
+ int word_len = strlen(word);
+ char path[word_len + 3];
+ char* dirname;
+ char* filename;
+ int dir_len;
+ int filename_len;
+ io_resource_t* dir;
+ io_direntry_t* dentry;
+ char** matches = NULL;
+ list_t* matches_list;
+
+ // Wenn ein slash am Ende ist, muessen wir noch ein Bisschen was anhaengen,
+ // damit dirname den ganzen String nimmt
+ if (word[word_len - 1] == '/') {
+ sprintf(path, "%s/.", word);
+ } else {
+ sprintf(path, "%s", word);
+ }
+
+ dirname = io_split_dirname(path);
+ dir_len = strlen(dirname);
+ filename = io_split_filename(path);
+ filename_len = strlen(filename);
+
+
+ dir = directory_open(dirname);
+ if (dir == NULL) {
+ goto out;
+ }
+
+ matches_list = list_create();
+ while ((dentry = directory_read(dir))) {
+ size_t namelen = strlen(dentry->name);
+ char name[namelen + dir_len + 3];
+ char end;
+
+ // Bei Verzeichnissen wollen wir einen Slash am Ende, sonst ein
+ // Leerschlag
+ if (dentry->type == IO_DIRENTRY_DIR) {
+ end = '/';
+ } else {
+ end = ' ';
+ }
+ sprintf(name, "%s%s%c", dirname, dentry->name, end);
+
+ if (strncmp(dentry->name, filename, filename_len) == 0) {
+ list_push(matches_list, strdup(name));
+ }
+
+ free(dentry);
+ }
+
+ if (list_size(matches_list) == 0) {
+ list_destroy(matches_list);
+ goto out;
+ }
+
+ matches = build_matches_array(matches_list);
+
+ list_destroy(matches_list);
+ directory_close(dir);
+
+out:
+ free(dirname);
+ free(filename);
+ return matches;
+}
+
Index: c/shell/shell.h
===================================================================
--- c/shell/shell.h (Revision 825)
+++ c/shell/shell.h (Arbeitskopie)
@@ -36,8 +36,18 @@
#ifndef _SHELL_H_
#define _SHELL_H_
+typedef struct shell_command_t {
+ const char* name;
+ int (*handler)(int argc, char* argv[], const char* args);
+} shell_command_t;
+
+/** Array mit den Befehlen */
+extern shell_command_t shell_commands[];
+
+
bool shell_script(const char* path);
bool shell_match_command(const char* cmd, const char* cmdline);
+void completion_init();
int shell_command_default(int argc, char* argv[], const char* args);
int shell_command_help(int argc, char* argv[], const char* args);