Merge branch 'mc-4.6'

* mc-4.6: (38 commits)
  mhl: mhl_shell_unescape_buf(): fixed memory array OOB.
  completion: fixed complete already escaped secuences.
  completion: added changelog entry for solved #147
  completion: fixed completion of escaped commands in commandline
  Removed unused char*.
  mhl: added mhl_strmove() function (memmove semantics)
  completion: added escapes in command line on completion
  complete: cleanup: #define to enum INPUT_COMPLETION_FLAGS
  added a new parameter for completion flags to input_new
  fish: cleanup: unboxed quoted strings when generate shell commands
  introduced new type SHELL_ESCAPED_STR for more type safety
  added mhl/types.h which defines bool enum, escape.h now using this type
  Removed unused variable
  Changes for use MHL.
  Fixed bug with renamig/copying files with backshashes in names
  Remove some testing stuff
  Temporarry commit. Fixed completion in browse by directoryes.
  Fixed some memory leaks.
  Add $ and ` for escaping and reorder it according to the ascii values
  Rewrite it to use g_string_append_c instead of some homebrew stuff
  ...
This commit is contained in:
Sergei Trofimovich 2009-02-01 14:26:54 +02:00
commit df4129517b
20 changed files with 387 additions and 204 deletions

View File

@ -1,3 +1,11 @@
2009-01-31 Enrico Weigelt, metux ITS <weigelt@metux.de>, Patrick Winnertz <winnie@debian.org>, Slava Zanko <slavazanko@gmail.com>, Sergei Trofimovich <slyfox@inbox.ru>
* edit/editcmd.c, mhl/escape.h, mhl/string.h, mhl/types.h, src/Makefile.am,
* src/boxes.c, src/command.c, src/complete.c, src/complete.h, src/file.c,
* src/find.c, src/main.c, src/panelize.c, src/util.c, src/utilunix.c,
* src/widget.c, src/widget.h, src/wtools.c, vfs/fish.c:
fixed shell escaping issues in commandline completion engine
2009-01-31 Enrico Weigelt, metux ITS <weigelt@metux.de>
* replaced buggy concat_dir_and_file() by mhl_str_dir_plus_file() (in mhl/string.h)
@ -17,6 +25,11 @@
This solves "strange" rename cases, when copying/moving is performed into
deleted directory.
2009-01-27 Enrico Weigelt, metux ITS <weigelt@metux.de>
* mhl/escape.h, src/complete.c, vfs/fish.c: introduced new type
SHELL_ESCAPED_STR for more type safety
2009-01-27 Enrico Weigelt, metux IT service <weigelt@metux.de>
* mhl/escape.h, mhl/string.h: fixed comments to use /* ... */

View File

@ -626,7 +626,7 @@ edit_raw_key_query (const char *heading, const char *query, int cancel)
NULL, heading,
DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
add_widget (raw_dlg,
input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0, INPUT_COMPLETE_DEFAULT));
add_widget (raw_dlg, label_new (3 - cancel, 2, query));
if (cancel)
add_widget (raw_dlg,

View File

@ -6,6 +6,8 @@
#include <string.h>
#include <stdlib.h>
#include <mhl/types.h>
#define mhl_shell_escape_toesc(x) \
(((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \
((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \
@ -16,10 +18,21 @@
#define mhl_shell_escape_nottoesc(x) \
(((x)!=0) && (!mhl_shell_escape_toesc((x))))
static inline char* mhl_shell_escape_dup(const char* src)
/* type for escaped string - just for a bit more type safety ;-p */
typedef struct { char* s; } SHELL_ESCAPED_STR;
/** To be compatible with the general posix command lines we have to escape
strings for the command line
/params const char * in
string for escaping
/returns
return escaped string (later need to free)
*/
static inline SHELL_ESCAPED_STR mhl_shell_escape_dup(const char* src)
{
if ((src==NULL)||(!(*src)))
return strdup("");
return (SHELL_ESCAPED_STR){ .s = strdup("") };
char* buffer = calloc(1, strlen(src)*2+2);
char* ptr = buffer;
@ -38,7 +51,7 @@ static inline char* mhl_shell_escape_dup(const char* src)
/* at this point we either have an \0 or an char to escape */
if (!c)
return buffer;
return (SHELL_ESCAPED_STR){ .s = buffer };
*ptr = '\\';
ptr++;
@ -48,7 +61,14 @@ static inline char* mhl_shell_escape_dup(const char* src)
}
}
/* shell-unescape within a given buffer (writing to it!) */
/** Unescape paths or other strings for e.g the internal cd
shell-unescape within a given buffer (writing to it!)
/params const char * in
string for unescaping
/returns
return unescaped string
*/
static inline char* mhl_shell_unescape_buf(char* text)
{
if (!text)
@ -93,6 +113,8 @@ static inline char* mhl_shell_unescape_buf(char* text)
case '`':
case '"':
case ';':
case '\0': /* end of line! malformed escape string */
goto out;
default:
(*writeptr) = c; writeptr++; break;
}
@ -104,9 +126,28 @@ static inline char* mhl_shell_unescape_buf(char* text)
}
readptr++;
}
out:
*writeptr = 0;
return text;
}
/** Check if char in pointer contain escape'd chars
/params const char * in
string for checking
/returns
return TRUE if string contain escaped chars
otherwise return FALSE
*/
static inline bool
mhl_shell_is_char_escaped ( const char *in )
{
if (in == NULL || !*in || in[0] != '\\')
return false;
if (mhl_shell_escape_toesc(in[1]))
return true;
return false;
}
#endif

View File

@ -3,6 +3,7 @@
#include <ctype.h>
#include <stdarg.h>
#include <assert.h>
#include <mhl/memory.h>
#define mhl_str_dup(str) ((str ? strdup(str) : strdup("")))
@ -121,6 +122,19 @@ static inline char* mhl_str_reverse(char* ptr)
return ptr;
}
/*
* strcpy is unsafe on overlapping memory areas, so define memmove-alike
* string function. Has sense only when dest <= src.
*/
static inline char * mhl_strmove(char * dest, const char * src)
{
size_t n = strlen (src) + 1; /* + '\0' */
assert (dest<=src);
return memmove(dest, src, n);
}
static inline char* mhl_str_dir_plus_file(const char* dirname, const char* filename)
{
/* make sure we have valid strings */

16
mhl/types.h Normal file
View File

@ -0,0 +1,16 @@
/*
Micro Helper Library: generic type declarations
*/
#ifndef __MHL_TYPES_H
#define __MHL_TYPES_H
typedef enum
{
false = 0,
true = 1
} bool;
#endif

View File

@ -45,7 +45,7 @@ CHARSET_SRC = charsets.c charsets.h selcodepage.c selcodepage.h
SRCS = achown.c achown.h background.c background.h boxes.c boxes.h \
chmod.c chmod.h chown.c chown.h cmd.c cmd.h color.c color.h \
command.c command.h complete.c complete.h cons.handler.c \
command.c command.h complete.c cons.handler.c \
cons.saver.h dialog.c dialog.h dir.c dir.h \
eregex.h execute.c execute.h ext.c ext.h file.c filegui.c \
filegui.h file.h filenot.c fileopctx.c fileopctx.h find.c \

View File

@ -197,7 +197,7 @@ display_init (int radio_sel, char *init_text, int _check_status,
status =
input_new (10, 9, INPUT_COLOR, DISPLAY_X - 14, _status[radio_sel],
"mini-input");
"mini-input", INPUT_COMPLETE_DEFAULT);
add_widget (dd, status);
input_set_point (status, 0);
@ -207,7 +207,7 @@ display_init (int radio_sel, char *init_text, int _check_status,
user =
input_new (7, 9, INPUT_COLOR, DISPLAY_X - 14, init_text,
"user-fmt-input");
"user-fmt-input", INPUT_COMPLETE_DEFAULT);
add_widget (dd, user);
input_set_point (user, 0);
@ -1085,17 +1085,17 @@ vfs_smb_get_authinfo (const char *host, const char *share, const char *domain,
g_free (title);
in_user = input_new (5, istart, INPUT_COLOR, ilen, user, "auth_name");
in_user = input_new (5, istart, INPUT_COLOR, ilen, user, "auth_name", INPUT_COMPLETE_DEFAULT);
add_widget (auth_dlg, in_user);
in_domain = input_new (3, istart, INPUT_COLOR, ilen, domain, "auth_domain");
in_domain = input_new (3, istart, INPUT_COLOR, ilen, domain, "auth_domain", INPUT_COMPLETE_DEFAULT);
add_widget (auth_dlg, in_domain);
add_widget (auth_dlg, button_new (9, b2, B_CANCEL, NORMAL_BUTTON,
buts[1], 0));
add_widget (auth_dlg, button_new (9, b0, B_ENTER, DEFPUSH_BUTTON,
buts[0], 0));
in_password = input_new (7, istart, INPUT_COLOR, ilen, "", "auth_password");
in_password = input_new (7, istart, INPUT_COLOR, ilen, "", "auth_password", INPUT_COMPLETE_DEFAULT);
in_password->completion_flags = 0;
in_password->is_password = 1;
add_widget (auth_dlg, in_password);

View File

@ -27,13 +27,14 @@
#include <errno.h>
#include <string.h>
#include <mhl/memory.h>
#include <mhl/escape.h>
#include <mhl/string.h>
#include "global.h" /* home_dir */
#include "tty.h"
#include "widget.h" /* WInput */
#include "command.h"
#include "complete.h" /* completion constants */
#include "wtools.h" /* message () */
#include "panel.h" /* view_tree enum. Also, needed by main.h */
#include "main.h" /* do_cd */
@ -66,6 +67,7 @@ examine_cd (char *path)
const char *t;
/* Tilde expansion */
path = mhl_shell_unescape_buf(path);
path_tilde = tilde_expand (path);
/* Leave space for further expansion */
@ -137,6 +139,7 @@ examine_cd (char *path)
}
g_free (q);
g_free (path_tilde);
// mhl_mem_free(path);
return result;
}
@ -292,7 +295,8 @@ command_new (int y, int x, int cols)
{
WInput *cmd;
cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline");
cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline",
INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC);
/* Add our hooks */
cmd->widget.callback = command_callback;

View File

@ -30,6 +30,8 @@
#include <sys/stat.h>
#include <unistd.h>
#include <mhl/memory.h>
#include <mhl/escape.h>
#include <mhl/string.h>
#include "global.h"
@ -39,21 +41,38 @@
#include "dialog.h"
#include "widget.h"
#include "wtools.h"
#include "complete.h"
#include "main.h"
#include "util.h"
#include "key.h" /* XCTRL and ALT macros */
typedef char *CompletionFunction (char *, int);
typedef char *CompletionFunction (char * text, int state, INPUT_COMPLETE_FLAGS flags);
/* This flag is used in filename_completion_function */
static int ignore_filenames = 0;
//#define DO_COMPLETION_DEBUG
#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";
/* This flag is used by command_completion_function */
/* to hint the filename_completion_function */
static int look_for_executables = 0;
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 */
static char *
filename_completion_function (char *text, int state)
filename_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static DIR *directory;
static char *filename = NULL;
@ -64,6 +83,11 @@ filename_completion_function (char *text, int state)
struct dirent *entry = NULL;
SHOW_C_CTX("filename_completion_function");
if (text && (flags & INPUT_COMPLETE_SHELL_ESC))
text = mhl_shell_unescape_buf (text);
/* If we're starting the match process, initialize us a bit. */
if (!state){
const char *temp;
@ -85,6 +109,7 @@ filename_completion_function (char *text, int state)
/* Save the version of the directory that the user typed. */
users_dirname = dirname;
{
// FIXME: memleak ?
dirname = tilde_expand (dirname);
canonicalize_pathname (dirname);
/* Here we should do something with variable expansion
@ -136,18 +161,15 @@ filename_completion_function (char *text, int state)
}
g_free (tmp);
}
switch (look_for_executables)
{
case 2: if (!isexec)
continue;
break;
case 1: if (!isexec && !isdir)
continue;
break;
}
if (ignore_filenames && !isdir)
continue;
break;
if ((flags & INPUT_COMPLETE_COMMANDS)
&& (isexec || isdir))
break;
if ((flags & INPUT_COMPLETE_CD)
&& isdir)
break;
if (flags & (INPUT_COMPLETE_FILENAMES))
break;
continue;
}
if (!entry){
@ -181,18 +203,28 @@ filename_completion_function (char *text, int state)
}
if (isdir)
strcat (temp, PATH_SEP_STR);
return temp;
if (temp && (flags & INPUT_COMPLETE_SHELL_ESC))
{
SHELL_ESCAPED_STR e_temp = mhl_shell_escape_dup(temp);
mhl_mem_free (temp);
temp = e_temp.s;
}
return temp;
}
}
/* We assume here that text[0] == '~' , if you want to call it in another way,
you have to change the code */
static char *
username_completion_function (char *text, int state)
username_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static struct passwd *entry;
static int userlen;
SHOW_C_CTX("username_completion_function");
if (text[0] == '\\' && text[1] == '~') text++;
if (!state){ /* Initialization stuff */
setpwent ();
userlen = strlen (text + 1);
@ -227,12 +259,14 @@ extern char **environ;
/* 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 */
static char *
variable_completion_function (char *text, int state)
variable_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static char **env_p;
static int varlen, isbrace;
const char *p = NULL;
SHOW_C_CTX("variable_completion_function");
if (!state){ /* Initialization stuff */
isbrace = (text [1] == '{');
varlen = strlen (text + 1 + isbrace);
@ -342,11 +376,13 @@ static void fetch_hosts (const char *filename)
}
static char *
hostname_completion_function (char *text, int state)
hostname_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static char **host_p;
static int textstart, textlen;
SHOW_C_CTX("hostname_completion_function");
if (!state){ /* Initialization stuff */
const char *p;
@ -396,7 +432,7 @@ hostname_completion_function (char *text, int state)
* table of shell built-ins.
*/
static char *
command_completion_function (char *text, int state)
command_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static const char *path_end;
static int isabsolute;
@ -422,9 +458,16 @@ command_completion_function (char *text, int state)
};
char *p, *found;
SHOW_C_CTX("command_completion_function");
if (!(flags & INPUT_COMPLETE_COMMANDS))
return 0;
text = mhl_shell_unescape_buf(text);
flags &= ~INPUT_COMPLETE_SHELL_ESC;
if (!state) { /* Initialize us a little bit */
isabsolute = strchr (text, PATH_SEP) != 0;
look_for_executables = isabsolute ? 1 : 2;
if (!isabsolute) {
words = bash_reserved;
phase = 0;
@ -440,10 +483,12 @@ command_completion_function (char *text, int state)
}
if (isabsolute) {
p = filename_completion_function (text, state);
p = filename_completion_function (text, state, flags);
if (!p)
look_for_executables = 0;
return p;
return 0;
SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
mhl_mem_free(p);
return e_p.s;
}
found = NULL;
@ -483,7 +528,7 @@ command_completion_function (char *text, int state)
}
found =
filename_completion_function (cur_word,
state - init_state);
state - init_state, flags);
if (!found) {
g_free (cur_word);
cur_word = NULL;
@ -492,16 +537,16 @@ command_completion_function (char *text, int state)
}
if (!found) {
look_for_executables = 0;
g_free (path);
path = NULL;
return NULL;
}
if ((p = strrchr (found, PATH_SEP)) != NULL) {
p++;
p = g_strdup (p);
g_free (found);
return p;
SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
mhl_mem_free(found);
return e_p.s;
}
return found;
@ -521,7 +566,7 @@ match_compare (const void *a, const void *b)
as the second.
In case no matches were found we return NULL. */
static char **
completion_matches (char *text, CompletionFunction entry_function)
completion_matches (char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
{
/* Number of slots in match_list. */
int match_list_size;
@ -537,7 +582,7 @@ completion_matches (char *text, CompletionFunction entry_function)
match_list[1] = NULL;
while ((string = (*entry_function) (text, matches)) != NULL){
while ((string = (*entry_function) (text, matches, flags)) != NULL){
if (matches + 1 == match_list_size)
match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
match_list[++matches] = string;
@ -597,14 +642,12 @@ completion_matches (char *text, CompletionFunction entry_function)
/* Check if directory completion is needed */
static int
check_is_cd (const char *text, int start, int flags)
check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
{
const char *p, *q;
if (flags & INPUT_COMPLETE_CD)
return 1;
if (!(flags & INPUT_COMPLETE_COMMANDS))
SHOW_C_CTX("check_is_cd");
if (!(flags & INPUT_COMPLETE_CD))
return 0;
/* Skip initial spaces */
@ -623,16 +666,17 @@ check_is_cd (const char *text, int start, int flags)
/* Returns an array of matches, or NULL if none. */
static char **
try_complete (char *text, int *start, int *end, int flags)
try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
{
int in_command_position = 0, i;
int in_command_position = 0;
char *word, c;
char **matches = NULL;
const char *command_separator_chars = ";|&{(`";
char *p = NULL, *q = NULL, *r = NULL;
int is_cd = check_is_cd (text, *start, flags);
ignore_filenames = 0;
SHOW_C_CTX("try_complete");
c = text [*end];
text [*end] = 0;
word = g_strdup (text + *start);
@ -643,9 +687,16 @@ try_complete (char *text, int *start, int *end, int flags)
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)){
i = *start - 1;
while (i > -1 && (text[i] == ' ' || text[i] == '\t'))
i--;
int i = *start - 1;
for (i = *start - 1; i > -1; i--) {
if (text[i] == ' ' || text[i] == '\t'){
if (i == 0 ) continue;
if (text[i-1] != '\\') {
i--;
break;
}
}
}
if (i < 0)
in_command_position++;
else if (strchr (command_separator_chars, text[i])){
@ -679,17 +730,19 @@ try_complete (char *text, int *start, int *end, int flags)
p = q + 1;
q = NULL;
}
/* Command substitution? */
if (p > q && p > r){
matches = completion_matches (p + 1, command_completion_function);
SHOW_C_CTX("try_complete:cmd_backq_subst");
matches = completion_matches (p + 1, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
if (matches)
*start += p + 1 - word;
}
/* Variable name? */
else if (q > p && q > r){
matches = completion_matches (q, variable_completion_function);
SHOW_C_CTX("try_complete:var_subst");
matches = completion_matches (q, variable_completion_function, flags);
if (matches)
*start += q - word;
}
@ -697,7 +750,8 @@ try_complete (char *text, int *start, int *end, int flags)
/* Starts with '@', then look through the known hostnames for
completion first. */
else if (r > p && r > q){
matches = completion_matches (r, hostname_completion_function);
SHOW_C_CTX("try_complete:host_subst");
matches = completion_matches (r, hostname_completion_function, flags);
if (matches)
*start += r - word;
}
@ -705,26 +759,46 @@ try_complete (char *text, int *start, int *end, int flags)
/* 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))
matches = completion_matches (word, username_completion_function);
{
SHOW_C_CTX("try_complete:user_subst");
matches = completion_matches (word, username_completion_function, flags);
}
/* 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)
matches = completion_matches (word, command_completion_function);
{
SHOW_C_CTX("try_complete:cmd_subst");
matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
}
else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
if (is_cd)
ignore_filenames = 1;
matches = completion_matches (word, filename_completion_function);
ignore_filenames = 0;
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);
if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
char *p, *q = text + *start;
for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); p++);
for (p = text; *p && p < q; p++){
if (*p == ' ' || *p == '\t') {
if (p == text) continue;
if (*(p-1) == '\\') {
p--;
break;
}
}
}
if (!strncmp (p, "cd", 2))
for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++);
for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++){
if (p == text) continue;
if (*(p-1) == '\\') {
p--;
break;
}
}
if (p == q){
char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
char *cdpath = cdpath_ref;
@ -742,10 +816,9 @@ try_complete (char *text, int *start, int *end, int flags)
*s = 0;
if (*cdpath){
r = mhl_str_dir_plus_file (cdpath, word);
ignore_filenames = 1;
matches = completion_matches (r, filename_completion_function);
ignore_filenames = 0;
g_free (r);
SHOW_C_CTX("try_complete:filename_subst_2");
matches = completion_matches (r, filename_completion_function, flags);
g_free (r);
}
*s = c;
cdpath = s + 1;
@ -917,15 +990,21 @@ complete_engine (WInput *in, int what_to_do)
if (!in->completions){
end = in->point;
for (start = end ? end - 1 : 0; start > -1; start--)
if (strchr (" \t;|<>", in->buffer [start]))
break;
if (strchr (" \t;|<>", in->buffer [start])){
if (start > 0 && in->buffer [start-1] == '\\')
continue;
else
break;
}
if (start < end)
start++;
in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
}
if (in->completions){
if (what_to_do & DO_INSERTION || ((what_to_do & DO_QUERY) && !in->completions[1])) {
if (insert_text (in, in->completions [0], strlen (in->completions [0]))){
char * complete = in->completions [0];
if (insert_text (in, complete, strlen (complete))){
if (in->completions [1])
beep ();
else
@ -940,10 +1019,11 @@ complete_engine (WInput *in, int what_to_do)
char **p, *q;
Dlg_head *query_dlg;
WListbox *query_list;
for (p=in->completions + 1; *p; count++, p++)
if ((i = strlen (*p)) > maxlen)
maxlen = i;
for (p=in->completions + 1; *p; count++, p++) {
if ((i = strlen (*p)) > maxlen)
maxlen = i;
}
start_x = in->widget.x;
start_y = in->widget.y;
if (start_y - 2 >= count) {

View File

@ -1,16 +0,0 @@
#ifndef MC_COMPLETE_H
#define MC_COMPLETE_H
#define INPUT_COMPLETE_FILENAMES 1
#define INPUT_COMPLETE_HOSTNAMES 2
#define INPUT_COMPLETE_COMMANDS 4
#define INPUT_COMPLETE_VARIABLES 8
#define INPUT_COMPLETE_USERNAMES 16
#define INPUT_COMPLETE_CD 32
#include "widget.h"
void free_completions (WInput *);
void complete (WInput *);
#endif

View File

@ -50,6 +50,8 @@
#include <sys/stat.h>
#include <unistd.h>
#include <mhl/memory.h>
#include <mhl/escape.h>
#include <mhl/string.h>
#include "global.h"
@ -65,6 +67,7 @@
#include "widget.h"
#include "wtools.h"
#include "background.h" /* we_are_background */
#include "util.h"
/* Needed for current_panel, other_panel and WTree */
#include "dir.h"
@ -178,37 +181,43 @@ do_transform_source (FileOpContext *ctx, const char *source)
for (next_reg = 1, j = 0, k = 0; j < strlen (ctx->dest_mask); j++) {
switch (ctx->dest_mask[j]) {
case '\\':
j++;
if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
/* Backslash followed by non-digit */
switch (ctx->dest_mask[j]) {
case 'U':
case_conv |= UP_SECT;
case_conv &= ~LOW_SECT;
break;
case 'u':
case_conv |= UP_CHAR;
break;
case 'L':
case_conv |= LOW_SECT;
case_conv &= ~UP_SECT;
break;
case 'l':
case_conv |= LOW_CHAR;
break;
case 'E':
case_conv = NO_CONV;
break;
default:
/* Backslash as quote mark */
fntarget[k++] =
convert_case (ctx->dest_mask[j], &case_conv);
}
if (mhl_shell_is_char_escaped (&ctx->dest_mask[j])){
fntarget[k++] = ctx->dest_mask[j++];
fntarget[k++] = ctx->dest_mask[j];
break;
} else {
/* Backslash followed by digit */
next_reg = ctx->dest_mask[j] - '0';
/* Fall through */
j++;
if (!isdigit ((unsigned char) ctx->dest_mask[j])) {
/* Backslash followed by non-digit */
switch (ctx->dest_mask[j]) {
case 'U':
case_conv |= UP_SECT;
case_conv &= ~LOW_SECT;
break;
case 'u':
case_conv |= UP_CHAR;
break;
case 'L':
case_conv |= LOW_SECT;
case_conv &= ~UP_SECT;
break;
case 'l':
case_conv |= LOW_CHAR;
break;
case 'E':
case_conv = NO_CONV;
break;
default:
/* Backslash as quote mark */
fntarget[k++] =
convert_case (ctx->dest_mask[j], &case_conv);
}
break;
} else {
/* Backslash followed by digit */
next_reg = ctx->dest_mask[j] - '0';
/* Fall through */
}
}
case '*':
@ -793,7 +802,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path,
}
}
if (!appending) {
if (!appending && ctx->preserve) {
while (mc_chmod (dst_path, (src_mode & ctx->umask_kill))) {
temp_status = file_error (
_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
@ -1896,7 +1905,7 @@ panel_operate (void *source_panel, FileOperation operation,
g_free (dest);
dest = temp2;
temp = NULL;
switch (operation) {
case OP_COPY:
/*
@ -1988,6 +1997,9 @@ panel_operate (void *source_panel, FileOperation operation,
else {
char *temp2 = mhl_str_dir_plus_file (dest, temp);
source_with_path = mhl_shell_unescape_buf(source_with_path);
temp2 = mhl_shell_unescape_buf(temp2);
switch (operation) {
case OP_COPY:
/*

View File

@ -276,16 +276,16 @@ find_parameters (char **start_dir, char **pattern, char **content)
add_widget (find_dlg, case_sense);
in_with =
input_new (8, istart, INPUT_COLOR, ilen, in_contents, "content");
input_new (8, istart, INPUT_COLOR, ilen, in_contents, "content", INPUT_COMPLETE_DEFAULT);
add_widget (find_dlg, in_with);
add_widget (find_dlg, recursively_cbox);
in_name =
input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name");
input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name", INPUT_COMPLETE_DEFAULT);
add_widget (find_dlg, in_name);
in_start =
input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start");
input_new (3, istart, INPUT_COLOR, ilen, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
add_widget (find_dlg, in_start);
add_widget (find_dlg, label_new (8, 3, labs[2]));

View File

@ -67,7 +67,6 @@
#include "widget.h"
#include "command.h"
#include "wtools.h"
#include "complete.h" /* For the free_completion */
#include "chmod.h"
#include "chown.h"

View File

@ -165,7 +165,7 @@ init_panelize (void)
pname =
input_new (UY + 14, UX, INPUT_COLOR, panelize_dlg->cols - 10, "",
"in");
"in", INPUT_COMPLETE_DEFAULT);
add_widget (panelize_dlg, pname);
add_widget (panelize_dlg, label_new (UY + 13, UX, _("Command")));

View File

@ -35,6 +35,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <mhl/escape.h>
#include <mhl/string.h>
#include "global.h"
@ -1516,4 +1517,3 @@ Q_ (const char *s)
sep = strchr(result, '|');
return (sep != NULL) ? sep + 1 : result;
}

View File

@ -41,6 +41,8 @@
#endif
#include <unistd.h>
#include <mhl/string.h>
#include "global.h"
#include "execute.h"
#include "wtools.h" /* message() */
@ -426,7 +428,7 @@ canonicalize_pathname (char *path)
if (p[0] == PATH_SEP && p[1] == PATH_SEP) {
s = p + 1;
while (*(++s) == PATH_SEP);
strcpy (p + 1, s);
mhl_strmove (p + 1, s);
}
p++;
}
@ -435,7 +437,7 @@ canonicalize_pathname (char *path)
p = lpath;
while (*p) {
if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
strcpy (p, p + 2);
mhl_strmove (p, p + 2);
else
p++;
}
@ -451,7 +453,7 @@ canonicalize_pathname (char *path)
lpath[1] = 0;
return;
} else {
strcpy (lpath, lpath + 2);
mhl_strmove (lpath, lpath + 2);
}
}
@ -497,10 +499,10 @@ canonicalize_pathname (char *path)
if (p[3] != 0) {
if (s == lpath && *s == PATH_SEP) {
/* "/../foo" -> "/foo" */
strcpy (s + 1, p + 4);
mhl_strmove (s + 1, p + 4);
} else {
/* "token/../foo" -> "foo" */
strcpy (s, p + 4);
mhl_strmove (s, p + 4);
}
p = (s > lpath) ? s - 1 : s;
continue;

View File

@ -43,7 +43,6 @@
#include "dialog.h"
#include "widget.h"
#include "win.h"
#include "complete.h"
#include "key.h" /* XCTRL and ALT macros */
#include "profile.h" /* for history loading and saving */
#include "wtools.h" /* For common_dialog_repaint() */
@ -1660,7 +1659,7 @@ input_event (Gpm_Event * event, void *data)
WInput *
input_new (int y, int x, int color, int len, const char *def_text,
const char *histname)
const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
{
WInput *in = g_new (WInput, 1);
int initial_buffer_len;
@ -1689,9 +1688,7 @@ input_new (int y, int x, int color, int len, const char *def_text,
initial_buffer_len = 1 + max ((size_t) len, strlen (def_text));
in->widget.options |= W_IS_INPUT;
in->completions = NULL;
in->completion_flags =
INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
in->completion_flags = completion_flags;
in->current_max_len = initial_buffer_len;
in->buffer = g_malloc (initial_buffer_len);
in->color = color;

View File

@ -3,6 +3,23 @@
#include "dialog.h" /* Widget */
/* Completion stuff */
typedef enum {
INPUT_COMPLETE_FILENAMES = 1<<0,
INPUT_COMPLETE_HOSTNAMES = 1<<1,
INPUT_COMPLETE_COMMANDS = 1<<2,
INPUT_COMPLETE_VARIABLES = 1<<3,
INPUT_COMPLETE_USERNAMES = 1<<4,
INPUT_COMPLETE_CD = 1<<5,
INPUT_COMPLETE_SHELL_ESC = 1<<6,
INPUT_COMPLETE_DEFAULT = INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_HOSTNAMES
| INPUT_COMPLETE_VARIABLES
| INPUT_COMPLETE_USERNAMES
} INPUT_COMPLETE_FLAGS;
/* Please note that the first element in all the widgets is a */
/* widget variable of type Widget. We abuse this fact everywhere */
@ -70,7 +87,7 @@ typedef struct {
GList *history; /* The history */
int need_push; /* need to push the current Input on hist? */
char **completions; /* Possible completions array */
int completion_flags; /* INPUT_COMPLETE* bitwise flags(complete.h) */
INPUT_COMPLETE_FLAGS completion_flags; /* INPUT_COMPLETE* bitwise flags(complete.h) */
char *history_name; /* name of history for loading and saving */
} WInput;
@ -122,12 +139,13 @@ typedef struct WGroupbox {
char *title;
} WGroupbox;
/* Constructors */
WButton *button_new (int y, int x, int action, int flags, const char *text,
bcback callback);
WRadio *radio_new (int y, int x, int count, const char **text);
WCheck *check_new (int y, int x, int state, const char *text);
WInput *input_new (int y, int x, int color, int len, const char *text, const char *histname);
WInput *input_new (int y, int x, int color, int len, const char *text, const char *histname, INPUT_COMPLETE_FLAGS completion_flags);
WLabel *label_new (int y, int x, const char *text);
WGauge *gauge_new (int y, int x, int shown, int max, int current);
WListbox *listbox_new (int x, int y, int width, int height, lcback callback);
@ -200,4 +218,7 @@ void buttonbar_set_label_data (Dlg_head *h, int idx, const char *text,
void buttonbar_set_visible (WButtonBar *, gboolean);
void buttonbar_redraw (Dlg_head *h);
void free_completions (WInput *);
void complete (WInput *);
#endif

View File

@ -36,7 +36,6 @@
#include "widget.h"
#include "wtools.h"
#include "key.h" /* mi_getch() */
#include "complete.h" /* INPUT_COMPLETE_CD */
#include "background.h" /* parent_call */
@ -361,7 +360,7 @@ quick_dialog_skip (QuickDialog *qd, int nskip)
case quick_input:
input =
input_new (ypos, xpos, INPUT_COLOR, qw->hotkey_pos,
qw->text, qw->histname);
qw->text, qw->histname, INPUT_COMPLETE_DEFAULT);
input->is_password = qw->value == 1;
input->point = 0;
if (qw->value & 2)

View File

@ -144,7 +144,7 @@ fish_command (struct vfs_class *me, struct vfs_s_super *super,
enable_interrupt_key ();
status = write (SUP.sockw, str, strlen (str));
g_free (str);
mhl_mem_free (str);
disable_interrupt_key ();
if (status < 0)
@ -168,10 +168,10 @@ fish_free_archive (struct vfs_class *me, struct vfs_s_super *super)
close (SUP.sockr);
SUP.sockw = SUP.sockr = -1;
}
g_free (SUP.host);
g_free (SUP.user);
g_free (SUP.cwdir);
g_free (SUP.password);
mhl_mem_free (SUP.host);
mhl_mem_free (SUP.user);
mhl_mem_free (SUP.cwdir);
mhl_mem_free (SUP.password);
}
static void
@ -260,7 +260,7 @@ fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
p = g_strconcat (_(" fish: Password required for "),
SUP.user, " ", (char *) NULL);
op = vfs_get_password (p);
g_free (p);
mhl_mem_free (p);
if (op == NULL)
ERRNOR (EPERM, -1);
SUP.password = op;
@ -323,7 +323,7 @@ fish_open_archive (struct vfs_class *me, struct vfs_s_super *super,
p = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags,
&password, 0, URL_NOSLASH);
g_free (p);
mhl_mem_free (p);
SUP.host = host;
SUP.user = user;
@ -350,12 +350,12 @@ fish_archive_same (struct vfs_class *me, struct vfs_s_super *super,
op = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags, 0, 0,
URL_NOSLASH);
g_free (op);
mhl_mem_free (op);
flags = ((strcmp (host, SUP.host) == 0)
&& (strcmp (user, SUP.user) == 0) && (flags == SUP.flags));
g_free (host);
g_free (user);
mhl_mem_free (host);
mhl_mem_free (user);
return flags;
}
@ -367,7 +367,7 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
char buffer[8192];
struct vfs_s_entry *ent = NULL;
FILE *logfile;
char *quoted_path;
SHELL_ESCAPED_STR quoted_path;
int reply_code;
#if 0
@ -462,8 +462,8 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
"else\n"
"echo '### 500'\n"
"fi\n",
quoted_path, quoted_path, quoted_path, quoted_path, quoted_path, quoted_path);
mhl_mem_free (quoted_path);
quoted_path.s, quoted_path.s, quoted_path.s, quoted_path.s, quoted_path.s, quoted_path.s);
mhl_mem_free (quoted_path.s);
ent = vfs_s_generate_entry(me, NULL, dir, 0);
while (1) {
int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
@ -594,7 +594,7 @@ fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
vfs_s_free_entry (me, ent);
reply_code = fish_decode_reply(buffer + 4, 0);
if (reply_code == COMPLETE) {
g_free (SUP.cwdir);
mhl_mem_free (SUP.cwdir);
SUP.cwdir = g_strdup (remote_path);
print_vfs_message (_("%s: done."), me->name);
return 0;
@ -618,7 +618,7 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
struct stat s;
int was_error = 0;
int h;
char *quoted_name;
SHELL_ESCAPED_STR quoted_name;
h = open (localname, O_RDONLY);
@ -659,7 +659,7 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
*/
quoted_name = mhl_shell_escape_dup(name);
print_vfs_message(_("fish: store %s: sending command..."), quoted_name );
print_vfs_message(_("fish: store %s: sending command..."), quoted_name.s );
/* FIXME: File size is limited to ULONG_MAX */
if (!fh->u.fish.append)
@ -683,8 +683,8 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
" rest=`expr $rest - $n`\n"
"done\n"
"}; echo '### 200'\n",
(unsigned long) s.st_size, quoted_name,
quoted_name, (unsigned long) s.st_size,
(unsigned long) s.st_size, quoted_name.s,
quoted_name.s, (unsigned long) s.st_size,
(unsigned long) s.st_size);
else
n = fish_command (me, super, WAIT_REPLY,
@ -700,8 +700,8 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
" rest=`expr $rest - $n`\n"
"done\n"
"}; echo '### 200'\n",
(unsigned long) s.st_size, quoted_name,
quoted_name, (unsigned long) s.st_size);
(unsigned long) s.st_size, quoted_name.s,
quoted_name.s, (unsigned long) s.st_size);
if (n != PRELIM) {
close (h);
@ -735,14 +735,14 @@ fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *loc
(unsigned long) s.st_size);
}
close(h);
mhl_mem_free(quoted_name);
mhl_mem_free(quoted_name.s);
if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
ERRNOR (E_REMOTE, -1);
return 0;
error_return:
close(h);
fish_get_reply(me, SUP.sockr, NULL, 0);
mhl_mem_free(quoted_name);
mhl_mem_free(quoted_name.s);
return -1;
}
@ -750,7 +750,7 @@ static int
fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
{
char *name;
char *quoted_name;
SHELL_ESCAPED_STR quoted_name;
if (offset)
ERRNOR (E_NOTSUPP, 0);
name = vfs_s_fullpath (me, fh->ino);
@ -779,8 +779,8 @@ fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
"else\n"
"echo '### 500'\n"
"fi\n",
quoted_name, quoted_name, quoted_name, quoted_name );
g_free (quoted_name);
quoted_name.s, quoted_name.s, quoted_name.s, quoted_name.s );
mhl_mem_free (quoted_name.s);
if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
fh->linear = LS_LINEAR_OPEN;
fh->u.fish.got = 0;
@ -889,17 +889,18 @@ fish_send_command(struct vfs_class *me, struct vfs_s_super *super, const char *c
#define PREFIX \
char buf[BUF_LARGE]; \
const char *crpath; \
char *rpath, *mpath = g_strdup (path); \
char *mpath = mhl_str_dup (path); \
SHELL_ESCAPED_STR rpath; \
struct vfs_s_super *super; \
if (!(crpath = vfs_s_get_path_mangle (me, mpath, &super, 0))) { \
g_free (mpath); \
mhl_mem_free (mpath); \
return -1; \
} \
rpath = mhl_shell_escape_dup(crpath); \
g_free (mpath);
mhl_mem_free (mpath);
#define POSTFIX(flags) \
g_free (rpath); \
mhl_mem_free (rpath.s); \
return fish_send_command(me, super, buf, flags);
static int
@ -909,8 +910,8 @@ fish_chmod (struct vfs_class *me, const char *path, int mode)
g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
"chmod %4.4o /%s 2>/dev/null\n"
"echo '### 000'\n",
mode & 07777, rpath,
mode & 07777, rpath);
mode & 07777, rpath.s,
mode & 07777, rpath.s);
POSTFIX(OPT_FLUSH);
}
@ -919,24 +920,24 @@ static int fish_##name (struct vfs_class *me, const char *path1, const char *pat
{ \
char buf[BUF_LARGE]; \
const char *crpath1, *crpath2; \
char *rpath1, *rpath2, *mpath1, *mpath2; \
char *mpath1, *mpath2; \
struct vfs_s_super *super1, *super2; \
if (!(crpath1 = vfs_s_get_path_mangle (me, mpath1 = g_strdup(path1), &super1, 0))) { \
g_free (mpath1); \
mhl_mem_free (mpath1); \
return -1; \
} \
if (!(crpath2 = vfs_s_get_path_mangle (me, mpath2 = g_strdup(path2), &super2, 0))) { \
g_free (mpath1); \
g_free (mpath2); \
mhl_mem_free (mpath1); \
mhl_mem_free (mpath2); \
return -1; \
} \
rpath1 = mhl_shell_escape_dup (crpath1); \
g_free (mpath1); \
rpath2 = mhl_shell_escape_dup (crpath2); \
g_free (mpath2); \
g_snprintf(buf, sizeof(buf), string "\n", rpath1, rpath2, rpath1, rpath2); \
mhl_mem_free (rpath1); \
mhl_mem_free (rpath2); \
SHELL_ESCAPED_STR rpath1 = mhl_shell_escape_dup (crpath1); \
mhl_mem_free (mpath1); \
SHELL_ESCAPED_STR rpath2 = mhl_shell_escape_dup (crpath2); \
mhl_mem_free (mpath2); \
g_snprintf(buf, sizeof(buf), string "\n", rpath1.s, rpath2.s, rpath1.s, rpath2.s); \
mhl_mem_free (rpath1.s); \
mhl_mem_free (rpath2.s); \
return fish_send_command(me, super2, buf, OPT_FLUSH); \
}
@ -949,15 +950,15 @@ FISH_OP(link, "#LINK /%s /%s\n"
static int fish_symlink (struct vfs_class *me, const char *setto, const char *path)
{
char *qsetto;
SHELL_ESCAPED_STR qsetto;
PREFIX
qsetto = mhl_shell_escape_dup (setto);
g_snprintf(buf, sizeof(buf),
"#SYMLINK %s /%s\n"
"ln -s %s /%s 2>/dev/null\n"
"echo '### 000'\n",
qsetto, rpath, qsetto, rpath);
mhl_mem_free (qsetto);
qsetto.s, rpath.s, qsetto.s, rpath.s);
mhl_mem_free (qsetto.s);
POSTFIX(OPT_FLUSH);
}
@ -982,16 +983,16 @@ fish_chown (struct vfs_class *me, const char *path, int owner, int group)
"#CHOWN %s /%s\n"
"chown %s /%s 2>/dev/null\n"
"echo '### 000'\n",
sowner, rpath,
sowner, rpath);
sowner, rpath.s,
sowner, rpath.s);
fish_send_command (me, super, buf, OPT_FLUSH);
/* FIXME: what should we report if chgrp succeeds but chown fails? */
g_snprintf (buf, sizeof(buf),
"#CHGRP /%s \"/%s\"\n"
"chgrp %s \"/%s\" 2>/dev/null\n"
"echo '### 000'\n",
sgroup, rpath,
sgroup, rpath);
sgroup, rpath.s,
sgroup, rpath.s);
/* fish_send_command(me, super, buf, OPT_FLUSH); */
POSTFIX (OPT_FLUSH)
}
@ -1004,7 +1005,7 @@ static int fish_unlink (struct vfs_class *me, const char *path)
"#DELE /%s\n"
"rm -f /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
rpath.s, rpath.s);
POSTFIX(OPT_FLUSH);
}
@ -1018,7 +1019,7 @@ static int fish_mkdir (struct vfs_class *me, const char *path, mode_t mode)
"#MKD /%s\n"
"mkdir /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
rpath.s, rpath.s);
POSTFIX(OPT_FLUSH);
}
@ -1029,7 +1030,7 @@ static int fish_rmdir (struct vfs_class *me, const char *path)
"#RMD /%s\n"
"rmdir /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
rpath.s, rpath.s);
POSTFIX(OPT_FLUSH);
}
@ -1093,7 +1094,7 @@ fish_fill_names (struct vfs_class *me, fill_names_f func)
name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, flags,
"/", SUP.cwdir, (char *) NULL);
(*func)(name);
g_free (name);
mhl_mem_free (name);
super = super->next;
}
}