 8655d2de0a
			
		
	
	
		8655d2de0a
		
	
	
	
	
		
			
			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);
 | |
| }
 |