mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
![Paolo Bonzini](/assets/img/avatar_default.png)
qemu-io's cvtstr function sometimes will incorrectly omit the decimal part of the number, and sometimes will incorrectly include it. This patch fixes both. The former is more serious, and can be seen in the patches to 027.out and 033.out. The changes to all other files were scripted with sed, so there were no "surprises" beyond 027.out and 033.out. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
613 lines
12 KiB
C
613 lines
12 KiB
C
/*
|
|
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it would 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <getopt.h>
|
|
|
|
#include "cmd.h"
|
|
#include "qemu-aio.h"
|
|
#include "main-loop.h"
|
|
|
|
#define _(x) x /* not gettext support yet */
|
|
|
|
/* from libxcmd/command.c */
|
|
|
|
cmdinfo_t *cmdtab;
|
|
int ncmds;
|
|
|
|
static argsfunc_t args_func;
|
|
static checkfunc_t check_func;
|
|
static int ncmdline;
|
|
static char **cmdline;
|
|
|
|
static int
|
|
compare(const void *a, const void *b)
|
|
{
|
|
return strcmp(((const cmdinfo_t *)a)->name,
|
|
((const cmdinfo_t *)b)->name);
|
|
}
|
|
|
|
void add_command(const cmdinfo_t *ci)
|
|
{
|
|
cmdtab = g_realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
|
|
cmdtab[ncmds - 1] = *ci;
|
|
qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
|
|
}
|
|
|
|
static int
|
|
check_command(
|
|
const cmdinfo_t *ci)
|
|
{
|
|
if (check_func)
|
|
return check_func(ci);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
add_check_command(
|
|
checkfunc_t cf)
|
|
{
|
|
check_func = cf;
|
|
}
|
|
|
|
int
|
|
command_usage(
|
|
const cmdinfo_t *ci)
|
|
{
|
|
printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
command(
|
|
const cmdinfo_t *ct,
|
|
int argc,
|
|
char **argv)
|
|
{
|
|
char *cmd = argv[0];
|
|
|
|
if (!check_command(ct))
|
|
return 0;
|
|
|
|
if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
|
|
if (ct->argmax == -1)
|
|
fprintf(stderr,
|
|
_("bad argument count %d to %s, expected at least %d arguments\n"),
|
|
argc-1, cmd, ct->argmin);
|
|
else if (ct->argmin == ct->argmax)
|
|
fprintf(stderr,
|
|
_("bad argument count %d to %s, expected %d arguments\n"),
|
|
argc-1, cmd, ct->argmin);
|
|
else
|
|
fprintf(stderr,
|
|
_("bad argument count %d to %s, expected between %d and %d arguments\n"),
|
|
argc-1, cmd, ct->argmin, ct->argmax);
|
|
return 0;
|
|
}
|
|
optind = 0;
|
|
return ct->cfunc(argc, argv);
|
|
}
|
|
|
|
const cmdinfo_t *
|
|
find_command(
|
|
const char *cmd)
|
|
{
|
|
cmdinfo_t *ct;
|
|
|
|
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
|
|
if (strcmp(ct->name, cmd) == 0 ||
|
|
(ct->altname && strcmp(ct->altname, cmd) == 0))
|
|
return (const cmdinfo_t *)ct;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void add_user_command(char *optarg)
|
|
{
|
|
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *));
|
|
cmdline[ncmdline-1] = optarg;
|
|
}
|
|
|
|
static int
|
|
args_command(
|
|
int index)
|
|
{
|
|
if (args_func)
|
|
return args_func(index);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
add_args_command(
|
|
argsfunc_t af)
|
|
{
|
|
args_func = af;
|
|
}
|
|
|
|
static void prep_fetchline(void *opaque)
|
|
{
|
|
int *fetchable = opaque;
|
|
|
|
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
|
*fetchable= 1;
|
|
}
|
|
|
|
static char *get_prompt(void);
|
|
|
|
void command_loop(void)
|
|
{
|
|
int c, i, j = 0, done = 0, fetchable = 0, prompted = 0;
|
|
char *input;
|
|
char **v;
|
|
const cmdinfo_t *ct;
|
|
|
|
for (i = 0; !done && i < ncmdline; i++) {
|
|
input = strdup(cmdline[i]);
|
|
if (!input) {
|
|
fprintf(stderr, _("cannot strdup command '%s': %s\n"),
|
|
cmdline[i], strerror(errno));
|
|
exit(1);
|
|
}
|
|
v = breakline(input, &c);
|
|
if (c) {
|
|
ct = find_command(v[0]);
|
|
if (ct) {
|
|
if (ct->flags & CMD_FLAG_GLOBAL) {
|
|
done = command(ct, c, v);
|
|
} else {
|
|
j = 0;
|
|
while (!done && (j = args_command(j))) {
|
|
done = command(ct, c, v);
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
|
|
}
|
|
}
|
|
doneline(input, v);
|
|
}
|
|
if (cmdline) {
|
|
g_free(cmdline);
|
|
return;
|
|
}
|
|
|
|
while (!done) {
|
|
if (!prompted) {
|
|
printf("%s", get_prompt());
|
|
fflush(stdout);
|
|
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
|
|
prompted = 1;
|
|
}
|
|
|
|
main_loop_wait(false);
|
|
|
|
if (!fetchable) {
|
|
continue;
|
|
}
|
|
input = fetchline();
|
|
if (input == NULL) {
|
|
break;
|
|
}
|
|
v = breakline(input, &c);
|
|
if (c) {
|
|
ct = find_command(v[0]);
|
|
if (ct) {
|
|
done = command(ct, c, v);
|
|
} else {
|
|
fprintf(stderr, _("command \"%s\" not found\n"), v[0]);
|
|
}
|
|
}
|
|
doneline(input, v);
|
|
|
|
prompted = 0;
|
|
fetchable = 0;
|
|
}
|
|
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
|
|
}
|
|
|
|
/* from libxcmd/input.c */
|
|
|
|
#if defined(ENABLE_READLINE)
|
|
# include <readline/history.h>
|
|
# include <readline/readline.h>
|
|
#elif defined(ENABLE_EDITLINE)
|
|
# include <histedit.h>
|
|
#endif
|
|
|
|
static char *
|
|
get_prompt(void)
|
|
{
|
|
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
|
|
|
|
if (!prompt[0])
|
|
snprintf(prompt, sizeof(prompt), "%s> ", progname);
|
|
return prompt;
|
|
}
|
|
|
|
#if defined(ENABLE_READLINE)
|
|
char *
|
|
fetchline(void)
|
|
{
|
|
char *line;
|
|
|
|
line = readline(get_prompt());
|
|
if (line && *line)
|
|
add_history(line);
|
|
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);
|
|
}
|
|
line = strdup(el_gets(el, &count));
|
|
if (line) {
|
|
if (count > 0)
|
|
line[count-1] = '\0';
|
|
if (*line)
|
|
history(hist, &hevent, H_ENTER, line);
|
|
}
|
|
return line;
|
|
}
|
|
#else
|
|
# define MAXREADLINESZ 1024
|
|
char *
|
|
fetchline(void)
|
|
{
|
|
char *p, *line = malloc(MAXREADLINESZ);
|
|
|
|
if (!line)
|
|
return NULL;
|
|
if (!fgets(line, MAXREADLINESZ, stdin)) {
|
|
free(line);
|
|
return NULL;
|
|
}
|
|
p = line + strlen(line);
|
|
if (p != line && p[-1] == '\n')
|
|
p[-1] = '\0';
|
|
return line;
|
|
}
|
|
#endif
|
|
|
|
static char *qemu_strsep(char **input, const char *delim)
|
|
{
|
|
char *result = *input;
|
|
if (result != NULL) {
|
|
char *p;
|
|
|
|
for (p = result; *p != '\0'; p++) {
|
|
if (strchr(delim, *p)) {
|
|
break;
|
|
}
|
|
}
|
|
if (*p == '\0') {
|
|
*input = NULL;
|
|
} else {
|
|
*p = '\0';
|
|
*input = p + 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
char **breakline(char *input, int *count)
|
|
{
|
|
int c = 0;
|
|
char *p;
|
|
char **rval = calloc(sizeof(char *), 1);
|
|
char **tmp;
|
|
|
|
while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
|
|
if (!*p) {
|
|
continue;
|
|
}
|
|
c++;
|
|
tmp = realloc(rval, sizeof(*rval) * (c + 1));
|
|
if (!tmp) {
|
|
free(rval);
|
|
rval = NULL;
|
|
c = 0;
|
|
break;
|
|
} else {
|
|
rval = tmp;
|
|
}
|
|
rval[c - 1] = p;
|
|
rval[c] = NULL;
|
|
}
|
|
*count = c;
|
|
return rval;
|
|
}
|
|
|
|
void
|
|
doneline(
|
|
char *input,
|
|
char **vec)
|
|
{
|
|
free(input);
|
|
free(vec);
|
|
}
|
|
|
|
#define EXABYTES(x) ((long long)(x) << 60)
|
|
#define PETABYTES(x) ((long long)(x) << 50)
|
|
#define TERABYTES(x) ((long long)(x) << 40)
|
|
#define GIGABYTES(x) ((long long)(x) << 30)
|
|
#define MEGABYTES(x) ((long long)(x) << 20)
|
|
#define KILOBYTES(x) ((long long)(x) << 10)
|
|
|
|
long long
|
|
cvtnum(
|
|
char *s)
|
|
{
|
|
long long i;
|
|
char *sp;
|
|
int c;
|
|
|
|
i = strtoll(s, &sp, 0);
|
|
if (i == 0 && sp == s)
|
|
return -1LL;
|
|
if (*sp == '\0')
|
|
return i;
|
|
|
|
if (sp[1] != '\0')
|
|
return -1LL;
|
|
|
|
c = qemu_tolower(*sp);
|
|
switch (c) {
|
|
default:
|
|
return i;
|
|
case 'k':
|
|
return KILOBYTES(i);
|
|
case 'm':
|
|
return MEGABYTES(i);
|
|
case 'g':
|
|
return GIGABYTES(i);
|
|
case 't':
|
|
return TERABYTES(i);
|
|
case 'p':
|
|
return PETABYTES(i);
|
|
case 'e':
|
|
return EXABYTES(i);
|
|
}
|
|
return -1LL;
|
|
}
|
|
|
|
#define TO_EXABYTES(x) ((x) / EXABYTES(1))
|
|
#define TO_PETABYTES(x) ((x) / PETABYTES(1))
|
|
#define TO_TERABYTES(x) ((x) / TERABYTES(1))
|
|
#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
|
|
#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
|
|
#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
|
|
|
|
void
|
|
cvtstr(
|
|
double value,
|
|
char *str,
|
|
size_t size)
|
|
{
|
|
char *trim;
|
|
const char *suffix;
|
|
|
|
if (value >= EXABYTES(1)) {
|
|
suffix = " EiB";
|
|
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
|
|
} else if (value >= PETABYTES(1)) {
|
|
suffix = " PiB";
|
|
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
|
|
} else if (value >= TERABYTES(1)) {
|
|
suffix = " TiB";
|
|
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
|
|
} else if (value >= GIGABYTES(1)) {
|
|
suffix = " GiB";
|
|
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
|
|
} else if (value >= MEGABYTES(1)) {
|
|
suffix = " MiB";
|
|
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
|
|
} else if (value >= KILOBYTES(1)) {
|
|
suffix = " KiB";
|
|
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
|
|
} else {
|
|
suffix = " bytes";
|
|
snprintf(str, size - 6, "%f", value);
|
|
}
|
|
|
|
trim = strstr(str, ".000");
|
|
if (trim) {
|
|
strcpy(trim, suffix);
|
|
} else {
|
|
strcat(str, suffix);
|
|
}
|
|
}
|
|
|
|
struct timeval
|
|
tsub(struct timeval t1, struct timeval t2)
|
|
{
|
|
t1.tv_usec -= t2.tv_usec;
|
|
if (t1.tv_usec < 0) {
|
|
t1.tv_usec += 1000000;
|
|
t1.tv_sec--;
|
|
}
|
|
t1.tv_sec -= t2.tv_sec;
|
|
return t1;
|
|
}
|
|
|
|
double
|
|
tdiv(double value, struct timeval tv)
|
|
{
|
|
return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
|
|
}
|
|
|
|
#define HOURS(sec) ((sec) / (60 * 60))
|
|
#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
|
|
#define SECONDS(sec) ((sec) % 60)
|
|
|
|
void
|
|
timestr(
|
|
struct timeval *tv,
|
|
char *ts,
|
|
size_t size,
|
|
int format)
|
|
{
|
|
double usec = (double)tv->tv_usec / 1000000.0;
|
|
|
|
if (format & TERSE_FIXED_TIME) {
|
|
if (!HOURS(tv->tv_sec)) {
|
|
snprintf(ts, size, "%u:%02u.%02u",
|
|
(unsigned int) MINUTES(tv->tv_sec),
|
|
(unsigned int) SECONDS(tv->tv_sec),
|
|
(unsigned int) (usec * 100));
|
|
return;
|
|
}
|
|
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
|
|
}
|
|
|
|
if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
|
|
snprintf(ts, size, "%u:%02u:%02u.%02u",
|
|
(unsigned int) HOURS(tv->tv_sec),
|
|
(unsigned int) MINUTES(tv->tv_sec),
|
|
(unsigned int) SECONDS(tv->tv_sec),
|
|
(unsigned int) (usec * 100));
|
|
} else {
|
|
snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
|
|
}
|
|
}
|
|
|
|
|
|
/* from libxcmd/quit.c */
|
|
|
|
static cmdinfo_t quit_cmd;
|
|
|
|
/* ARGSUSED */
|
|
static int
|
|
quit_f(
|
|
int argc,
|
|
char **argv)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
quit_init(void)
|
|
{
|
|
quit_cmd.name = _("quit");
|
|
quit_cmd.altname = _("q");
|
|
quit_cmd.cfunc = quit_f;
|
|
quit_cmd.argmin = -1;
|
|
quit_cmd.argmax = -1;
|
|
quit_cmd.flags = CMD_FLAG_GLOBAL;
|
|
quit_cmd.oneline = _("exit the program");
|
|
|
|
add_command(&quit_cmd);
|
|
}
|
|
|
|
/* from libxcmd/help.c */
|
|
|
|
static cmdinfo_t help_cmd;
|
|
static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
|
|
static void help_oneline(const char *cmd, const cmdinfo_t *ct);
|
|
|
|
static void
|
|
help_all(void)
|
|
{
|
|
const cmdinfo_t *ct;
|
|
|
|
for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
|
|
help_oneline(ct->name, ct);
|
|
printf(_("\nUse 'help commandname' for extended help.\n"));
|
|
}
|
|
|
|
static int
|
|
help_f(
|
|
int argc,
|
|
char **argv)
|
|
{
|
|
const cmdinfo_t *ct;
|
|
|
|
if (argc == 1) {
|
|
help_all();
|
|
return 0;
|
|
}
|
|
ct = find_command(argv[1]);
|
|
if (ct == NULL) {
|
|
printf(_("command %s not found\n"), argv[1]);
|
|
return 0;
|
|
}
|
|
help_onecmd(argv[1], ct);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
help_onecmd(
|
|
const char *cmd,
|
|
const cmdinfo_t *ct)
|
|
{
|
|
help_oneline(cmd, ct);
|
|
if (ct->help)
|
|
ct->help();
|
|
}
|
|
|
|
static void
|
|
help_oneline(
|
|
const char *cmd,
|
|
const cmdinfo_t *ct)
|
|
{
|
|
if (cmd)
|
|
printf("%s ", cmd);
|
|
else {
|
|
printf("%s ", ct->name);
|
|
if (ct->altname)
|
|
printf("(or %s) ", ct->altname);
|
|
}
|
|
if (ct->args)
|
|
printf("%s ", ct->args);
|
|
printf("-- %s\n", ct->oneline);
|
|
}
|
|
|
|
void
|
|
help_init(void)
|
|
{
|
|
help_cmd.name = _("help");
|
|
help_cmd.altname = _("?");
|
|
help_cmd.cfunc = help_f;
|
|
help_cmd.argmin = 0;
|
|
help_cmd.argmax = 1;
|
|
help_cmd.flags = CMD_FLAG_GLOBAL;
|
|
help_cmd.args = _("[command]");
|
|
help_cmd.oneline = _("help for one or all commands");
|
|
|
|
add_command(&help_cmd);
|
|
}
|