blob: 4d6c73761b0ba364401497990feff07993d26860 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2003 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include <signal.h>
#include "command.h"
#include "input.h"
#include "output.h"
#include "sig.h"
#include "malloc.h"
#include "init.h"
#if defined(ENABLE_READLINE)
# include <readline/history.h>
# include <readline/readline.h>
#elif defined(ENABLE_EDITLINE)
# include <histedit.h>
#endif
static int inputstacksize;
static FILE **inputstack;
static FILE *curinput;
static void popfile(void);
static int source_f(int argc, char **argv);
static const cmdinfo_t source_cmd =
{ "source", NULL, source_f, 1, 1, 0, N_("source-file"),
N_("get commands from source-file"), NULL };
/* our homegrown strtok that understands strings */
static char *
tokenize(
char *inp)
{
static char *last_place = NULL;
char *start;
char *walk;
int in_string = 0;
int in_escape = 0;
if (inp) {
start = inp;
} else {
if (last_place == NULL)
return NULL;
/* we're done */
if (*last_place != '\0')
return NULL;
start = last_place + 1;
}
last_place = NULL;
/* eat whitespace */
while (*start == ' ' || *start == '\t')
start++;
walk = start;
for (;*walk != '\0'; walk++) {
if (in_escape) {
in_escape = 0;
continue;
}
if (*walk == '\\')
in_escape = 1;
else if (*walk == '\"')
in_string ^= 1;
if (!in_string && !in_escape &&
(*walk == ' ' || *walk == '\t')) {
last_place = walk;
*last_place = '\0';
break;
}
}
if (walk == start)
return NULL;
return start;
}
char **
breakline(
char *input,
int *count)
{
int c;
char *inp;
char *p;
char **rval;
c = 0;
inp = input;
rval = xcalloc(sizeof(char *), 1);
for (;;) {
p = tokenize(inp);
if (p == NULL)
break;
inp = NULL;
c++;
rval = xrealloc(rval, sizeof(*rval) * (c + 1));
rval[c - 1] = p;
rval[c] = NULL;
}
*count = c;
return rval;
}
void
doneline(
char *input,
char **vec)
{
xfree(input);
xfree(vec);
}
static char *
get_prompt(void)
{
static char prompt[FILENAME_MAX + 1];
if (!prompt[0])
snprintf(prompt, sizeof(prompt), "%s> ", progname);
return prompt;
}
static char *
fetchline_internal(void)
{
char buf[1024];
int iscont;
size_t len;
size_t rlen;
char *rval;
rval = NULL;
for (rlen = iscont = 0; ; ) {
if (curinput == stdin) {
if (iscont)
dbprintf("... ");
else
dbprintf(get_prompt(), progname);
fflush(stdin);
}
if (seenint() ||
(!fgets(buf, sizeof(buf), curinput) &&
ferror(curinput) && seenint())) {
clearint();
dbprintf("^C\n");
clearerr(curinput);
if (iscont) {
iscont = 0;
rlen = 0;
if (rval) {
xfree(rval);
rval = NULL;
}
}
continue;
}
if (ferror(curinput) || feof(curinput) ||
(len = strlen(buf)) == 0) {
/*
* No more input at this inputstack level; pop
* our fd off and return so that a lower
* level fetchline can handle us. If this was
* an interactive session, print a newline
* because ^D doesn't emit one.
*/
if (curinput == stdin)
dbprintf("\n");
popfile();
iscont = 0;
rlen = 0;
if (rval) {
xfree(rval);
rval = NULL;
}
return NULL;
}
if (inputstacksize == 1)
logprintf("%s", buf);
rval = xrealloc(rval, rlen + len + 1);
if (rlen == 0)
rval[0] = '\0';
rlen += len;
strcat(rval, buf);
if (buf[len - 1] == '\n') {
if (len > 1 && buf[len - 2] == '\\') {
rval[rlen - 2] = ' ';
rval[rlen - 1] = '\0';
rlen--;
iscont = 1;
} else {
rval[rlen - 1] = '\0';
rlen--;
break;
}
}
}
return rval;
}
#ifdef ENABLE_READLINE
char *
fetchline(void)
{
char *line;
if (inputstacksize == 1) {
line = readline(get_prompt());
if (!line)
dbprintf("\n");
else if (line && *line) {
add_history(line);
logprintf("%s", line);
}
} else {
line = fetchline_internal();
}
return line;
}
#elif defined(ENABLE_EDITLINE)
static char *el_get_prompt(EditLine *e) { return get_prompt(); }
char *
fetchline(void)
{
static EditLine *el;
static History *hist;
HistEvent hevent;
char *line;
int count;
if (!el) {
hist = history_init();
history(hist, &hevent, H_SETSIZE, 100);
el = el_init(progname, stdin, stdout, stderr);
el_source(el, NULL);
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_PROMPT, el_get_prompt);
el_set(el, EL_HIST, history, (const char *)hist);
}
if (inputstacksize == 1) {
line = xstrdup(el_gets(el, &count));
if (line) {
if (count > 0)
line[count-1] = '\0';
if (*line) {
history(hist, &hevent, H_ENTER, line);
logprintf("%s", line);
}
}
} else {
line = fetchline_internal();
}
return line;
}
#else
char * fetchline(void) { return fetchline_internal(); }
#endif
static void
popfile(void)
{
if (inputstacksize == 0) {
curinput = NULL;
return;
}
if (curinput != stdin)
fclose(curinput);
inputstacksize--;
if (inputstacksize) {
inputstack =
xrealloc(inputstack, inputstacksize * sizeof(*inputstack));
curinput = inputstack[inputstacksize - 1];
} else {
free(inputstack);
curinput = NULL;
inputstack = NULL;
}
}
void
pushfile(
FILE *file)
{
inputstack =
xrealloc(inputstack,
(inputstacksize + 1) * sizeof(*inputstack));
inputstacksize++;
curinput = inputstack[inputstacksize - 1] = file;
}
/* ARGSUSED */
static int
source_f(
int argc,
char **argv)
{
FILE *f;
int c, done = 0;
char *input;
char **v;
f = fopen(argv[1], "r");
if (f == NULL) {
dbprintf(_("can't open %s\n"), argv[0]);
return 0;
}
/* Run the sourced commands now. */
pushfile(f);
while (!done) {
if ((input = fetchline_internal()) == NULL)
break;
v = breakline(input, &c);
if (c)
done = command(c, v);
doneline(input, v);
}
return 0;
}
void
input_init(void)
{
add_command(&source_cmd);
}