1998-02-27 07:54:42 +03:00
|
|
|
/* Input line filename/username/hostname/variable/command completion.
|
|
|
|
(Let mc type for you...)
|
|
|
|
|
2007-09-25 19:33:35 +04:00
|
|
|
Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
|
|
|
2007 Free Software Foundation, Inc.
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
Written by: 1995 Jakub Jelinek
|
|
|
|
|
|
|
|
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
|
2005-05-27 07:35:10 +04:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-02-05 21:28:18 +03:00
|
|
|
/** \file complete.c
|
|
|
|
* \brief Source: Input line filename/username/hostname/variable/command completion
|
|
|
|
*/
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <config.h>
|
2005-02-08 12:04:03 +03:00
|
|
|
|
|
|
|
#include <ctype.h>
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <stdio.h>
|
2005-02-08 12:04:03 +03:00
|
|
|
#include <stdlib.h>
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <string.h>
|
2009-03-07 15:23:15 +03:00
|
|
|
#include <dirent.h>
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2009-03-07 15:23:15 +03:00
|
|
|
#include <pwd.h>
|
2005-02-08 12:04:03 +03:00
|
|
|
#include <unistd.h>
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
#include "global.h"
|
2009-05-09 19:17:57 +04:00
|
|
|
|
2009-05-08 14:01:05 +04:00
|
|
|
#include "../src/tty/tty.h"
|
2009-05-09 19:17:57 +04:00
|
|
|
#include "../src/tty/key.h" /* XCTRL and ALT macros */
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
#include "dialog.h"
|
2003-10-25 03:20:30 +04:00
|
|
|
#include "widget.h"
|
1998-02-27 07:54:42 +03:00
|
|
|
#include "wtools.h"
|
2009-03-07 15:23:15 +03:00
|
|
|
#include "main.h" /* show_all_if_ambiguous */
|
2009-01-07 00:00:18 +03:00
|
|
|
#include "util.h"
|
2009-06-09 12:50:45 +04:00
|
|
|
#include "../src/strescape.h"
|
2008-12-29 01:52:45 +03:00
|
|
|
#include "strutil.h"
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-07-15 16:54:18 +04:00
|
|
|
typedef char *CompletionFunction (const char * text, int state, INPUT_COMPLETE_FLAGS flags);
|
2003-10-25 02:11:57 +04:00
|
|
|
|
2009-04-24 02:47:22 +04:00
|
|
|
/* #define DO_COMPLETION_DEBUG */
|
2009-01-31 22:46:32 +03:00
|
|
|
#ifdef DO_COMPLETION_DEBUG
|
|
|
|
/*
|
|
|
|
* Useful to print/debug completion flags
|
|
|
|
*/
|
|
|
|
static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags)
|
|
|
|
{
|
|
|
|
static char s_cf[] = "FHCVUDS";
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
s_cf[0] = (flags & INPUT_COMPLETE_FILENAMES) ? 'F' : ' ';
|
|
|
|
s_cf[1] = (flags & INPUT_COMPLETE_HOSTNAMES) ? 'H' : ' ';
|
|
|
|
s_cf[2] = (flags & INPUT_COMPLETE_COMMANDS) ? 'C' : ' ';
|
|
|
|
s_cf[3] = (flags & INPUT_COMPLETE_VARIABLES) ? 'V' : ' ';
|
|
|
|
s_cf[4] = (flags & INPUT_COMPLETE_USERNAMES) ? 'U' : ' ';
|
|
|
|
s_cf[5] = (flags & INPUT_COMPLETE_CD) ? 'D' : ' ';
|
|
|
|
s_cf[6] = (flags & INPUT_COMPLETE_SHELL_ESC) ? 'S' : ' ';
|
|
|
|
|
|
|
|
return s_cf;
|
|
|
|
}
|
|
|
|
#define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
|
|
|
|
#else
|
|
|
|
#define SHOW_C_CTX(func)
|
|
|
|
#endif /* DO_CMPLETION_DEBUG */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
1998-12-03 00:27:27 +03:00
|
|
|
static char *
|
2009-05-29 16:14:52 +04:00
|
|
|
filename_completion_function (const char * text, int state, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
static DIR *directory;
|
|
|
|
static char *filename = NULL;
|
|
|
|
static char *dirname = NULL;
|
|
|
|
static char *users_dirname = NULL;
|
2004-08-16 08:17:43 +04:00
|
|
|
static size_t filename_len;
|
1998-02-27 07:54:42 +03:00
|
|
|
int isdir = 1, isexec = 0;
|
|
|
|
|
|
|
|
struct dirent *entry = NULL;
|
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("filename_completion_function");
|
|
|
|
|
2009-05-29 16:14:52 +04:00
|
|
|
if (text && (flags & INPUT_COMPLETE_SHELL_ESC))
|
|
|
|
{
|
|
|
|
char * u_text;
|
|
|
|
char * result;
|
|
|
|
char * e_result;
|
|
|
|
|
2009-06-09 13:12:17 +04:00
|
|
|
u_text = strutils_shell_unescape (text);
|
2009-05-29 16:14:52 +04:00
|
|
|
|
|
|
|
result = filename_completion_function (u_text, state, flags & (~INPUT_COMPLETE_SHELL_ESC));
|
|
|
|
g_free (u_text);
|
|
|
|
|
2009-06-09 13:12:17 +04:00
|
|
|
e_result = strutils_shell_escape (result);
|
2009-05-29 16:14:52 +04:00
|
|
|
g_free (result);
|
|
|
|
|
|
|
|
return e_result;
|
|
|
|
}
|
2009-02-01 14:28:31 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* If we're starting the match process, initialize us a bit. */
|
|
|
|
if (!state){
|
2004-09-25 18:34:27 +04:00
|
|
|
const char *temp;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (dirname);
|
|
|
|
g_free (filename);
|
|
|
|
g_free (users_dirname);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2001-06-25 17:59:04 +04:00
|
|
|
if ((*text) && (temp = strrchr (text, PATH_SEP))){
|
2009-02-06 01:40:32 +03:00
|
|
|
filename = g_strdup (++temp);
|
2001-06-25 17:59:04 +04:00
|
|
|
dirname = g_strndup (text, temp - text);
|
|
|
|
} else {
|
2009-02-06 01:40:32 +03:00
|
|
|
dirname = g_strdup (".");
|
|
|
|
filename = g_strdup (text);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We aren't done yet. We also support the "~user" syntax. */
|
|
|
|
|
|
|
|
/* Save the version of the directory that the user typed. */
|
2001-06-25 17:59:04 +04:00
|
|
|
users_dirname = dirname;
|
2009-05-27 09:18:44 +04:00
|
|
|
dirname = tilde_expand (dirname);
|
|
|
|
canonicalize_pathname (dirname);
|
|
|
|
|
|
|
|
/* Here we should do something with variable expansion
|
|
|
|
and `command`.
|
|
|
|
Maybe a dream - UNIMPLEMENTED yet. */
|
|
|
|
|
1998-06-01 01:35:38 +04:00
|
|
|
directory = mc_opendir (dirname);
|
1998-02-27 07:54:42 +03:00
|
|
|
filename_len = strlen (filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now that we have some state, we can read the directory. */
|
|
|
|
|
1998-06-01 01:35:38 +04:00
|
|
|
while (directory && (entry = mc_readdir (directory))){
|
2009-05-27 09:18:44 +04:00
|
|
|
if (!str_is_valid_string (entry->d_name))
|
|
|
|
continue;
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Special case for no filename.
|
|
|
|
All entries except "." and ".." match. */
|
2009-05-27 09:18:44 +04:00
|
|
|
if (filename_len == 0) {
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
/* Otherwise, if these match up to the length of filename, then
|
|
|
|
it may be a match. */
|
|
|
|
if ((entry->d_name[0] != filename[0]) ||
|
|
|
|
((NLENGTH (entry)) < filename_len) ||
|
|
|
|
strncmp (filename, entry->d_name, filename_len))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
isdir = 1; isexec = 0;
|
|
|
|
{
|
2009-05-27 09:18:44 +04:00
|
|
|
char *tmp;
|
1998-02-27 07:54:42 +03:00
|
|
|
struct stat tempstat;
|
2009-05-27 09:18:44 +04:00
|
|
|
|
|
|
|
tmp = g_strconcat (dirname, PATH_SEP_STR, entry->d_name, (char *) NULL);
|
1998-02-27 07:54:42 +03:00
|
|
|
canonicalize_pathname (tmp);
|
|
|
|
/* Unix version */
|
1998-06-01 01:35:38 +04:00
|
|
|
if (!mc_stat (tmp, &tempstat)){
|
1998-02-27 07:54:42 +03:00
|
|
|
uid_t my_uid = getuid ();
|
|
|
|
gid_t my_gid = getgid ();
|
2009-05-27 09:18:44 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!S_ISDIR (tempstat.st_mode)){
|
|
|
|
isdir = 0;
|
|
|
|
if ((!my_uid && (tempstat.st_mode & 0111)) ||
|
|
|
|
(my_uid == tempstat.st_uid && (tempstat.st_mode & 0100)) ||
|
|
|
|
(my_gid == tempstat.st_gid && (tempstat.st_mode & 0010)) ||
|
|
|
|
(tempstat.st_mode & 0001))
|
|
|
|
isexec = 1;
|
|
|
|
}
|
|
|
|
}
|
2009-02-07 17:04:36 +03:00
|
|
|
else
|
|
|
|
{
|
2009-04-24 02:47:22 +04:00
|
|
|
/* stat failed, strange. not a dir in any case */
|
2009-02-07 17:04:36 +03:00
|
|
|
isdir = 0;
|
|
|
|
}
|
2009-05-27 09:18:44 +04:00
|
|
|
g_free (tmp);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2009-01-31 22:46:32 +03:00
|
|
|
if ((flags & INPUT_COMPLETE_COMMANDS)
|
|
|
|
&& (isexec || isdir))
|
|
|
|
break;
|
|
|
|
if ((flags & INPUT_COMPLETE_CD)
|
|
|
|
&& isdir)
|
|
|
|
break;
|
|
|
|
if (flags & (INPUT_COMPLETE_FILENAMES))
|
|
|
|
break;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!entry){
|
|
|
|
if (directory){
|
1998-06-01 01:35:38 +04:00
|
|
|
mc_closedir (directory);
|
1998-02-27 07:54:42 +03:00
|
|
|
directory = NULL;
|
|
|
|
}
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (dirname);
|
2004-09-04 02:00:27 +04:00
|
|
|
dirname = NULL;
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (filename);
|
2004-09-04 02:00:27 +04:00
|
|
|
filename = NULL;
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (users_dirname);
|
2004-09-04 02:00:27 +04:00
|
|
|
users_dirname = NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
char *temp;
|
|
|
|
|
|
|
|
if (users_dirname && (users_dirname[0] != '.' || users_dirname[1])){
|
2009-05-27 09:18:44 +04:00
|
|
|
size_t dirlen = strlen (users_dirname);
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
temp = g_malloc (3 + dirlen + NLENGTH (entry));
|
1998-02-27 07:54:42 +03:00
|
|
|
strcpy (temp, users_dirname);
|
|
|
|
/* We need a `/' at the end. */
|
|
|
|
if (users_dirname[dirlen - 1] != PATH_SEP){
|
|
|
|
temp[dirlen] = PATH_SEP;
|
|
|
|
temp[dirlen + 1] = 0;
|
|
|
|
}
|
|
|
|
strcat (temp, entry->d_name);
|
|
|
|
} else {
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
temp = g_malloc (2 + NLENGTH (entry));
|
1998-02-27 07:54:42 +03:00
|
|
|
strcpy (temp, entry->d_name);
|
|
|
|
}
|
|
|
|
if (isdir)
|
|
|
|
strcat (temp, PATH_SEP_STR);
|
2009-01-31 22:46:32 +03:00
|
|
|
|
|
|
|
return temp;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We assume here that text[0] == '~' , if you want to call it in another way,
|
|
|
|
you have to change the code */
|
1998-12-03 00:27:27 +03:00
|
|
|
static char *
|
2009-07-15 16:54:18 +04:00
|
|
|
username_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
static struct passwd *entry;
|
2009-05-27 09:18:44 +04:00
|
|
|
static size_t userlen;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-04-24 02:47:22 +04:00
|
|
|
(void) flags;
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("username_completion_function");
|
|
|
|
|
2009-05-27 09:18:44 +04:00
|
|
|
if (text[0] == '\\' && text[1] == '~')
|
|
|
|
text++;
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!state){ /* Initialization stuff */
|
|
|
|
setpwent ();
|
|
|
|
userlen = strlen (text + 1);
|
|
|
|
}
|
|
|
|
while ((entry = getpwent ()) != NULL){
|
|
|
|
/* Null usernames should result in all users as possible completions. */
|
2009-05-27 09:18:44 +04:00
|
|
|
if (userlen == 0)
|
1998-02-27 07:54:42 +03:00
|
|
|
break;
|
2009-05-27 09:18:44 +04:00
|
|
|
if (text[1] == entry->pw_name[0]
|
|
|
|
&& !strncmp (text + 1, entry->pw_name, userlen))
|
1998-02-27 07:54:42 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-05-27 09:18:44 +04:00
|
|
|
if (entry)
|
|
|
|
return g_strconcat ("~", entry->pw_name, PATH_SEP_STR, (char *) NULL);
|
|
|
|
|
|
|
|
endpwent ();
|
|
|
|
return NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2004-09-19 21:35:05 +04:00
|
|
|
/* Linux declares environ in <unistd.h>, so don't repeat it here. */
|
2004-10-23 11:12:39 +04:00
|
|
|
#if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
|
1998-02-27 07:54:42 +03:00
|
|
|
extern char **environ;
|
2004-09-19 21:35:05 +04:00
|
|
|
#endif
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* We assume text [0] == '$' and want to have a look at text [1], if it is
|
|
|
|
equal to '{', so that we should append '}' at the end */
|
1998-12-03 00:27:27 +03:00
|
|
|
static char *
|
2009-07-15 16:54:18 +04:00
|
|
|
variable_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
static char **env_p;
|
|
|
|
static int varlen, isbrace;
|
2004-09-25 18:34:27 +04:00
|
|
|
const char *p = NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-04-24 02:47:22 +04:00
|
|
|
(void) flags;
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("variable_completion_function");
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!state){ /* Initialization stuff */
|
|
|
|
isbrace = (text [1] == '{');
|
|
|
|
varlen = strlen (text + 1 + isbrace);
|
|
|
|
env_p = environ;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*env_p){
|
|
|
|
p = strchr (*env_p, '=');
|
|
|
|
if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
|
|
|
|
break;
|
|
|
|
env_p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*env_p)
|
|
|
|
return NULL;
|
|
|
|
else {
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
*temp = '$';
|
|
|
|
if (isbrace)
|
|
|
|
temp [1] = '{';
|
2004-09-04 02:00:27 +04:00
|
|
|
memcpy (temp + 1 + isbrace, *env_p, p - *env_p);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (isbrace)
|
|
|
|
strcpy (temp + 2 + (p - *env_p), "}");
|
|
|
|
else
|
|
|
|
temp [1 + p - *env_p] = 0;
|
|
|
|
env_p++;
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define whitespace(c) ((c) == ' ' || (c) == '\t')
|
|
|
|
#define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
|
|
|
|
|
|
|
|
static char **hosts = NULL;
|
|
|
|
static char **hosts_p = NULL;
|
|
|
|
static int hosts_alloclen = 0;
|
2004-08-30 02:38:06 +04:00
|
|
|
static void fetch_hosts (const char *filename)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
FILE *file = fopen (filename, "r");
|
2005-08-16 02:30:36 +04:00
|
|
|
char buffer[256], *name;
|
2008-12-29 01:52:45 +03:00
|
|
|
char *start;
|
|
|
|
char *bi;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (!file)
|
|
|
|
return;
|
|
|
|
|
2005-08-16 02:30:36 +04:00
|
|
|
while (fgets (buffer, 255, file) != NULL){
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Skip to first character. */
|
2008-12-29 01:52:45 +03:00
|
|
|
for (bi = buffer;
|
|
|
|
bi[0] != '\0' && str_isspace (bi);
|
|
|
|
str_next_char (&bi));
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Ignore comments... */
|
2008-12-29 01:52:45 +03:00
|
|
|
if (bi[0] == '#')
|
1998-02-27 07:54:42 +03:00
|
|
|
continue;
|
|
|
|
/* Handle $include. */
|
2008-12-29 01:52:45 +03:00
|
|
|
if (!strncmp (bi, "$include ", 9)){
|
|
|
|
char *includefile = bi + 9;
|
1998-02-27 07:54:42 +03:00
|
|
|
char *t;
|
|
|
|
|
|
|
|
/* Find start of filename. */
|
|
|
|
while (*includefile && whitespace (*includefile))
|
|
|
|
includefile++;
|
|
|
|
t = includefile;
|
|
|
|
|
|
|
|
/* Find end of filename. */
|
2008-12-29 01:52:45 +03:00
|
|
|
while (t[0] != '\0' && !str_isspace (t))
|
|
|
|
str_next_char (&t);
|
1998-02-27 07:54:42 +03:00
|
|
|
*t = '\0';
|
|
|
|
|
|
|
|
fetch_hosts (includefile);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip IP #s. */
|
2008-12-29 01:52:45 +03:00
|
|
|
while (bi[0] != '\0' && !str_isspace (bi))
|
|
|
|
str_next_char (&bi);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* Get the host names separated by white space. */
|
2008-12-29 01:52:45 +03:00
|
|
|
while (bi[0] != '\0' && bi[0] != '#'){
|
|
|
|
while (bi[0] != '\0' && str_isspace (bi))
|
|
|
|
str_next_char (&bi);
|
|
|
|
if (bi[0] == '#')
|
2001-06-25 17:59:04 +04:00
|
|
|
continue;
|
2008-12-29 01:52:45 +03:00
|
|
|
for (start = bi;
|
|
|
|
bi[0] != '\0' && !str_isspace (bi);
|
|
|
|
str_next_char (&bi));
|
|
|
|
|
|
|
|
if (bi - start == 0) continue;
|
|
|
|
|
|
|
|
name = g_strndup (start, bi - start);
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
char **host_p;
|
|
|
|
|
|
|
|
if (hosts_p - hosts >= hosts_alloclen){
|
|
|
|
int j = hosts_p - hosts;
|
|
|
|
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
hosts = g_realloc ((void *)hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
|
1998-02-27 07:54:42 +03:00
|
|
|
hosts_p = hosts + j;
|
|
|
|
}
|
|
|
|
for (host_p = hosts; host_p < hosts_p; host_p++)
|
|
|
|
if (!strcmp (name, *host_p))
|
|
|
|
break; /* We do not want any duplicates */
|
|
|
|
if (host_p == hosts_p){
|
|
|
|
*(hosts_p++) = name;
|
|
|
|
*hosts_p = NULL;
|
|
|
|
} else
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (name);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose (file);
|
|
|
|
}
|
|
|
|
|
1998-12-03 00:27:27 +03:00
|
|
|
static char *
|
2009-07-15 16:54:18 +04:00
|
|
|
hostname_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
static char **host_p;
|
|
|
|
static int textstart, textlen;
|
|
|
|
|
2009-04-24 02:47:22 +04:00
|
|
|
(void) flags;
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("hostname_completion_function");
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!state){ /* Initialization stuff */
|
2004-08-30 02:38:06 +04:00
|
|
|
const char *p;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (hosts != NULL){
|
|
|
|
for (host_p = hosts; *host_p; host_p++)
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (*host_p);
|
|
|
|
g_free (hosts);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
hosts = g_new (char *, (hosts_alloclen = 30) + 1);
|
1998-02-27 07:54:42 +03:00
|
|
|
*hosts = NULL;
|
|
|
|
hosts_p = hosts;
|
|
|
|
fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
|
|
|
|
host_p = hosts;
|
|
|
|
textstart = (*text == '@') ? 1 : 0;
|
|
|
|
textlen = strlen (text + textstart);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*host_p){
|
|
|
|
if (!textlen)
|
|
|
|
break; /* Match all of them */
|
|
|
|
else if (!strncmp (text + textstart, *host_p, textlen))
|
|
|
|
break;
|
|
|
|
host_p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*host_p){
|
|
|
|
for (host_p = hosts; *host_p; host_p++)
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (*host_p);
|
|
|
|
g_free (hosts);
|
1998-02-27 07:54:42 +03:00
|
|
|
hosts = NULL;
|
|
|
|
return NULL;
|
|
|
|
} else {
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
char *temp = g_malloc (2 + strlen (*host_p));
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (textstart)
|
|
|
|
*temp = '@';
|
|
|
|
strcpy (temp + textstart, *host_p);
|
|
|
|
host_p++;
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-12-03 00:27:27 +03:00
|
|
|
/*
|
|
|
|
* This is the function to call when the word to complete is in a position
|
|
|
|
* where a command word can be found. It looks around $PATH, looking for
|
|
|
|
* commands that match. It also scans aliases, function names, and the
|
|
|
|
* table of shell built-ins.
|
|
|
|
*/
|
|
|
|
static char *
|
2009-05-27 01:14:08 +04:00
|
|
|
command_completion_function (const char *_text, int state, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2009-05-27 01:14:08 +04:00
|
|
|
char *text;
|
2004-09-25 18:34:27 +04:00
|
|
|
static const char *path_end;
|
2009-05-27 09:18:44 +04:00
|
|
|
static gboolean isabsolute;
|
1998-02-27 07:54:42 +03:00
|
|
|
static int phase;
|
|
|
|
static int text_len;
|
2004-01-24 02:53:37 +03:00
|
|
|
static const char *const *words;
|
1998-02-27 07:54:42 +03:00
|
|
|
static char *path;
|
|
|
|
static char *cur_path;
|
|
|
|
static char *cur_word;
|
|
|
|
static int init_state;
|
2004-01-24 02:53:37 +03:00
|
|
|
static const char *const bash_reserved[] = {
|
|
|
|
"if", "then", "else", "elif", "fi", "case", "esac", "for",
|
|
|
|
"select", "while", "until", "do", "done", "in", "function", 0
|
2001-06-25 17:59:04 +04:00
|
|
|
};
|
2004-01-24 02:53:37 +03:00
|
|
|
static const char *const bash_builtins[] = {
|
|
|
|
"alias", "bg", "bind", "break", "builtin", "cd", "command",
|
|
|
|
"continue", "declare", "dirs", "echo", "enable", "eval",
|
|
|
|
"exec", "exit", "export", "fc", "fg", "getopts", "hash",
|
|
|
|
"help", "history", "jobs", "kill", "let", "local", "logout",
|
|
|
|
"popd", "pushd", "pwd", "read", "readonly", "return", "set",
|
|
|
|
"shift", "source", "suspend", "test", "times", "trap", "type",
|
|
|
|
"typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
|
2001-06-25 17:59:04 +04:00
|
|
|
};
|
2004-09-26 01:08:29 +04:00
|
|
|
char *p, *found;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("command_completion_function");
|
|
|
|
|
|
|
|
if (!(flags & INPUT_COMPLETE_COMMANDS))
|
|
|
|
return 0;
|
2009-06-09 13:12:17 +04:00
|
|
|
text = strutils_shell_unescape(_text);
|
2009-02-01 14:28:31 +03:00
|
|
|
flags &= ~INPUT_COMPLETE_SHELL_ESC;
|
2009-01-31 22:46:32 +03:00
|
|
|
|
2004-01-24 02:53:37 +03:00
|
|
|
if (!state) { /* Initialize us a little bit */
|
2009-05-27 09:18:44 +04:00
|
|
|
isabsolute = strchr (text, PATH_SEP) != NULL;
|
2004-01-24 02:53:37 +03:00
|
|
|
if (!isabsolute) {
|
1998-02-27 07:54:42 +03:00
|
|
|
words = bash_reserved;
|
|
|
|
phase = 0;
|
|
|
|
text_len = strlen (text);
|
2009-02-06 01:40:32 +03:00
|
|
|
if (!path && (path = g_strdup (getenv ("PATH"))) != NULL) {
|
2004-09-26 01:08:29 +04:00
|
|
|
p = path;
|
2001-07-31 19:21:28 +04:00
|
|
|
path_end = strchr (p, 0);
|
2004-09-26 01:08:29 +04:00
|
|
|
while ((p = strchr (p, PATH_ENV_SEP))) {
|
2001-07-31 19:21:28 +04:00
|
|
|
*p++ = 0;
|
2004-01-24 02:53:37 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-01-24 02:53:37 +03:00
|
|
|
|
|
|
|
if (isabsolute) {
|
2009-01-31 22:46:32 +03:00
|
|
|
p = filename_completion_function (text, state, flags);
|
2009-05-27 09:18:44 +04:00
|
|
|
|
|
|
|
if (p) {
|
|
|
|
char *temp_p = p;
|
2009-06-09 13:12:17 +04:00
|
|
|
p = strutils_shell_escape (p);
|
2009-05-27 09:18:44 +04:00
|
|
|
g_free (temp_p);
|
2009-05-27 01:14:08 +04:00
|
|
|
}
|
2009-05-27 09:18:44 +04:00
|
|
|
|
|
|
|
g_free (text);
|
2009-02-06 02:09:35 +03:00
|
|
|
return p;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2004-01-24 02:53:37 +03:00
|
|
|
found = NULL;
|
|
|
|
switch (phase) {
|
|
|
|
case 0: /* Reserved words */
|
|
|
|
while (*words) {
|
|
|
|
if (!strncmp (*words, text, text_len))
|
2009-02-06 01:40:32 +03:00
|
|
|
return g_strdup (*(words++));
|
2004-01-24 02:53:37 +03:00
|
|
|
words++;
|
|
|
|
}
|
|
|
|
phase++;
|
|
|
|
words = bash_builtins;
|
|
|
|
case 1: /* Builtin commands */
|
|
|
|
while (*words) {
|
|
|
|
if (!strncmp (*words, text, text_len))
|
2009-02-06 01:40:32 +03:00
|
|
|
return g_strdup (*(words++));
|
2004-01-24 02:53:37 +03:00
|
|
|
words++;
|
|
|
|
}
|
|
|
|
phase++;
|
|
|
|
if (!path)
|
|
|
|
break;
|
|
|
|
cur_path = path;
|
|
|
|
cur_word = NULL;
|
|
|
|
case 2: /* And looking through the $PATH */
|
|
|
|
while (!found) {
|
|
|
|
if (!cur_word) {
|
|
|
|
char *expanded;
|
|
|
|
|
|
|
|
if (cur_path >= path_end)
|
|
|
|
break;
|
|
|
|
expanded = tilde_expand (*cur_path ? cur_path : ".");
|
2009-02-06 13:17:03 +03:00
|
|
|
cur_word = concat_dir_and_file (expanded, text);
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (expanded);
|
2004-01-24 02:53:37 +03:00
|
|
|
canonicalize_pathname (cur_word);
|
2004-09-26 01:08:29 +04:00
|
|
|
cur_path = strchr (cur_path, 0) + 1;
|
2004-01-24 02:53:37 +03:00
|
|
|
init_state = state;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2004-01-24 02:53:37 +03:00
|
|
|
found =
|
|
|
|
filename_completion_function (cur_word,
|
2009-01-31 22:46:32 +03:00
|
|
|
state - init_state, flags);
|
2004-01-24 02:53:37 +03:00
|
|
|
if (!found) {
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (cur_word);
|
2004-01-24 02:53:37 +03:00
|
|
|
cur_word = NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2004-01-24 02:53:37 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2004-01-24 02:53:37 +03:00
|
|
|
|
2009-05-27 09:18:44 +04:00
|
|
|
if (found == NULL) {
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (path);
|
2004-09-04 02:00:27 +04:00
|
|
|
path = NULL;
|
2009-05-27 09:18:44 +04:00
|
|
|
} else if ((p = strrchr (found, PATH_SEP)) != NULL) {
|
|
|
|
char *tmp = found;
|
2009-06-09 13:12:17 +04:00
|
|
|
found = strutils_shell_escape (p + 1);
|
2009-05-27 09:18:44 +04:00
|
|
|
g_free (tmp);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2009-05-27 09:18:44 +04:00
|
|
|
|
2009-05-27 01:14:08 +04:00
|
|
|
g_free(text);
|
1998-02-27 07:54:42 +03:00
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
1998-12-03 00:27:27 +03:00
|
|
|
static int
|
|
|
|
match_compare (const void *a, const void *b)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
return strcmp (*(char **)a, *(char **)b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns an array of char * matches with the longest common denominator
|
|
|
|
in the 1st entry. Then a NULL terminated list of different possible
|
|
|
|
completions follows.
|
|
|
|
You have to supply your own CompletionFunction with the word you
|
|
|
|
want to complete as the first argument and an count of previous matches
|
|
|
|
as the second.
|
|
|
|
In case no matches were found we return NULL. */
|
1998-12-03 00:27:27 +03:00
|
|
|
static char **
|
2009-07-15 16:54:18 +04:00
|
|
|
completion_matches (const char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
/* Number of slots in match_list. */
|
|
|
|
int match_list_size;
|
|
|
|
|
|
|
|
/* The list of matches. */
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
char **match_list = g_new (char *, (match_list_size = 30) + 1);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* Number of matches actually found. */
|
|
|
|
int matches = 0;
|
|
|
|
|
|
|
|
/* Temporary string binder. */
|
|
|
|
char *string;
|
|
|
|
|
|
|
|
match_list[1] = NULL;
|
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
while ((string = (*entry_function) (text, matches, flags)) != NULL){
|
1998-02-27 07:54:42 +03:00
|
|
|
if (matches + 1 == match_list_size)
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
|
1998-02-27 07:54:42 +03:00
|
|
|
match_list[++matches] = string;
|
|
|
|
match_list[matches + 1] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there were any matches, then look through them finding out the
|
|
|
|
lowest common denominator. That then becomes match_list[0]. */
|
|
|
|
if (matches)
|
|
|
|
{
|
|
|
|
register int i = 1;
|
|
|
|
int low = 4096; /* Count of max-matched characters. */
|
|
|
|
|
|
|
|
/* If only one match, just use that. */
|
|
|
|
if (matches == 1){
|
|
|
|
match_list[0] = match_list[1];
|
2004-08-16 21:45:59 +04:00
|
|
|
match_list[1] = NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
} else {
|
|
|
|
int j;
|
|
|
|
|
|
|
|
qsort (match_list + 1, matches, sizeof (char *), match_compare);
|
|
|
|
|
|
|
|
/* And compare each member of the list with
|
|
|
|
the next, finding out where they stop matching.
|
|
|
|
If we find two equal strings, we have to put one away... */
|
|
|
|
|
|
|
|
j = i + 1;
|
|
|
|
while (j < matches + 1)
|
|
|
|
{
|
2008-12-29 01:52:45 +03:00
|
|
|
char *si, *sj;
|
|
|
|
char *ni, *nj;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
for (si = match_list[i], sj = match_list[j];
|
|
|
|
si[0] && sj[0];) {
|
|
|
|
|
|
|
|
ni = str_get_next_char (si);
|
|
|
|
nj = str_get_next_char (sj);
|
|
|
|
|
|
|
|
if (ni - si != nj - sj) break;
|
|
|
|
if (strncmp (si, sj, ni - si) != 0) break;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
si = ni;
|
|
|
|
sj = nj;
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
if (si[0] == '\0' && sj[0] == '\0'){ /* Two equal strings */
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (match_list [j]);
|
1998-02-27 07:54:42 +03:00
|
|
|
j++;
|
|
|
|
if (j > matches)
|
|
|
|
break;
|
2004-10-28 23:39:46 +04:00
|
|
|
continue; /* Look for a run of equal strings */
|
1998-02-27 07:54:42 +03:00
|
|
|
} else
|
2008-12-29 01:52:45 +03:00
|
|
|
if (low > si - match_list[i]) low = si - match_list[i];
|
1998-02-27 07:54:42 +03:00
|
|
|
if (i + 1 != j) /* So there's some gap */
|
|
|
|
match_list [i + 1] = match_list [j];
|
|
|
|
i++; j++;
|
|
|
|
}
|
|
|
|
matches = i;
|
|
|
|
match_list [matches + 1] = NULL;
|
2004-09-02 03:25:21 +04:00
|
|
|
match_list[0] = g_strndup(match_list[1], low);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
} else { /* There were no matches. */
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (match_list);
|
1998-02-27 07:54:42 +03:00
|
|
|
match_list = NULL;
|
|
|
|
}
|
|
|
|
return match_list;
|
|
|
|
}
|
|
|
|
|
2003-11-25 00:22:00 +03:00
|
|
|
/* Check if directory completion is needed */
|
1998-12-03 00:27:27 +03:00
|
|
|
static int
|
2009-01-29 22:13:30 +03:00
|
|
|
check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2008-12-29 01:52:45 +03:00
|
|
|
char *p, *q;
|
|
|
|
int test = 0;
|
2003-11-25 00:22:00 +03:00
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("check_is_cd");
|
|
|
|
if (!(flags & INPUT_COMPLETE_CD))
|
2003-11-25 00:22:00 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Skip initial spaces */
|
2008-12-29 01:52:45 +03:00
|
|
|
p = (char*)text;
|
|
|
|
q = (char*)text + start;
|
|
|
|
while (p < q && p[0] != '\0' && str_isspace (p))
|
|
|
|
str_next_char (&p);
|
2003-11-25 00:22:00 +03:00
|
|
|
|
|
|
|
/* Check if the command is "cd" and the cursor is after it */
|
2008-12-29 01:52:45 +03:00
|
|
|
text+= p[0] == 'c';
|
|
|
|
str_next_char (&p);
|
|
|
|
text+= p[0] == 'd';
|
|
|
|
str_next_char (&p);
|
|
|
|
text+= str_isspace (p);
|
|
|
|
if (test == 3 && (p < q))
|
2003-11-25 00:22:00 +03:00
|
|
|
return 1;
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns an array of matches, or NULL if none. */
|
1998-12-03 00:27:27 +03:00
|
|
|
static char **
|
2009-01-31 22:46:32 +03:00
|
|
|
try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2009-01-29 21:17:43 +03:00
|
|
|
int in_command_position = 0;
|
2008-12-29 01:52:45 +03:00
|
|
|
char *word;
|
1998-02-27 07:54:42 +03:00
|
|
|
char **matches = NULL;
|
2004-08-30 02:38:06 +04:00
|
|
|
const char *command_separator_chars = ";|&{(`";
|
1998-02-27 07:54:42 +03:00
|
|
|
char *p = NULL, *q = NULL, *r = NULL;
|
|
|
|
int is_cd = check_is_cd (text, *start, flags);
|
2008-12-29 01:52:45 +03:00
|
|
|
char *ti;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("try_complete");
|
2008-12-29 01:52:45 +03:00
|
|
|
word = g_strndup (text + *start, *end - *start);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* Determine if this could be a command word. It is if it appears at
|
|
|
|
the start of the line (ignoring preceding whitespace), or if it
|
|
|
|
appears after a character that separates commands. And we have to
|
|
|
|
be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
|
|
|
|
if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
|
2008-12-29 01:52:45 +03:00
|
|
|
ti = str_get_prev_char (&text[*start]);
|
|
|
|
while (ti > text && (ti[0] == ' ' || ti[0] == '\t'))
|
|
|
|
str_prev_char (&ti);
|
|
|
|
if (ti <= text&& (ti[0] == ' ' || ti[0] == '\t'))
|
2009-04-04 23:50:46 +04:00
|
|
|
in_command_position++;
|
2008-12-29 01:52:45 +03:00
|
|
|
else if (strchr (command_separator_chars, ti[0])){
|
1998-02-27 07:54:42 +03:00
|
|
|
register int this_char, prev_char;
|
|
|
|
|
|
|
|
in_command_position++;
|
2009-04-04 23:50:46 +04:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
if (ti > text){
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Handle the two character tokens `>&', `<&', and `>|'.
|
|
|
|
We are not in a command position after one of these. */
|
2008-12-29 01:52:45 +03:00
|
|
|
this_char = ti[0];
|
|
|
|
prev_char = str_get_prev_char (ti)[0];
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
|
|
|
|
(this_char == '|' && prev_char == '>'))
|
|
|
|
in_command_position = 0;
|
2009-04-04 23:50:46 +04:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
else if (ti > text && str_get_prev_char (ti)[0] == '\\') /* Quoted */
|
1998-02-27 07:54:42 +03:00
|
|
|
in_command_position = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & INPUT_COMPLETE_COMMANDS)
|
2004-09-26 01:08:29 +04:00
|
|
|
p = strrchr (word, '`');
|
1998-02-27 07:54:42 +03:00
|
|
|
if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
|
2004-09-26 01:08:29 +04:00
|
|
|
q = strrchr (word, '$');
|
1998-02-27 07:54:42 +03:00
|
|
|
if (flags & INPUT_COMPLETE_HOSTNAMES)
|
2004-09-26 01:08:29 +04:00
|
|
|
r = strrchr (word, '@');
|
1998-02-27 07:54:42 +03:00
|
|
|
if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
|
2009-04-04 23:50:46 +04:00
|
|
|
if (q > p)
|
|
|
|
p = str_get_next_char (q);
|
|
|
|
q = NULL;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2009-01-29 21:17:43 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Command substitution? */
|
|
|
|
if (p > q && p > r){
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("try_complete:cmd_backq_subst");
|
2009-07-15 16:54:18 +04:00
|
|
|
matches = completion_matches (str_cget_next_char (p),
|
2009-04-04 23:50:46 +04:00
|
|
|
command_completion_function,
|
|
|
|
flags & (~INPUT_COMPLETE_FILENAMES));
|
1998-02-27 07:54:42 +03:00
|
|
|
if (matches)
|
2008-12-29 01:52:45 +03:00
|
|
|
*start += str_get_next_char (p) - word;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Variable name? */
|
|
|
|
else if (q > p && q > r){
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("try_complete:var_subst");
|
|
|
|
matches = completion_matches (q, variable_completion_function, flags);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (matches)
|
|
|
|
*start += q - word;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Starts with '@', then look through the known hostnames for
|
|
|
|
completion first. */
|
|
|
|
else if (r > p && r > q){
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("try_complete:host_subst");
|
|
|
|
matches = completion_matches (r, hostname_completion_function, flags);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (matches)
|
|
|
|
*start += r - word;
|
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Starts with `~' and there is no slash in the word, then
|
|
|
|
try completing this word as a username. */
|
|
|
|
if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
|
2009-01-31 22:46:32 +03:00
|
|
|
{
|
|
|
|
SHOW_C_CTX("try_complete:user_subst");
|
|
|
|
matches = completion_matches (word, username_completion_function, flags);
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
|
|
|
|
/* And finally if this word is in a command position, then
|
|
|
|
complete over possible command names, including aliases, functions,
|
|
|
|
and command names. */
|
|
|
|
if (!matches && in_command_position)
|
2009-01-31 22:46:32 +03:00
|
|
|
{
|
|
|
|
SHOW_C_CTX("try_complete:cmd_subst");
|
|
|
|
matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
|
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
|
2009-01-31 22:46:32 +03:00
|
|
|
if (is_cd)
|
|
|
|
flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
|
|
|
|
SHOW_C_CTX("try_complete:filename_subst_1");
|
|
|
|
matches = completion_matches (word, filename_completion_function, flags);
|
2009-04-04 23:50:46 +04:00
|
|
|
if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
|
|
|
|
char *p, *q = text + *start;
|
|
|
|
for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p));
|
|
|
|
if (!strncmp (p, "cd", 2))
|
|
|
|
for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p));
|
|
|
|
if (p == q){
|
2009-02-06 01:40:32 +03:00
|
|
|
char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
|
2004-09-25 18:34:27 +04:00
|
|
|
char *cdpath = cdpath_ref;
|
1998-02-27 07:54:42 +03:00
|
|
|
char c, *s, *r;
|
|
|
|
|
|
|
|
if (cdpath == NULL)
|
|
|
|
c = 0;
|
|
|
|
else
|
|
|
|
c = ':';
|
|
|
|
while (!matches && c == ':'){
|
2004-09-26 01:08:29 +04:00
|
|
|
s = strchr (cdpath, ':');
|
1998-02-27 07:54:42 +03:00
|
|
|
if (s == NULL)
|
2004-09-26 01:08:29 +04:00
|
|
|
s = strchr (cdpath, 0);
|
1998-02-27 07:54:42 +03:00
|
|
|
c = *s;
|
|
|
|
*s = 0;
|
|
|
|
if (*cdpath){
|
2009-02-06 13:17:03 +03:00
|
|
|
r = concat_dir_and_file (cdpath, word);
|
2009-01-31 22:46:32 +03:00
|
|
|
SHOW_C_CTX("try_complete:filename_subst_2");
|
|
|
|
matches = completion_matches (r, filename_completion_function, flags);
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (r);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
*s = c;
|
2008-12-29 01:52:45 +03:00
|
|
|
cdpath = str_get_next_char (s);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (cdpath_ref);
|
2009-04-04 23:50:46 +04:00
|
|
|
}
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2001-06-25 17:59:04 +04:00
|
|
|
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (word);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_completions (WInput *in)
|
|
|
|
{
|
|
|
|
char **p;
|
2009-04-04 23:50:46 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!in->completions)
|
|
|
|
return;
|
|
|
|
for (p=in->completions; *p; p++)
|
2009-02-06 01:27:37 +03:00
|
|
|
g_free (*p);
|
|
|
|
g_free (in->completions);
|
1998-02-27 07:54:42 +03:00
|
|
|
in->completions = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_height, query_width;
|
|
|
|
static WInput *input;
|
2001-05-15 02:17:06 +04:00
|
|
|
static int min_end;
|
|
|
|
static int start, end;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
static int insert_text (WInput *in, char *text, ssize_t size)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2008-12-29 01:52:45 +03:00
|
|
|
int buff_len = str_length (in->buffer);
|
|
|
|
|
|
|
|
size = min (size, (ssize_t) strlen (text)) + start - end;
|
|
|
|
if (strlen (in->buffer) + size >= (size_t) in->current_max_size){
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Expand the buffer */
|
2008-12-29 01:52:45 +03:00
|
|
|
char *narea = g_realloc (in->buffer, in->current_max_size
|
|
|
|
+ size + in->field_width);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (narea){
|
|
|
|
in->buffer = narea;
|
2008-12-29 01:52:45 +03:00
|
|
|
in->current_max_size += size + in->field_width;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
2008-12-29 01:52:45 +03:00
|
|
|
if (strlen (in->buffer)+1 < (size_t) in->current_max_size){
|
|
|
|
if (size > 0){
|
2002-03-25 10:28:25 +03:00
|
|
|
int i = strlen (&in->buffer [end]);
|
|
|
|
for (; i >= 0; i--)
|
2008-12-29 01:52:45 +03:00
|
|
|
in->buffer [end + size + i] = in->buffer [end + i];
|
|
|
|
} else if (size < 0){
|
|
|
|
char *p = in->buffer + end + size, *q = in->buffer + end;
|
1998-02-27 07:54:42 +03:00
|
|
|
while (*q)
|
|
|
|
*(p++) = *(q++);
|
|
|
|
*p = 0;
|
|
|
|
}
|
2008-12-29 01:52:45 +03:00
|
|
|
memcpy (in->buffer + start, text, size - start + end);
|
|
|
|
in->point+= str_length (in->buffer) - buff_len;
|
1998-04-09 08:58:24 +04:00
|
|
|
update_input (in, 1);
|
2008-12-29 01:52:45 +03:00
|
|
|
end+= size;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2008-12-29 01:52:45 +03:00
|
|
|
return size != 0;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2003-09-08 01:24:01 +04:00
|
|
|
static cb_ret_t
|
|
|
|
query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2008-12-29 01:52:45 +03:00
|
|
|
static char buff[MB_LEN_MAX] = "";
|
|
|
|
static int bl = 0;
|
|
|
|
|
2003-09-08 01:24:01 +04:00
|
|
|
switch (msg) {
|
2002-08-22 19:34:25 +04:00
|
|
|
case DLG_KEY:
|
2003-09-08 01:24:01 +04:00
|
|
|
switch (parm) {
|
2002-08-22 19:34:25 +04:00
|
|
|
case KEY_LEFT:
|
|
|
|
case KEY_RIGHT:
|
2008-12-29 01:52:45 +03:00
|
|
|
bl = 0;
|
2002-08-22 19:34:25 +04:00
|
|
|
h->ret_value = 0;
|
|
|
|
dlg_stop (h);
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
|
|
|
|
case KEY_BACKSPACE:
|
2008-12-29 01:52:45 +03:00
|
|
|
bl = 0;
|
2002-08-22 19:34:25 +04:00
|
|
|
if (end == min_end) {
|
|
|
|
h->ret_value = 0;
|
|
|
|
dlg_stop (h);
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
} else {
|
|
|
|
WLEntry *e, *e1;
|
|
|
|
|
2003-09-13 03:38:42 +04:00
|
|
|
e1 = e = ((WListbox *) (h->current))->list;
|
2002-08-22 19:34:25 +04:00
|
|
|
do {
|
2008-12-29 01:52:45 +03:00
|
|
|
if (!strncmp (input->buffer + start,
|
|
|
|
e1->text, end - start - 1)) {
|
|
|
|
|
|
|
|
listbox_select_entry ((WListbox *) (h->current), e1);
|
|
|
|
end = str_get_prev_char (&(input->buffer[end]))
|
|
|
|
- input->buffer;
|
2003-09-08 01:24:01 +04:00
|
|
|
handle_char (input, parm);
|
2003-09-13 03:38:42 +04:00
|
|
|
send_message (h->current, WIDGET_DRAW, 0);
|
2002-08-22 19:34:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
e1 = e1->next;
|
|
|
|
} while (e != e1);
|
|
|
|
}
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
|
|
|
|
default:
|
2008-12-29 01:52:45 +03:00
|
|
|
if (parm < 32 || parm > 256) {
|
|
|
|
bl = 0;
|
2003-09-08 01:24:01 +04:00
|
|
|
if (is_in_input_map (input, parm) == 2) {
|
2002-08-22 19:34:25 +04:00
|
|
|
if (end == min_end)
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
h->ret_value = B_USER; /* This means we want to refill the
|
|
|
|
list box and start again */
|
1998-03-14 03:42:23 +03:00
|
|
|
dlg_stop (h);
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
} else
|
2005-08-16 02:33:20 +04:00
|
|
|
return MSG_NOT_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
} else {
|
|
|
|
WLEntry *e, *e1;
|
|
|
|
int need_redraw = 0;
|
|
|
|
int low = 4096;
|
|
|
|
char *last_text = NULL;
|
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
buff[bl] = (char) parm;
|
|
|
|
bl++;
|
|
|
|
buff[bl] = '\0';
|
|
|
|
switch (str_is_valid_char (buff, bl)) {
|
|
|
|
case -1:
|
|
|
|
bl = 0;
|
|
|
|
case -2:
|
|
|
|
return MSG_HANDLED;
|
|
|
|
}
|
|
|
|
|
2003-09-13 03:38:42 +04:00
|
|
|
e1 = e = ((WListbox *) (h->current))->list;
|
2002-08-22 19:34:25 +04:00
|
|
|
do {
|
2008-12-29 01:52:45 +03:00
|
|
|
if (!strncmp (input->buffer + start,
|
|
|
|
e1->text, end - start)) {
|
|
|
|
|
|
|
|
if (strncmp (&e1->text[end - start], buff, bl) == 0) {
|
2002-08-22 19:34:25 +04:00
|
|
|
if (need_redraw) {
|
2008-12-29 01:52:45 +03:00
|
|
|
char *si, *sl;
|
|
|
|
char *ni, *nl;
|
|
|
|
si = &(e1->text[end - start]);
|
|
|
|
sl = &(last_text[end - start]);
|
|
|
|
|
|
|
|
for (; si[0] != '\0' && sl[0] != '\0';) {
|
|
|
|
|
|
|
|
ni = str_get_next_char (si);
|
|
|
|
nl = str_get_next_char (sl);
|
|
|
|
|
|
|
|
if (ni - si != nl - sl) break;
|
|
|
|
if (strncmp (si, sl, ni - si) != 0) break;
|
|
|
|
|
|
|
|
si = ni;
|
|
|
|
sl = nl;
|
|
|
|
}
|
|
|
|
|
2009-07-30 22:50:18 +04:00
|
|
|
if (low > si - &e1->text[end - start])
|
|
|
|
low = si - &e1->text[end - start];
|
2008-12-29 01:52:45 +03:00
|
|
|
|
2002-08-22 19:34:25 +04:00
|
|
|
last_text = e1->text;
|
|
|
|
need_redraw = 2;
|
|
|
|
} else {
|
|
|
|
need_redraw = 1;
|
|
|
|
listbox_select_entry ((WListbox *) (h->
|
2003-09-13 03:38:42 +04:00
|
|
|
current),
|
2002-08-22 19:34:25 +04:00
|
|
|
e1);
|
|
|
|
last_text = e1->text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e1 = e1->next;
|
|
|
|
} while (e != e1);
|
|
|
|
if (need_redraw == 2) {
|
|
|
|
insert_text (input, last_text, low);
|
2003-09-13 03:38:42 +04:00
|
|
|
send_message (h->current, WIDGET_DRAW, 0);
|
2002-08-22 19:34:25 +04:00
|
|
|
} else if (need_redraw == 1) {
|
|
|
|
h->ret_value = B_ENTER;
|
|
|
|
dlg_stop (h);
|
|
|
|
}
|
2008-12-29 01:52:45 +03:00
|
|
|
bl = 0;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2003-09-08 01:24:01 +04:00
|
|
|
return MSG_HANDLED;
|
2002-08-22 19:34:25 +04:00
|
|
|
}
|
|
|
|
break;
|
2003-09-08 01:24:01 +04:00
|
|
|
|
|
|
|
default:
|
|
|
|
return default_dlg_callback (h, msg, parm);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DO_INSERTION 1
|
|
|
|
#define DO_QUERY 2
|
|
|
|
/* Returns 1 if the user would like to see us again */
|
1998-12-03 00:27:27 +03:00
|
|
|
static int
|
|
|
|
complete_engine (WInput *in, int what_to_do)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2008-12-29 01:52:45 +03:00
|
|
|
int s;
|
2009-04-04 23:50:46 +04:00
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
if (in->completions && (str_offset_to_pos (in->buffer, in->point)) != end)
|
1998-02-27 07:54:42 +03:00
|
|
|
free_completions (in);
|
|
|
|
if (!in->completions){
|
2008-12-29 01:52:45 +03:00
|
|
|
end = str_offset_to_pos (in->buffer, in->point);
|
|
|
|
for (s = in->point ? in->point - 1 : 0; s >= 0; s--) {
|
|
|
|
start = str_offset_to_pos (in->buffer, s);
|
2009-04-04 23:50:46 +04:00
|
|
|
if (strchr (" \t;|<>", in->buffer [start])) {
|
2008-12-29 01:52:45 +03:00
|
|
|
if (start < end) start = str_offset_to_pos (in->buffer, s + 1);
|
2009-04-04 23:50:46 +04:00
|
|
|
/* FIXME: maybe need check '\\' prev char
|
|
|
|
if (start > 0 && in->buffer [start-1] == '\\')
|
|
|
|
*/
|
|
|
|
break;
|
2008-12-29 01:52:45 +03:00
|
|
|
}
|
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (in->completions){
|
2009-04-04 23:50:46 +04:00
|
|
|
if (what_to_do & DO_INSERTION || ((what_to_do & DO_QUERY) && !in->completions[1])) {
|
2009-01-30 00:38:59 +03:00
|
|
|
char * complete = in->completions [0];
|
|
|
|
if (insert_text (in, complete, strlen (complete))){
|
2009-04-04 23:50:46 +04:00
|
|
|
if (in->completions [1])
|
|
|
|
beep ();
|
1998-06-01 00:58:50 +04:00
|
|
|
else
|
|
|
|
free_completions (in);
|
1998-02-27 07:54:42 +03:00
|
|
|
} else
|
|
|
|
beep ();
|
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
if ((what_to_do & DO_QUERY) && in->completions && in->completions [1]) {
|
|
|
|
int maxlen = 0, i, count = 0;
|
|
|
|
int x, y, w, h;
|
|
|
|
int start_x, start_y;
|
|
|
|
char **p, *q;
|
|
|
|
Dlg_head *query_dlg;
|
|
|
|
WListbox *query_list;
|
|
|
|
|
|
|
|
for (p=in->completions + 1; *p; count++, p++)
|
|
|
|
if ((i = str_term_width1 (*p)) > maxlen)
|
|
|
|
maxlen = i;
|
|
|
|
start_x = in->widget.x;
|
|
|
|
start_y = in->widget.y;
|
|
|
|
if (start_y - 2 >= count) {
|
|
|
|
y = start_y - 2 - count;
|
|
|
|
h = 2 + count;
|
|
|
|
} else {
|
|
|
|
if (start_y >= LINES - start_y - 1) {
|
|
|
|
y = 0;
|
|
|
|
h = start_y;
|
|
|
|
} else {
|
|
|
|
y = start_y + 1;
|
|
|
|
h = LINES - start_y - 1;
|
|
|
|
}
|
2009-01-20 15:14:35 +03:00
|
|
|
}
|
2009-04-04 23:50:46 +04:00
|
|
|
x = start - in->term_first_shown - 2 + start_x;
|
|
|
|
w = maxlen + 4;
|
|
|
|
if (x + w > COLS)
|
|
|
|
x = COLS - w;
|
|
|
|
if (x < 0)
|
|
|
|
x = 0;
|
|
|
|
if (x + w > COLS)
|
|
|
|
w = COLS;
|
|
|
|
input = in;
|
|
|
|
min_end = end;
|
1998-02-27 07:54:42 +03:00
|
|
|
query_height = h;
|
|
|
|
query_width = w;
|
2009-04-04 23:50:46 +04:00
|
|
|
query_dlg = create_dlg (y, x, query_height, query_width,
|
1998-02-27 07:54:42 +03:00
|
|
|
dialog_colors, query_callback,
|
2003-09-13 02:08:09 +04:00
|
|
|
"[Completion]", NULL, DLG_COMPACT);
|
2009-04-15 20:22:26 +04:00
|
|
|
query_list = listbox_new (1, 1, h - 2, w - 2, NULL);
|
2009-04-04 23:50:46 +04:00
|
|
|
add_widget (query_dlg, query_list);
|
|
|
|
for (p = in->completions + 1; *p; p++)
|
|
|
|
listbox_add_item (query_list, 0, 0, *p, NULL);
|
|
|
|
run_dlg (query_dlg);
|
|
|
|
q = NULL;
|
|
|
|
if (query_dlg->ret_value == B_ENTER){
|
|
|
|
listbox_get_current (query_list, &q, NULL);
|
|
|
|
if (q)
|
|
|
|
insert_text (in, q, strlen (q));
|
|
|
|
}
|
|
|
|
if (q || end != min_end)
|
|
|
|
free_completions (in);
|
|
|
|
i = query_dlg->ret_value; /* B_USER if user wants to start over again */
|
|
|
|
destroy_dlg (query_dlg);
|
|
|
|
if (i == B_USER)
|
|
|
|
return 1;
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
} else
|
2009-04-04 23:50:46 +04:00
|
|
|
beep ();
|
1998-02-27 07:54:42 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void complete (WInput *in)
|
|
|
|
{
|
2005-07-06 15:16:08 +04:00
|
|
|
int engine_flags;
|
|
|
|
|
2008-12-29 01:52:45 +03:00
|
|
|
if (!str_is_valid_string (in->buffer)) return;
|
2009-04-04 23:50:46 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (in->completions)
|
2005-07-06 15:16:08 +04:00
|
|
|
engine_flags = DO_QUERY;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
engine_flags = DO_INSERTION;
|
|
|
|
|
|
|
|
if (show_all_if_ambiguous)
|
|
|
|
engine_flags |= DO_QUERY;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (complete_engine (in, engine_flags));
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|