diff options
Diffstat (limited to 'mini-commander/src/cmd_completion.c')
-rw-r--r-- | mini-commander/src/cmd_completion.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/mini-commander/src/cmd_completion.c b/mini-commander/src/cmd_completion.c new file mode 100644 index 00000000..7b91ff5b --- /dev/null +++ b/mini-commander/src/cmd_completion.c @@ -0,0 +1,246 @@ +/* + * Mini-Commander Applet + * Copyright (C) 1998, 1999 Oliver Maruhn <[email protected]> + * + * Author: Oliver Maruhn <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + If you expect tons of C code for command completion then you will + probably be astonished... + + These routines have probably to be rewritten in future. But they + should work on every system with a bash-alike shell. +*/ + +#include <config.h> +#include <string.h> +#include <stdlib.h> + +#include <mate-panel-applet.h> + +#include <sys/stat.h> +#include <dirent.h> + +#include "cmd_completion.h" +#include "preferences.h" +#include "macro.h" + + +static GList* cmdc( char* ); +static void process_dir( const gchar* ); +static void cleanup( void ); +static gint g_list_str_cmp( gconstpointer, gconstpointer ); + +/* global declaration so g_atexit() can reference it to free it. + * (not strictly necessary). + */ +static GList *path_elements = NULL; + +void +mc_cmd_completion (MCData *mc, + char *cmd) +{ + char buffer[MC_MAX_COMMAND_LENGTH] = ""; + char largest_possible_completion[MC_MAX_COMMAND_LENGTH] = ""; + int num_whitespaces, i, pos; + + GList *possible_completions_list = NULL; + GList *completion_element; + + + if(strlen(cmd) == 0) + { + return; + } + + num_whitespaces = mc_macro_prefix_len_wspace (mc, cmd) - mc_macro_prefix_len (mc, cmd); + possible_completions_list = cmdc(cmd + mc_macro_prefix_len_wspace (mc, cmd)); + + /* get first possible completion */ + completion_element = g_list_first(possible_completions_list); + if(completion_element) + strcpy(largest_possible_completion, (char *) completion_element->data); + else + strcpy(largest_possible_completion, ""); + + /* get the rest */ + while((completion_element = g_list_next(completion_element))) + { + strcpy(buffer, (char *) completion_element->data); + pos = 0; + while(largest_possible_completion[pos] != '\000' + && buffer[pos] != '\000' + && strncmp(largest_possible_completion, buffer, pos + 1) == 0) + pos++; + strncpy(largest_possible_completion, buffer, pos); + /* strncpy does not add \000 to the end */ + largest_possible_completion[pos] = '\000'; + } + + if(strlen(largest_possible_completion) > 0) + { + if(mc_macro_get_prefix(mc, cmd) != NULL) + strcpy(cmd, mc_macro_get_prefix(mc, cmd)); + else + strcpy(cmd, ""); + + /* fill up the whitespaces */ + for(i = 0; i < num_whitespaces; i++) + strcat(cmd, " "); + + strcat(cmd, largest_possible_completion); + } +} + +/* + * cmdc() -- command completion function. + * + * cmdc takes a char* and returns a GList* of possible completions. + * + * Initial version by Travis Hume <[email protected]>. + * + */ +static GList * +cmdc( char *s ) +{ + GCompletion *completion = NULL; + GList *ret_list = NULL; + static GHashTable *path_hash = NULL; + static char *path = NULL; + gchar *path_elem; + struct stat buf; + static gboolean inited = FALSE; + gpointer hash_key = NULL; + + + /* + * Only want to build the GCompletion once. At some point I'd like to add + * code to refresh the GCompletion, either at a regular interval, or when + * there is a completion failure, ... + * + */ + if(!inited) + { + /* Make a local copy of the path variable. Otherwise the path + environment variable would be modified. */ + path = (char *) malloc(sizeof(char) * (strlen(getenv("PATH")) + 1)); + strcpy(path, getenv("PATH")); + + path_hash = g_hash_table_new( g_str_hash, g_str_equal ); + + for( path_elem = strtok( path, ":" ); path_elem; + path_elem = strtok( NULL, ":" )) + { + if( stat( path_elem, &buf )) + continue; + + if( buf.st_mode & S_IFDIR ) + { + /* keep a hash of processed paths, to avoid reprocessing + * dupped path entries. + */ + hash_key = g_hash_table_lookup( path_hash, path_elem ); + if( hash_key ) + continue; /* duplicate $PATH entry */ + else + { + g_hash_table_insert( + path_hash, (gpointer)path_elem, (gpointer)path_elem ); + + process_dir( path_elem ); + } + } + } + + /* atexit() we want to free the completion. */ + g_atexit( cleanup ); + + inited = TRUE; + } + + completion = g_completion_new( NULL ); + g_completion_add_items( completion, path_elements ); + ret_list = g_list_copy( g_completion_complete( completion, s, NULL )); + g_completion_free( completion ); + + return g_list_sort( ret_list, (GCompareFunc)g_list_str_cmp ); +} + + +/* + * Compare function to return a sorted completion. + */ +static gint +g_list_str_cmp( gconstpointer a, gconstpointer b ) +{ + return( strcmp( (char *)a, (char *)b )); +} + + + +/* + * Reads directory entries and adds non-dir, executable files + * to the GCompletion. + * Initial version by Travis Hume <[email protected]>. + */ +static void +process_dir( const char *d ) +{ + DIR *dir; + struct dirent *de; + struct stat buf; + gpointer data; + gchar *path_str; + + + if( (dir = opendir( d )) == NULL ) + return; + + while( (de = readdir( dir )) != NULL ) + { + if( strcmp( de->d_name, "." ) == 0 || strcmp( de->d_name, "..") == 0) + continue; + + path_str = (gchar *)g_malloc( strlen( d ) + 1 + strlen( de->d_name ) + 1); + strcpy( path_str, d ); + strcat( path_str, "/" ); + strcat( path_str, de->d_name ); + if( stat( path_str, &buf ) != 0) + continue; + g_free( (gpointer)path_str ); + + if( S_ISDIR( buf.st_mode ) ) + continue; + + data = g_malloc( strlen( (gchar *)de->d_name ) + 1 ); + strcpy( (gchar *)data, (gchar *)(de->d_name) ); + if( buf.st_mode & S_IXUSR ) + path_elements = g_list_append( path_elements, data ); + } + closedir( dir ); +} + + +/* + * cleanup() -- free the memory used by the GCompletion. + */ +static void +cleanup( void ) +{ + g_list_free( path_elements ); +} |