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