#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <dbllist.h>
#include <safe.h>
#include <log.h>
#include "string.h"
#include "command.h"
#define MAX_CMD_LEN 256
bool libcommand_use_as_many_as_unique;
char *libcommand_eof_command;
char *libcommand_default_command;
static char *
command_completer (const char *text, int state)
{
static int n;
char *cmdname;
if (state == 0)
n = 0;
while ((cmdname = commands[n++].identifier))
if (!strncmp(text, cmdname, strlen(text)))
return xstrdup(cmdname);
return NULL;
}
static char *
get_command (const char *prompt)
{
static char *gotline;
char **m;
int mlen;
if (gotline) {
free(gotline);
gotline = NULL;
}
if (libcommand_use_as_many_as_unique) {
rl_num_chars_to_read = 1;
rl_completion_append_character = '\0';
rl_completion_entry_function = command_completer;
} else {
rl_num_chars_to_read = 0;
rl_completion_entry_function = command_completer;
}
while ((gotline = readline(prompt))) {
if (!*gotline)
return NULL;
if (contains_only_whitespace(gotline))
return NULL;
if (libcommand_use_as_many_as_unique) {
m = rl_completion_matches(gotline, command_completer);
if (!m)
return gotline;
rl_complete(0, '!');
for (mlen = 0; m[mlen]; mlen++);
if (mlen == 1) {
gotline = xxrealloc(gotline, rl_end + 1);
strncpy(gotline, rl_line_buffer, rl_end);
*(gotline + rl_end) = '\0';
return gotline;
}
continue;
}
add_history(gotline);
return gotline;
}
if (!gotline) {
if (libcommand_eof_command)
return xstrdup(libcommand_eof_command);
else
return NULL;
}
if (libcommand_default_command)
return xstrdup(libcommand_default_command);
else
return NULL;
}
static char *
check_token (char *p)
{
unsigned c;
if (strnlen(p, MAX_CMD_LEN) > MAX_CMD_LEN) {
xlog(0, "token exceeds maximum characters\n");
return NULL;
}
while ((c = *p++)) {
if (!isprint(c)) {
xlog(0, "token contains non-glyph characters\n");
return NULL;
}
}
return p;
}
static command_t *
lookup_command (char *p)
{
int n;
char *id;
for (n = 0; (id = commands[n].identifier); n++)
if (!strcmp(id, p))
return &commands[n];
return NULL;
}
XXX
XXX
static command_t *
make_command (char *cmdline)
{
int n, err;
char *tok;
list_head_t *list;
command_t *docmd, *command;
static wordexp_t words;
assert(cmdline);
assert(*cmdline);
err = wordexp(cmdline, &words, WRDE_REUSE|WRDE_SHOWERR|WRDE_UNDEF);
if (err) {
xlog(0, "wordexp error with input string\n");
switch (err) {
case WRDE_BADCHAR:
xlog(0, "bad char "
"WRDE_BADCHAR\n");
break;
case WRDE_BADVAL:
xlog(0, "undef'd shell var forbidden "
"WRDE_BADVAL\n");
break;
case WRDE_CMDSUB:
xlog(0, "command substitution forbidden "
"WRDE_CMDSUB\n");
break;
case WRDE_NOSPACE:
xlog(0, "alloc error, partial result "
"WRDE_NOSPACE\n");
break;
case WRDE_SYNTAX:
xlog(0, "syntax error "
"WRDE_SYNTAX\n");
break;
default:
xlog(0, "impossible default error from "
"wordexp()\n");
break;
}
return NULL;
}
tok = words.we_wordv[0];
if (!check_token(tok)) {
xlog(0, "%s: command name is not sane\n", __FUNCTION__);
return NULL;
}
if (!(command = lookup_command(tok))) {
xlog(0, "%s: command is not registered\n", __FUNCTION__);
return NULL;
}
docmd = xmalloc(sizeof(command_t));
memcpy(docmd, command, sizeof(command_t));
list = list_create();
for (n = 1; n < words.we_wordc; n++) {
XXX
tok = words.we_wordv[n];
if (!check_token(tok)) {
xlog(0, "invalid argument token\n");
return NULL;
}
list_add(list, strdup(tok));
}
docmd->arguments = list;
return docmd;
}
static bool
free_arg (list_node_t *list)
{
assert(list);
assert(list->item);
free(list->item);
return true;
}
static bool
do_command (command_t *docmd)
{
unsigned ret, errors;
ret = docmd->callback(docmd->arguments);
errors = list_iterate_all(docmd->arguments, free_arg);
list_destroy(docmd->arguments);
if (ret == false || errors == false )
return false;
return true;
}
void
command_loop (const char *prompt, int maxchars)
{
char *got;
command_t *command;
for (;;) {
if (!(got = get_command(prompt)))
continue;
if (!(command = make_command(got))) {
xlog(0, "%s: %s: invalid command\n", __FUNCTION__, got);
continue;
}
if (!do_command(command))
exit(EXIT_FAILURE);
}
}
XXX
char *
command_availables (void)
{
static char buf[BUFSIZ];
char *p;
int n;
for (n = 0, p = buf; commands[n].identifier; n++) {
p = stpncpy(p, commands[n].identifier, BUFSIZ - (p - buf));
p = stpncpy(p, "\t", BUFSIZ - (p - buf));
p = stpncpy(p, commands[n].shortdesc, BUFSIZ - (p - buf));
p = stpncpy(p, "\n", BUFSIZ - (p - buf));
}
return buf;
}