]>
| Commit | Line | Data |
|---|---|---|
| 1 | // SPDX-License-Identifier: GPL-2.0 | |
| 2 | /* | |
| 3 | * Copyright (c) 2003-2005 Silicon Graphics, Inc. | |
| 4 | * All Rights Reserved. | |
| 5 | */ | |
| 6 | ||
| 7 | #include "platform_defs.h" | |
| 8 | #include "command.h" | |
| 9 | #include "input.h" | |
| 10 | ||
| 11 | cmdinfo_t *cmdtab; | |
| 12 | int ncmds; | |
| 13 | ||
| 14 | static iterfunc_t iter_func; | |
| 15 | static checkfunc_t check_func; | |
| 16 | ||
| 17 | struct cmdline { | |
| 18 | char *cmdline; | |
| 19 | bool iterate; | |
| 20 | }; | |
| 21 | ||
| 22 | static int ncmdline; | |
| 23 | static struct cmdline *cmdline; | |
| 24 | ||
| 25 | static int | |
| 26 | compare(const void *a, const void *b) | |
| 27 | { | |
| 28 | return strcmp(((const cmdinfo_t *)a)->name, | |
| 29 | ((const cmdinfo_t *)b)->name); | |
| 30 | } | |
| 31 | ||
| 32 | void | |
| 33 | add_command( | |
| 34 | const cmdinfo_t *ci) | |
| 35 | { | |
| 36 | cmdtab = realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab)); | |
| 37 | if (!cmdtab) { | |
| 38 | perror(_("adding libxcmd command")); | |
| 39 | exit(1); | |
| 40 | } | |
| 41 | cmdtab[ncmds - 1] = *ci; | |
| 42 | qsort(cmdtab, ncmds, sizeof(*cmdtab), compare); | |
| 43 | } | |
| 44 | ||
| 45 | static int | |
| 46 | check_command( | |
| 47 | const cmdinfo_t *ci) | |
| 48 | { | |
| 49 | /* always run internal library supplied commands */ | |
| 50 | if (ci->flags & CMD_FLAG_LIBRARY) | |
| 51 | return 1; | |
| 52 | ||
| 53 | if (check_func) | |
| 54 | return check_func(ci); | |
| 55 | return 1; | |
| 56 | } | |
| 57 | ||
| 58 | void | |
| 59 | add_check_command( | |
| 60 | checkfunc_t cf) | |
| 61 | { | |
| 62 | check_func = cf; | |
| 63 | } | |
| 64 | ||
| 65 | int | |
| 66 | command_usage( | |
| 67 | const cmdinfo_t *ci) | |
| 68 | { | |
| 69 | printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); | |
| 70 | return 0; | |
| 71 | } | |
| 72 | ||
| 73 | int | |
| 74 | command( | |
| 75 | const cmdinfo_t *ct, | |
| 76 | int argc, | |
| 77 | char **argv) | |
| 78 | { | |
| 79 | char *cmd = argv[0]; | |
| 80 | ||
| 81 | if (!check_command(ct)) | |
| 82 | return 0; | |
| 83 | ||
| 84 | if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) { | |
| 85 | if (ct->argmax == -1) | |
| 86 | fprintf(stderr, | |
| 87 | _("bad argument count %d to %s, expected at least %d arguments\n"), | |
| 88 | argc-1, cmd, ct->argmin); | |
| 89 | else if (ct->argmin == ct->argmax) | |
| 90 | fprintf(stderr, | |
| 91 | _("bad argument count %d to %s, expected %d arguments\n"), | |
| 92 | argc-1, cmd, ct->argmin); | |
| 93 | else | |
| 94 | fprintf(stderr, | |
| 95 | _("bad argument count %d to %s, expected between %d and %d arguments\n"), | |
| 96 | argc-1, cmd, ct->argmin, ct->argmax); | |
| 97 | return 0; | |
| 98 | } | |
| 99 | platform_getoptreset(); | |
| 100 | return ct->cfunc(argc, argv); | |
| 101 | } | |
| 102 | ||
| 103 | const cmdinfo_t * | |
| 104 | find_command( | |
| 105 | const char *cmd) | |
| 106 | { | |
| 107 | cmdinfo_t *ct; | |
| 108 | ||
| 109 | for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) { | |
| 110 | if (strcmp(ct->name, cmd) == 0 || | |
| 111 | (ct->altname && strcmp(ct->altname, cmd) == 0)) | |
| 112 | return (const cmdinfo_t *)ct; | |
| 113 | } | |
| 114 | return NULL; | |
| 115 | } | |
| 116 | ||
| 117 | void | |
| 118 | add_user_command(char *optarg) | |
| 119 | { | |
| 120 | ncmdline++; | |
| 121 | cmdline = realloc(cmdline, sizeof(struct cmdline) * (ncmdline)); | |
| 122 | if (!cmdline) { | |
| 123 | perror("realloc"); | |
| 124 | exit(1); | |
| 125 | } | |
| 126 | cmdline[ncmdline-1].cmdline = optarg; | |
| 127 | cmdline[ncmdline-1].iterate = true; | |
| 128 | ||
| 129 | } | |
| 130 | ||
| 131 | void | |
| 132 | add_oneshot_user_command(char *optarg) | |
| 133 | { | |
| 134 | ncmdline++; | |
| 135 | cmdline = realloc(cmdline, sizeof(struct cmdline) * (ncmdline)); | |
| 136 | if (!cmdline) { | |
| 137 | perror("realloc"); | |
| 138 | exit(1); | |
| 139 | } | |
| 140 | cmdline[ncmdline-1].cmdline = optarg; | |
| 141 | cmdline[ncmdline-1].iterate = false; | |
| 142 | } | |
| 143 | ||
| 144 | /* | |
| 145 | * Run a command, iterating as necessary. Return 0 for success, non-zero | |
| 146 | * if an error occurred. Errors terminate loop iteration immediately. | |
| 147 | */ | |
| 148 | static int | |
| 149 | iterate_command( | |
| 150 | const cmdinfo_t *ct, | |
| 151 | int argc, | |
| 152 | char **argv) | |
| 153 | { | |
| 154 | int error = 0; | |
| 155 | int j; | |
| 156 | ||
| 157 | /* if there's nothing to iterate, we're done! */ | |
| 158 | if (!iter_func) | |
| 159 | return 0; | |
| 160 | ||
| 161 | for (j = iter_func(0); j; j = iter_func(j)) { | |
| 162 | error = command(ct, argc, argv); | |
| 163 | if (error) | |
| 164 | break; | |
| 165 | ||
| 166 | } | |
| 167 | ||
| 168 | return error; | |
| 169 | } | |
| 170 | ||
| 171 | void | |
| 172 | add_command_iterator( | |
| 173 | iterfunc_t func) | |
| 174 | { | |
| 175 | iter_func = func; | |
| 176 | } | |
| 177 | ||
| 178 | static int | |
| 179 | process_input( | |
| 180 | char *input, | |
| 181 | bool iterate) | |
| 182 | { | |
| 183 | char **v; | |
| 184 | const cmdinfo_t *ct; | |
| 185 | int c = 0; | |
| 186 | int error = 0; | |
| 187 | ||
| 188 | v = breakline(input, &c); | |
| 189 | if (!c) | |
| 190 | goto out; | |
| 191 | ||
| 192 | ct = find_command(v[0]); | |
| 193 | if (!ct) { | |
| 194 | fprintf(stderr, _("command \"%s\" not found\n"), v[0]); | |
| 195 | goto out; | |
| 196 | } | |
| 197 | ||
| 198 | /* oneshot commands don't iterate */ | |
| 199 | if (!iterate || (ct->flags & CMD_FLAG_ONESHOT)) | |
| 200 | error = command(ct, c, v); | |
| 201 | else | |
| 202 | error = iterate_command(ct, c, v); | |
| 203 | out: | |
| 204 | doneline(input, v); | |
| 205 | return error; | |
| 206 | } | |
| 207 | ||
| 208 | void | |
| 209 | command_loop(void) | |
| 210 | { | |
| 211 | char *input; | |
| 212 | int done = 0; | |
| 213 | int i; | |
| 214 | ||
| 215 | if (!cmdline) { | |
| 216 | /* interactive mode */ | |
| 217 | while (!done) { | |
| 218 | input = fetchline(); | |
| 219 | if (!input) | |
| 220 | break; | |
| 221 | done = process_input(input, false); | |
| 222 | } | |
| 223 | return; | |
| 224 | } | |
| 225 | ||
| 226 | /* command line mode */ | |
| 227 | for (i = 0; !done && i < ncmdline; i++) { | |
| 228 | input = strdup(cmdline[i].cmdline); | |
| 229 | if (!input) { | |
| 230 | fprintf(stderr, | |
| 231 | _("cannot strdup command '%s': %s\n"), | |
| 232 | cmdline[i].cmdline, strerror(errno)); | |
| 233 | exit(1); | |
| 234 | } | |
| 235 | done = process_input(input, cmdline[i].iterate); | |
| 236 | } | |
| 237 | free(cmdline); | |
| 238 | return; | |
| 239 | } | |
| 240 | ||
| 241 | void | |
| 242 | report_io_times( | |
| 243 | const char *verb, | |
| 244 | struct timeval *t2, | |
| 245 | long long offset, | |
| 246 | long long count, | |
| 247 | long long total, | |
| 248 | int ops, | |
| 249 | int compact) | |
| 250 | { | |
| 251 | char s1[64], s2[64], ts[64]; | |
| 252 | ||
| 253 | timestr(t2, ts, sizeof(ts), compact ? VERBOSE_FIXED_TIME : 0); | |
| 254 | if (!compact) { | |
| 255 | cvtstr((double)total, s1, sizeof(s1)); | |
| 256 | cvtstr(tdiv((double)total, *t2), s2, sizeof(s2)); | |
| 257 | printf(_("%s %lld/%lld bytes at offset %lld\n"), | |
| 258 | verb, total, count, (long long)offset); | |
| 259 | printf(_("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n"), | |
| 260 | s1, ops, ts, s2, tdiv((double)ops, *t2)); | |
| 261 | } else {/* bytes,ops,time,bytes/sec,ops/sec */ | |
| 262 | printf("%lld,%d,%s,%.3f,%.3f\n", | |
| 263 | total, ops, ts, | |
| 264 | tdiv((double)total, *t2), tdiv((double)ops, *t2)); | |
| 265 | } | |
| 266 | } |