//
// aegis - project change supervisor
// Copyright (C) 2003-2008 Peter Miller
//
// 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 3 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, see
// .
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct shell_zsh_ty
{
shell_ty inherited;
string_ty *command;
string_ty *prefix;
};
static char *
copy_of(const char *s, size_t len)
{
char *result = new char [len + 1];
memcpy(result, s, len);
result[len] = 0;
return result;
}
static void
destructor(shell_ty *sp)
{
shell_zsh_ty *this_thing;
this_thing = (shell_zsh_ty *)sp;
if (this_thing->command)
str_free(this_thing->command);
if (this_thing->prefix)
str_free(this_thing->prefix);
}
static void
usage(void)
{
const char *prog;
prog = progname_get();
fprintf(stderr, "Usage: %s \n", prog);
exit(1);
}
static int
test(shell_ty *sp)
{
shell_zsh_ty *this_thing;
char *cp;
char *end;
unsigned long n;
char *comp_line;
unsigned long comp_point;
size_t ac;
size_t ac_max;
char **av;
int inco_ac;
this_thing = (shell_zsh_ty *)sp;
//
// The PREFIX environment variable must be set and valid.
//
cp = getenv("PREFIX");
if (!cp)
return 0;
this_thing->prefix = str_from_c(cp);
//
// The BUFFER environment variable must be set and valid.
//
comp_line = getenv("BUFFER");
if (!comp_line || !*comp_line)
return 0;
//
// The CURSOR environment variable must be set and valid.
//
cp = getenv("CURSOR");
if (!cp)
return 0;
n = strtoul(cp, &end, 10);
if (end == cp || *end)
return 0;
comp_point = n;
if (comp_point > strlen(comp_line))
return 0;
//
// There should be at least one command line argument,
// the name of the command being completed
//
if (arglex_get_string() != arglex_token_string)
usage();
this_thing->command = str_from_c(arglex_value.alv_string);
//
// Ignore the rest of the command line arguments.
//
while (arglex_get_string() == arglex_token_string)
;
if (arglex_token != arglex_token_eoln)
usage();
//
// Generate the new command line, by splitting the comp_line string
// into words.
//
ac = 0;
ac_max = 0;
av = 0;
inco_ac = -1;
for (cp = comp_line; ; )
{
//
// If the completion point is in the middle of white space,
// or at the start of another word, insert an empty argument to
// serve as the imcomplete argument in need of attention.
//
if ((unsigned long)(cp - comp_line) == comp_point)
{
//
// insert the empty string as the incomplete argument.
//
if (ac >= ac_max)
{
ac_max = ac_max * 2 + 8;
char **new_av = new char * [ac_max];
for (size_t k = 0; k < ac; ++k)
new_av[k] = av[k];
delete [] av;
av = new_av;
}
inco_ac = ac;
av[ac++] = copy_of(cp, 0);
comp_point = ~0uL;
}
//
// end of the line
//
if (!*cp)
{
break;
}
//
// Skip white space around words.
//
if (isspace((unsigned char)*cp))
{
++cp;
continue;
}
//
// Collect one word.
//
// Note that the completion point also terminates the word,
// even if there is no white space present.
//
end = cp;
while
(
*end
&&
!isspace((unsigned char)*end)
&&
end != comp_line + comp_point
)
{
++end;
}
//
// Insert word into the list.
//
if (ac >= ac_max)
{
ac_max = ac_max * 2 + 8;
char **new_av = new char * [ac_max];
for (size_t k = 0; k < ac; ++k)
new_av[k] = av[k];
delete [] av;
av = new_av;
}
if ((unsigned long)(cp - comp_line) < comp_point &&
comp_point <= (unsigned long)(end - comp_line))
{
inco_ac = ac;
comp_point = ~0uL;
}
av[ac++] = copy_of(cp, end - cp);
//
// Move past the word.
//
cp = end;
}
assert(inco_ac >= 0);
//
// NULL terminate the list of word pointers.
//
if (ac >= ac_max)
{
ac_max = ac_max * 2 + 8;
char **new_av = new char * [ac_max];
for (size_t k = 0; k < ac; ++k)
new_av[k] = av[k];
delete [] av;
av = new_av;
}
av[ac] = 0;
//
// Insert our fake command line into the command line processor.
//
arglex_synthetic(ac, av, inco_ac);
//
// Report success.
//
return 1;
}
static string_ty *
command_get(shell_ty *sh)
{
shell_zsh_ty *this_thing;
this_thing = (shell_zsh_ty *)sh;
return this_thing->command;
}
static string_ty *
prefix_get(shell_ty *sh)
{
shell_zsh_ty *this_thing;
this_thing = (shell_zsh_ty *)sh;
return this_thing->prefix;
}
static void
emit(shell_ty *, string_ty *s)
{
char *cp;
for (cp = s->str_text; *cp; ++cp)
{
switch (*cp)
{
case '\\':
case '\n':
putchar('\\');
// fall through...
default:
putchar(*cp);
}
}
putchar('\n');
}
static shell_vtbl_ty vtbl =
{
destructor,
test,
command_get,
prefix_get,
emit,
sizeof(shell_zsh_ty),
"zsh",
};
shell_ty *
shell_zsh(void)
{
shell_ty *sp;
shell_zsh_ty *this_thing;
sp = shell_new(&vtbl);
this_thing = (shell_zsh_ty *)sp;
this_thing->command = 0;
this_thing->prefix = 0;
return sp;
}