| /* |
| * 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); |
| } |