Skip to content
Snippets Groups Projects
Commit ff00771e authored by Jan Hák's avatar Jan Hák Committed by Daniel Salzman
Browse files

knsupdate: use of libedit in interactive mode with history and completion

parent 1987b5ae
No related merge requests found
This commit is part of merge request !1428. Comments created here will be created in the context of that merge request.
......@@ -524,6 +524,8 @@ src/utils/knotd/main.c
src/utils/knsec3hash/knsec3hash.c
src/utils/knsupdate/knsupdate_exec.c
src/utils/knsupdate/knsupdate_exec.h
src/utils/knsupdate/knsupdate_interactive.c
src/utils/knsupdate/knsupdate_interactive.h
src/utils/knsupdate/knsupdate_main.c
src/utils/knsupdate/knsupdate_params.c
src/utils/knsupdate/knsupdate_params.h
......
......@@ -64,10 +64,12 @@ knsec3hash_SOURCES = \
utils/knsec3hash/knsec3hash.c
knsupdate_SOURCES = \
utils/knsupdate/knsupdate_exec.c \
utils/knsupdate/knsupdate_exec.h \
utils/knsupdate/knsupdate_main.c \
utils/knsupdate/knsupdate_params.c \
utils/knsupdate/knsupdate_exec.c \
utils/knsupdate/knsupdate_exec.h \
utils/knsupdate/knsupdate_interactive.c \
utils/knsupdate/knsupdate_interactive.h \
utils/knsupdate/knsupdate_main.c \
utils/knsupdate/knsupdate_params.c \
utils/knsupdate/knsupdate_params.h
kdig_CPPFLAGS = $(libknotus_la_CPPFLAGS)
......
......@@ -23,6 +23,7 @@
#include "libdnssec/random.h"
#include "utils/knsupdate/knsupdate_exec.h"
#include "utils/knsupdate/knsupdate_interactive.h"
#include "utils/common/exec.h"
#include "utils/common/msg.h"
#include "utils/common/netio.h"
......@@ -66,7 +67,7 @@ int cmd_zone(const char* lp, knsupdate_params_t *params);
* This way we could identify command byte-per-byte and
* cancel early if the next is lexicographically greater.
*/
const char* cmd_array[] = {
const char* knsupdate_cmd_array[] = {
"\x3" "add",
"\x6" "answer",
"\x5" "class", /* {classname} */
......@@ -474,26 +475,24 @@ static int pkt_sendrecv(knsupdate_params_t *params)
return rb;
}
static int process_line(char *lp, void *arg)
int knsupdate_process_line(const char *line, knsupdate_params_t *params)
{
knsupdate_params_t *params = (knsupdate_params_t *)arg;
/* Check for empty line or comment. */
if (lp[0] == '\0' || lp[0] == ';') {
if (line[0] == '\0' || line[0] == ';') {
return KNOT_EOK;
}
int ret = tok_find(lp, cmd_array);
int ret = tok_find(line, knsupdate_cmd_array);
if (ret < 0) {
return ret; /* Syntax error - do nothing. */
}
const char *cmd = cmd_array[ret];
const char *val = tok_skipspace(lp + TOK_L(cmd));
const char *cmd = knsupdate_cmd_array[ret];
const char *val = tok_skipspace(line + TOK_L(cmd));
ret = cmd_handle[ret](val, params);
if (ret != KNOT_EOK) {
DBG("operation '%s' failed (%s) on line '%s'\n",
TOK_S(cmd), knot_strerror(ret), lp);
TOK_S(cmd), knot_strerror(ret), line);
}
return ret;
......@@ -510,44 +509,23 @@ static int process_lines(knsupdate_params_t *params, FILE *input)
{
char *buf = NULL;
size_t buflen = 0;
bool interactive = is_terminal(input);
int ret = KNOT_EOK;
/* Print first program prompt if interactive. */
if (interactive) {
fprintf(stderr, "> ");
if(is_terminal(input)) {
return interactive_loop(params);
}
int ret = KNOT_EOK;
/* Process lines. */
while (!params->stop && knot_getline(&buf, &buflen, input) != -1) {
/* Remove leading and trailing white space. */
char *line = strstrip(buf);
int call_ret = process_line(line, params);
ret = knsupdate_process_line(line, params);
memset(line, 0, strlen(line));
free(line);
if (call_ret != KNOT_EOK) {
/* Return the first error. */
if (ret == KNOT_EOK) {
ret = call_ret;
}
/* Exit if error and not interactive. */
if (!interactive) {
break;
}
}
/* Print program prompt if interactive. */
if (interactive && !params->stop) {
fprintf(stderr, "> ");
if (ret != KNOT_EOK) {
break;
}
}
if (interactive && feof(input)) {
/* Terminate line after empty prompt. */
fprintf(stderr, "\n");
}
if (buf != NULL) {
memset(buf, 0, buflen);
free(buf);
......@@ -564,9 +542,9 @@ int knsupdate_exec(knsupdate_params_t *params)
int ret = KNOT_EOK;
/* If no file specified, use stdin. */
/* If no file specified, enter the interactive mode. */
if (EMPTY_LIST(params->qfiles)) {
ret = process_lines(params, stdin);
ret = interactive_loop(params);
}
/* Read from each specified file. */
......@@ -603,7 +581,7 @@ int cmd_update(const char* lp, knsupdate_params_t *params)
DBG("%s: lp='%s'\n", __func__, lp);
/* update is optional token, next add|del|delete */
int bp = tok_find(lp, cmd_array);
int bp = tok_find(lp, knsupdate_cmd_array);
if (bp < 0) return bp; /* Syntax error. */
/* allow only specific tokens */
......@@ -614,7 +592,7 @@ int cmd_update(const char* lp, knsupdate_params_t *params)
return KNOT_EPARSEFAIL;
}
return h[bp](tok_skipspace(lp + TOK_L(cmd_array[bp])), params);
return h[bp](tok_skipspace(lp + TOK_L(knsupdate_cmd_array[bp])), params);
}
int cmd_add(const char* lp, knsupdate_params_t *params)
......
/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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
......@@ -18,4 +18,8 @@
#include "utils/knsupdate/knsupdate_params.h"
extern const char* knsupdate_cmd_array[];
int knsupdate_exec(knsupdate_params_t *params);
int knsupdate_process_line(const char *line, knsupdate_params_t *params);
/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 <https://www.gnu.org/licenses/>.
*/
#include <histedit.h>
#include "contrib/string.h"
#include "utils/common/lookup.h"
#include "utils/common/msg.h"
#include "utils/knsupdate/knsupdate_exec.h"
#include "utils/knsupdate/knsupdate_interactive.h"
#define PROGRAM_NAME "knsupdate"
#define HISTORY_FILE ".knsupdate_history"
static char *prompt(EditLine *el)
{
return PROGRAM_NAME"> ";
}
static void print_commands(void)
{
printf("\n");
for (const char **cmd = knsupdate_cmd_array; *cmd != NULL; cmd++) {
printf(" %-18s\n", (*cmd) + 1);
}
}
static void cmds_lookup(EditLine *el, const char *str, size_t str_len)
{
lookup_t lookup;
int ret = lookup_init(&lookup);
if (ret != KNOT_EOK) {
return;
}
// Fill the lookup with command names.
for (const char **desc = knsupdate_cmd_array; *desc != NULL; desc++) {
ret = lookup_insert(&lookup, (*desc) + 1, NULL);
if (ret != KNOT_EOK) {
goto cmds_lookup_finish;
}
}
lookup_complete(&lookup, str, str_len, el, true);
cmds_lookup_finish:
lookup_deinit(&lookup);
}
static unsigned char complete(EditLine *el, int ch)
{
int argc, token, pos;
const char **argv;
const LineInfo *li = el_line(el);
Tokenizer *tok = tok_init(NULL);
// Parse the line.
int ret = tok_line(tok, li, &argc, &argv, &token, &pos);
if (ret != 0) {
goto complete_exit;
}
// Show possible commands.
if (argc == 0) {
print_commands();
goto complete_exit;
}
// Complete the command name.
if (token == 0) {
cmds_lookup(el, argv[0], pos);
goto complete_exit;
}
// Find the command descriptor.
const char **desc = knsupdate_cmd_array;
while (*desc != NULL && strcmp((*desc) + 1, argv[0]) != 0) {
(*desc)++;
}
if (*desc == NULL) {
goto complete_exit;
}
complete_exit:
tok_reset(tok);
tok_end(tok);
return CC_REDISPLAY;
}
int interactive_loop(knsupdate_params_t *params)
{
char *hist_file = NULL;
const char *home = getenv("HOME");
if (home != NULL) {
hist_file = sprintf_alloc("%s/%s", home, HISTORY_FILE);
}
if (hist_file == NULL) {
INFO("failed to get home directory");
}
EditLine *el = el_init(PROGRAM_NAME, stdin, stdout, stderr);
if (el == NULL) {
ERR("interactive mode not available");
free(hist_file);
return KNOT_ERROR;
}
History *hist = history_init();
if (hist == NULL) {
ERR("interactive mode not available");
el_end(el);
free(hist_file);
return KNOT_ERROR;
}
HistEvent hev = { 0 };
history(hist, &hev, H_SETSIZE, 1000);
history(hist, &hev, H_SETUNIQUE, 1);
el_set(el, EL_HIST, history, hist);
history(hist, &hev, H_LOAD, hist_file);
el_set(el, EL_TERMINAL, NULL);
el_set(el, EL_EDITOR, "emacs");
el_set(el, EL_PROMPT, prompt);
el_set(el, EL_SIGNAL, 1);
el_source(el, NULL);
// Warning: these two el_sets()'s always leak -- in libedit2 library!
// For more details see this commit's message.
el_set(el, EL_ADDFN, PROGRAM_NAME"-complete",
"Perform "PROGRAM_NAME" completion.", complete);
el_set(el, EL_BIND, "^I", PROGRAM_NAME"-complete", NULL);
int count;
const char *line;
while ((line = el_gets(el, &count)) != NULL && count > 0) {
char command[count + 1];
memcpy(command, line, count);
command[count] = '\0';
// Removes trailing newline
size_t cmd_len = strcspn(command, "\n");
command[cmd_len] = '\0';
if (cmd_len > 0) {
history(hist, &hev, H_ENTER, command);
history(hist, &hev, H_SAVE, hist_file);
}
// Process the command.
(void)knsupdate_process_line(command, params);
if (params->stop) {
break;
}
}
history_end(hist);
free(hist_file);
el_end(el);
return KNOT_EOK;
}
/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "utils/knsupdate/knsupdate_params.h"
/*!
* Executes an interactive processing loop.
*
* \param[in] params Utility parameters.
*/
int interactive_loop(knsupdate_params_t *params);
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment