| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2003-2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "platform_defs.h" |
| #include "command.h" |
| #include "input.h" |
| |
| cmdinfo_t *cmdtab; |
| int ncmds; |
| |
| static iterfunc_t iter_func; |
| static checkfunc_t check_func; |
| |
| struct cmdline { |
| char *cmdline; |
| bool iterate; |
| }; |
| |
| static int ncmdline; |
| static struct cmdline *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 = realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab)); |
| if (!cmdtab) { |
| perror(_("adding libxcmd command")); |
| exit(1); |
| } |
| cmdtab[ncmds - 1] = *ci; |
| qsort(cmdtab, ncmds, sizeof(*cmdtab), compare); |
| } |
| |
| static int |
| check_command( |
| const cmdinfo_t *ci) |
| { |
| /* always run internal library supplied commands */ |
| if (ci->flags & CMD_FLAG_LIBRARY) |
| return 1; |
| |
| 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; |
| } |
| platform_getoptreset(); |
| 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) |
| { |
| ncmdline++; |
| cmdline = realloc(cmdline, sizeof(struct cmdline) * (ncmdline)); |
| if (!cmdline) { |
| perror("realloc"); |
| exit(1); |
| } |
| cmdline[ncmdline-1].cmdline = optarg; |
| cmdline[ncmdline-1].iterate = true; |
| |
| } |
| |
| void |
| add_oneshot_user_command(char *optarg) |
| { |
| ncmdline++; |
| cmdline = realloc(cmdline, sizeof(struct cmdline) * (ncmdline)); |
| if (!cmdline) { |
| perror("realloc"); |
| exit(1); |
| } |
| cmdline[ncmdline-1].cmdline = optarg; |
| cmdline[ncmdline-1].iterate = false; |
| } |
| |
| /* |
| * Run a command, iterating as necessary. Return 0 for success, non-zero |
| * if an error occurred. Errors terminate loop iteration immediately. |
| */ |
| static int |
| iterate_command( |
| const cmdinfo_t *ct, |
| int argc, |
| char **argv) |
| { |
| int error = 0; |
| int j; |
| |
| /* if there's nothing to iterate, we're done! */ |
| if (!iter_func) |
| return 0; |
| |
| for (j = iter_func(0); j; j = iter_func(j)) { |
| error = command(ct, argc, argv); |
| if (error) |
| break; |
| |
| } |
| |
| return error; |
| } |
| |
| void |
| add_command_iterator( |
| iterfunc_t func) |
| { |
| iter_func = func; |
| } |
| |
| static int |
| process_input( |
| char *input, |
| bool iterate) |
| { |
| char **v; |
| const cmdinfo_t *ct; |
| int c = 0; |
| int error = 0; |
| |
| v = breakline(input, &c); |
| if (!c) |
| goto out; |
| |
| ct = find_command(v[0]); |
| if (!ct) { |
| fprintf(stderr, _("command \"%s\" not found\n"), v[0]); |
| goto out; |
| } |
| |
| /* oneshot commands don't iterate */ |
| if (!iterate || (ct->flags & CMD_FLAG_ONESHOT)) |
| error = command(ct, c, v); |
| else |
| error = iterate_command(ct, c, v); |
| out: |
| doneline(input, v); |
| return error; |
| } |
| |
| void |
| command_loop(void) |
| { |
| char *input; |
| int done = 0; |
| int i; |
| |
| if (!cmdline) { |
| /* interactive mode */ |
| while (!done) { |
| input = fetchline(); |
| if (!input) |
| break; |
| done = process_input(input, false); |
| } |
| return; |
| } |
| |
| /* command line mode */ |
| for (i = 0; !done && i < ncmdline; i++) { |
| input = strdup(cmdline[i].cmdline); |
| if (!input) { |
| fprintf(stderr, |
| _("cannot strdup command '%s': %s\n"), |
| cmdline[i].cmdline, strerror(errno)); |
| exit(1); |
| } |
| done = process_input(input, cmdline[i].iterate); |
| } |
| free(cmdline); |
| return; |
| } |
| |
| void |
| report_io_times( |
| const char *verb, |
| struct timeval *t2, |
| long long offset, |
| long long count, |
| long long total, |
| int ops, |
| int compact) |
| { |
| char s1[64], s2[64], ts[64]; |
| |
| timestr(t2, ts, sizeof(ts), compact ? VERBOSE_FIXED_TIME : 0); |
| if (!compact) { |
| cvtstr((double)total, s1, sizeof(s1)); |
| cvtstr(tdiv((double)total, *t2), s2, sizeof(s2)); |
| printf(_("%s %lld/%lld bytes at offset %lld\n"), |
| verb, total, count, (long long)offset); |
| printf(_("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n"), |
| s1, ops, ts, s2, tdiv((double)ops, *t2)); |
| } else {/* bytes,ops,time,bytes/sec,ops/sec */ |
| printf("%lld,%d,%s,%.3f,%.3f\n", |
| total, ops, ts, |
| tdiv((double)total, *t2), tdiv((double)ops, *t2)); |
| } |
| } |