1 /* $OpenBSD: arguments.c,v 1.57 2022/12/16 08:13:40 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <vis.h> 25 26 #include "tmux.h" 27 28 /* 29 * Manipulate command arguments. 30 */ 31 32 /* List of argument values. */ 33 TAILQ_HEAD(args_values, args_value); 34 35 /* Single arguments flag. */ 36 struct args_entry { 37 u_char flag; 38 struct args_values values; 39 u_int count; 40 41 int flags; 42 #define ARGS_ENTRY_OPTIONAL_VALUE 0x1 43 44 RB_ENTRY(args_entry) entry; 45 }; 46 47 /* Parsed argument flags and values. */ 48 struct args { 49 struct args_tree tree; 50 u_int count; 51 struct args_value *values; 52 }; 53 54 /* Prepared command state. */ 55 struct args_command_state { 56 struct cmd_list *cmdlist; 57 char *cmd; 58 struct cmd_parse_input pi; 59 }; 60 61 static struct args_entry *args_find(struct args *, u_char); 62 63 static int args_cmp(struct args_entry *, struct args_entry *); 64 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); 65 66 /* Arguments tree comparison function. */ 67 static int 68 args_cmp(struct args_entry *a1, struct args_entry *a2) 69 { 70 return (a1->flag - a2->flag); 71 } 72 73 /* Find a flag in the arguments tree. */ 74 static struct args_entry * 75 args_find(struct args *args, u_char flag) 76 { 77 struct args_entry entry; 78 79 entry.flag = flag; 80 return (RB_FIND(args_tree, &args->tree, &entry)); 81 } 82 83 /* Copy value. */ 84 static void 85 args_copy_value(struct args_value *to, struct args_value *from) 86 { 87 to->type = from->type; 88 switch (from->type) { 89 case ARGS_NONE: 90 break; 91 case ARGS_COMMANDS: 92 to->cmdlist = from->cmdlist; 93 to->cmdlist->references++; 94 break; 95 case ARGS_STRING: 96 to->string = xstrdup(from->string); 97 break; 98 } 99 } 100 101 /* Get value as string. */ 102 static const char * 103 args_value_as_string(struct args_value *value) 104 { 105 switch (value->type) { 106 case ARGS_NONE: 107 return (""); 108 case ARGS_COMMANDS: 109 if (value->cached == NULL) 110 value->cached = cmd_list_print(value->cmdlist, 0); 111 return (value->cached); 112 case ARGS_STRING: 113 return (value->string); 114 } 115 fatalx("unexpected argument type"); 116 } 117 118 /* Create an empty arguments set. */ 119 struct args * 120 args_create(void) 121 { 122 struct args *args; 123 124 args = xcalloc(1, sizeof *args); 125 RB_INIT(&args->tree); 126 return (args); 127 } 128 129 /* Parse a single flag. */ 130 static int 131 args_parse_flag_argument(struct args_value *values, u_int count, char **cause, 132 struct args *args, u_int *i, const char *string, int flag, 133 int optional_argument) 134 { 135 struct args_value *argument, *new; 136 const char *s; 137 138 new = xcalloc(1, sizeof *new); 139 if (*string != '\0') { 140 new->type = ARGS_STRING; 141 new->string = xstrdup(string); 142 goto out; 143 } 144 145 if (*i == count) 146 argument = NULL; 147 else { 148 argument = &values[*i]; 149 if (argument->type != ARGS_STRING) { 150 xasprintf(cause, "-%c argument must be a string", flag); 151 return (-1); 152 } 153 if (argument->string[0] == '-') 154 argument = NULL; 155 } 156 if (argument == NULL) { 157 if (optional_argument) { 158 log_debug("%s: -%c (optional)", __func__, flag); 159 args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE); 160 return (0); /* either - or end */ 161 } 162 xasprintf(cause, "-%c expects an argument", flag); 163 return (-1); 164 } 165 args_copy_value(new, argument); 166 (*i)++; 167 168 out: 169 s = args_value_as_string(new); 170 log_debug("%s: -%c = %s", __func__, flag, s); 171 args_set(args, flag, new, 0); 172 return (0); 173 } 174 175 /* Parse flags argument. */ 176 static int 177 args_parse_flags(const struct args_parse *parse, struct args_value *values, 178 u_int count, char **cause, struct args *args, int *i) 179 { 180 struct args_value *value; 181 u_char flag; 182 const char *found, *string; 183 int optional_argument; 184 185 value = &values[*i]; 186 if (value->type != ARGS_STRING) 187 return (1); 188 189 string = value->string; 190 log_debug("%s: next %s", __func__, string); 191 if (*string++ != '-' || *string == '\0') 192 return (1); 193 (*i)++; 194 if (string[0] == '-' && string[1] == '\0') 195 return (1); 196 197 for (;;) { 198 flag = *string++; 199 if (flag == '\0') 200 return (0); 201 if (flag == '?') 202 return (-1); 203 if (!isalnum(flag)) { 204 xasprintf(cause, "invalid flag -%c", flag); 205 return (-1); 206 } 207 208 found = strchr(parse->template, flag); 209 if (found == NULL) { 210 xasprintf(cause, "unknown flag -%c", flag); 211 return (-1); 212 } 213 if (*++found != ':') { 214 log_debug("%s: -%c", __func__, flag); 215 args_set(args, flag, NULL, 0); 216 continue; 217 } 218 optional_argument = (*found == ':'); 219 return (args_parse_flag_argument(values, count, cause, args, i, 220 string, flag, optional_argument)); 221 } 222 } 223 224 /* Parse arguments into a new argument set. */ 225 struct args * 226 args_parse(const struct args_parse *parse, struct args_value *values, 227 u_int count, char **cause) 228 { 229 struct args *args; 230 u_int i; 231 enum args_parse_type type; 232 struct args_value *value, *new; 233 const char *s; 234 int stop; 235 236 if (count == 0) 237 return (args_create()); 238 239 args = args_create(); 240 for (i = 1; i < count; /* nothing */) { 241 stop = args_parse_flags(parse, values, count, cause, args, &i); 242 if (stop == -1) { 243 args_free(args); 244 return (NULL); 245 } 246 if (stop == 1) 247 break; 248 } 249 log_debug("%s: flags end at %u of %u", __func__, i, count); 250 if (i != count) { 251 for (/* nothing */; i < count; i++) { 252 value = &values[i]; 253 254 s = args_value_as_string(value); 255 log_debug("%s: %u = %s (type %d)", __func__, i, s, 256 value->type); 257 258 if (parse->cb != NULL) { 259 type = parse->cb(args, args->count, cause); 260 if (type == ARGS_PARSE_INVALID) { 261 args_free(args); 262 return (NULL); 263 } 264 } else 265 type = ARGS_PARSE_STRING; 266 267 args->values = xrecallocarray(args->values, 268 args->count, args->count + 1, sizeof *args->values); 269 new = &args->values[args->count++]; 270 271 switch (type) { 272 case ARGS_PARSE_INVALID: 273 fatalx("unexpected argument type"); 274 case ARGS_PARSE_STRING: 275 if (value->type != ARGS_STRING) { 276 xasprintf(cause, 277 "argument %u must be \"string\"", 278 args->count); 279 args_free(args); 280 return (NULL); 281 } 282 args_copy_value(new, value); 283 break; 284 case ARGS_PARSE_COMMANDS_OR_STRING: 285 args_copy_value(new, value); 286 break; 287 case ARGS_PARSE_COMMANDS: 288 if (value->type != ARGS_COMMANDS) { 289 xasprintf(cause, 290 "argument %u must be { commands }", 291 args->count); 292 args_free(args); 293 return (NULL); 294 } 295 args_copy_value(new, value); 296 break; 297 } 298 } 299 } 300 301 if (parse->lower != -1 && args->count < (u_int)parse->lower) { 302 xasprintf(cause, 303 "too few arguments (need at least %u)", 304 parse->lower); 305 args_free(args); 306 return (NULL); 307 } 308 if (parse->upper != -1 && args->count > (u_int)parse->upper) { 309 xasprintf(cause, 310 "too many arguments (need at most %u)", 311 parse->upper); 312 args_free(args); 313 return (NULL); 314 } 315 return (args); 316 } 317 318 /* Copy and expand a value. */ 319 static void 320 args_copy_copy_value(struct args_value *to, struct args_value *from, int argc, 321 char **argv) 322 { 323 char *s, *expanded; 324 int i; 325 326 to->type = from->type; 327 switch (from->type) { 328 case ARGS_NONE: 329 break; 330 case ARGS_STRING: 331 expanded = xstrdup(from->string); 332 for (i = 0; i < argc; i++) { 333 s = cmd_template_replace(expanded, argv[i], i + 1); 334 free(expanded); 335 expanded = s; 336 } 337 to->string = expanded; 338 break; 339 case ARGS_COMMANDS: 340 to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv); 341 break; 342 } 343 } 344 345 /* Copy an arguments set. */ 346 struct args * 347 args_copy(struct args *args, int argc, char **argv) 348 { 349 struct args *new_args; 350 struct args_entry *entry; 351 struct args_value *value, *new_value; 352 u_int i; 353 354 cmd_log_argv(argc, argv, "%s", __func__); 355 356 new_args = args_create(); 357 RB_FOREACH(entry, args_tree, &args->tree) { 358 if (TAILQ_EMPTY(&entry->values)) { 359 for (i = 0; i < entry->count; i++) 360 args_set(new_args, entry->flag, NULL, 0); 361 continue; 362 } 363 TAILQ_FOREACH(value, &entry->values, entry) { 364 new_value = xcalloc(1, sizeof *new_value); 365 args_copy_copy_value(new_value, value, argc, argv); 366 args_set(new_args, entry->flag, new_value, 0); 367 } 368 } 369 if (args->count == 0) 370 return (new_args); 371 new_args->count = args->count; 372 new_args->values = xcalloc(args->count, sizeof *new_args->values); 373 for (i = 0; i < args->count; i++) { 374 new_value = &new_args->values[i]; 375 args_copy_copy_value(new_value, &args->values[i], argc, argv); 376 } 377 return (new_args); 378 } 379 380 /* Free a value. */ 381 void 382 args_free_value(struct args_value *value) 383 { 384 switch (value->type) { 385 case ARGS_NONE: 386 break; 387 case ARGS_STRING: 388 free(value->string); 389 break; 390 case ARGS_COMMANDS: 391 cmd_list_free(value->cmdlist); 392 break; 393 } 394 free(value->cached); 395 } 396 397 /* Free values. */ 398 void 399 args_free_values(struct args_value *values, u_int count) 400 { 401 u_int i; 402 403 for (i = 0; i < count; i++) 404 args_free_value(&values[i]); 405 } 406 407 /* Free an arguments set. */ 408 void 409 args_free(struct args *args) 410 { 411 struct args_entry *entry; 412 struct args_entry *entry1; 413 struct args_value *value; 414 struct args_value *value1; 415 416 args_free_values(args->values, args->count); 417 free(args->values); 418 419 RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { 420 RB_REMOVE(args_tree, &args->tree, entry); 421 TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { 422 TAILQ_REMOVE(&entry->values, value, entry); 423 args_free_value(value); 424 free(value); 425 } 426 free(entry); 427 } 428 429 free(args); 430 } 431 432 /* Convert arguments to vector. */ 433 void 434 args_to_vector(struct args *args, int *argc, char ***argv) 435 { 436 char *s; 437 u_int i; 438 439 *argc = 0; 440 *argv = NULL; 441 442 for (i = 0; i < args->count; i++) { 443 switch (args->values[i].type) { 444 case ARGS_NONE: 445 break; 446 case ARGS_STRING: 447 cmd_append_argv(argc, argv, args->values[i].string); 448 break; 449 case ARGS_COMMANDS: 450 s = cmd_list_print(args->values[i].cmdlist, 0); 451 cmd_append_argv(argc, argv, s); 452 free(s); 453 break; 454 } 455 } 456 } 457 458 /* Convert arguments from vector. */ 459 struct args_value * 460 args_from_vector(int argc, char **argv) 461 { 462 struct args_value *values; 463 int i; 464 465 values = xcalloc(argc, sizeof *values); 466 for (i = 0; i < argc; i++) { 467 values[i].type = ARGS_STRING; 468 values[i].string = xstrdup(argv[i]); 469 } 470 return (values); 471 } 472 473 /* Add to string. */ 474 static void printflike(3, 4) 475 args_print_add(char **buf, size_t *len, const char *fmt, ...) 476 { 477 va_list ap; 478 char *s; 479 size_t slen; 480 481 va_start(ap, fmt); 482 slen = xvasprintf(&s, fmt, ap); 483 va_end(ap); 484 485 *len += slen; 486 *buf = xrealloc(*buf, *len); 487 488 strlcat(*buf, s, *len); 489 free(s); 490 } 491 492 /* Add value to string. */ 493 static void 494 args_print_add_value(char **buf, size_t *len, struct args_value *value) 495 { 496 char *expanded = NULL; 497 498 if (**buf != '\0') 499 args_print_add(buf, len, " "); 500 501 switch (value->type) { 502 case ARGS_NONE: 503 break; 504 case ARGS_COMMANDS: 505 expanded = cmd_list_print(value->cmdlist, 0); 506 args_print_add(buf, len, "{ %s }", expanded); 507 break; 508 case ARGS_STRING: 509 expanded = args_escape(value->string); 510 args_print_add(buf, len, "%s", expanded); 511 break; 512 } 513 free(expanded); 514 } 515 516 /* Print a set of arguments. */ 517 char * 518 args_print(struct args *args) 519 { 520 size_t len; 521 char *buf; 522 u_int i, j; 523 struct args_entry *entry; 524 struct args_entry *last = NULL; 525 struct args_value *value; 526 527 len = 1; 528 buf = xcalloc(1, len); 529 530 /* Process the flags first. */ 531 RB_FOREACH(entry, args_tree, &args->tree) { 532 if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) 533 continue; 534 if (!TAILQ_EMPTY(&entry->values)) 535 continue; 536 537 if (*buf == '\0') 538 args_print_add(&buf, &len, "-"); 539 for (j = 0; j < entry->count; j++) 540 args_print_add(&buf, &len, "%c", entry->flag); 541 } 542 543 /* Then the flags with arguments. */ 544 RB_FOREACH(entry, args_tree, &args->tree) { 545 if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) { 546 if (*buf != '\0') 547 args_print_add(&buf, &len, " -%c", entry->flag); 548 else 549 args_print_add(&buf, &len, "-%c", entry->flag); 550 last = entry; 551 continue; 552 } 553 if (TAILQ_EMPTY(&entry->values)) 554 continue; 555 TAILQ_FOREACH(value, &entry->values, entry) { 556 if (*buf != '\0') 557 args_print_add(&buf, &len, " -%c", entry->flag); 558 else 559 args_print_add(&buf, &len, "-%c", entry->flag); 560 args_print_add_value(&buf, &len, value); 561 } 562 last = entry; 563 } 564 if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE)) 565 args_print_add(&buf, &len, " --"); 566 567 /* And finally the argument vector. */ 568 for (i = 0; i < args->count; i++) 569 args_print_add_value(&buf, &len, &args->values[i]); 570 571 return (buf); 572 } 573 574 /* Escape an argument. */ 575 char * 576 args_escape(const char *s) 577 { 578 static const char dquoted[] = " #';${}%"; 579 static const char squoted[] = " \""; 580 char *escaped, *result; 581 int flags, quotes = 0; 582 583 if (*s == '\0') { 584 xasprintf(&result, "''"); 585 return (result); 586 } 587 if (s[strcspn(s, dquoted)] != '\0') 588 quotes = '"'; 589 else if (s[strcspn(s, squoted)] != '\0') 590 quotes = '\''; 591 592 if (s[0] != ' ' && 593 s[1] == '\0' && 594 (quotes != 0 || s[0] == '~')) { 595 xasprintf(&escaped, "\\%c", s[0]); 596 return (escaped); 597 } 598 599 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 600 if (quotes == '"') 601 flags |= VIS_DQ; 602 utf8_stravis(&escaped, s, flags); 603 604 if (quotes == '\'') 605 xasprintf(&result, "'%s'", escaped); 606 else if (quotes == '"') { 607 if (*escaped == '~') 608 xasprintf(&result, "\"\\%s\"", escaped); 609 else 610 xasprintf(&result, "\"%s\"", escaped); 611 } else { 612 if (*escaped == '~') 613 xasprintf(&result, "\\%s", escaped); 614 else 615 result = xstrdup(escaped); 616 } 617 free(escaped); 618 return (result); 619 } 620 621 /* Return if an argument is present. */ 622 int 623 args_has(struct args *args, u_char flag) 624 { 625 struct args_entry *entry; 626 627 entry = args_find(args, flag); 628 if (entry == NULL) 629 return (0); 630 return (entry->count); 631 } 632 633 /* Set argument value in the arguments tree. */ 634 void 635 args_set(struct args *args, u_char flag, struct args_value *value, int flags) 636 { 637 struct args_entry *entry; 638 639 entry = args_find(args, flag); 640 if (entry == NULL) { 641 entry = xcalloc(1, sizeof *entry); 642 entry->flag = flag; 643 entry->count = 1; 644 entry->flags = flags; 645 TAILQ_INIT(&entry->values); 646 RB_INSERT(args_tree, &args->tree, entry); 647 } else 648 entry->count++; 649 if (value != NULL && value->type != ARGS_NONE) 650 TAILQ_INSERT_TAIL(&entry->values, value, entry); 651 } 652 653 /* Get argument value. Will be NULL if it isn't present. */ 654 const char * 655 args_get(struct args *args, u_char flag) 656 { 657 struct args_entry *entry; 658 659 if ((entry = args_find(args, flag)) == NULL) 660 return (NULL); 661 if (TAILQ_EMPTY(&entry->values)) 662 return (NULL); 663 return (TAILQ_LAST(&entry->values, args_values)->string); 664 } 665 666 /* Get first argument. */ 667 u_char 668 args_first(struct args *args, struct args_entry **entry) 669 { 670 *entry = RB_MIN(args_tree, &args->tree); 671 if (*entry == NULL) 672 return (0); 673 return ((*entry)->flag); 674 } 675 676 /* Get next argument. */ 677 u_char 678 args_next(struct args_entry **entry) 679 { 680 *entry = RB_NEXT(args_tree, &args->tree, *entry); 681 if (*entry == NULL) 682 return (0); 683 return ((*entry)->flag); 684 } 685 686 /* Get argument count. */ 687 u_int 688 args_count(struct args *args) 689 { 690 return (args->count); 691 } 692 693 /* Get argument values. */ 694 struct args_value * 695 args_values(struct args *args) 696 { 697 return (args->values); 698 } 699 700 /* Get argument value. */ 701 struct args_value * 702 args_value(struct args *args, u_int idx) 703 { 704 if (idx >= args->count) 705 return (NULL); 706 return (&args->values[idx]); 707 } 708 709 /* Return argument as string. */ 710 const char * 711 args_string(struct args *args, u_int idx) 712 { 713 if (idx >= args->count) 714 return (NULL); 715 return (args_value_as_string(&args->values[idx])); 716 } 717 718 /* Make a command now. */ 719 struct cmd_list * 720 args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx, 721 int expand) 722 { 723 struct args_command_state *state; 724 char *error; 725 struct cmd_list *cmdlist; 726 727 state = args_make_commands_prepare(self, item, idx, NULL, 0, expand); 728 cmdlist = args_make_commands(state, 0, NULL, &error); 729 if (cmdlist == NULL) { 730 cmdq_error(item, "%s", error); 731 free(error); 732 } 733 else 734 cmdlist->references++; 735 args_make_commands_free(state); 736 return (cmdlist); 737 } 738 739 /* Save bits to make a command later. */ 740 struct args_command_state * 741 args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx, 742 const char *default_command, int wait, int expand) 743 { 744 struct args *args = cmd_get_args(self); 745 struct cmd_find_state *target = cmdq_get_target(item); 746 struct client *tc = cmdq_get_target_client(item); 747 struct args_value *value; 748 struct args_command_state *state; 749 const char *cmd; 750 751 state = xcalloc(1, sizeof *state); 752 753 if (idx < args->count) { 754 value = &args->values[idx]; 755 if (value->type == ARGS_COMMANDS) { 756 state->cmdlist = value->cmdlist; 757 state->cmdlist->references++; 758 return (state); 759 } 760 cmd = value->string; 761 } else { 762 if (default_command == NULL) 763 fatalx("argument out of range"); 764 cmd = default_command; 765 } 766 767 768 if (expand) 769 state->cmd = format_single_from_target(item, cmd); 770 else 771 state->cmd = xstrdup(cmd); 772 log_debug("%s: %s", __func__, state->cmd); 773 774 if (wait) 775 state->pi.item = item; 776 cmd_get_source(self, &state->pi.file, &state->pi.line); 777 state->pi.c = tc; 778 if (state->pi.c != NULL) 779 state->pi.c->references++; 780 cmd_find_copy_state(&state->pi.fs, target); 781 782 return (state); 783 } 784 785 /* Return argument as command. */ 786 struct cmd_list * 787 args_make_commands(struct args_command_state *state, int argc, char **argv, 788 char **error) 789 { 790 struct cmd_parse_result *pr; 791 char *cmd, *new_cmd; 792 int i; 793 794 if (state->cmdlist != NULL) { 795 if (argc == 0) 796 return (state->cmdlist); 797 return (cmd_list_copy(state->cmdlist, argc, argv)); 798 } 799 800 cmd = xstrdup(state->cmd); 801 for (i = 0; i < argc; i++) { 802 new_cmd = cmd_template_replace(cmd, argv[i], i + 1); 803 log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); 804 free(cmd); 805 cmd = new_cmd; 806 } 807 log_debug("%s: %s", __func__, cmd); 808 809 pr = cmd_parse_from_string(cmd, &state->pi); 810 free(cmd); 811 switch (pr->status) { 812 case CMD_PARSE_ERROR: 813 *error = pr->error; 814 return (NULL); 815 case CMD_PARSE_SUCCESS: 816 return (pr->cmdlist); 817 } 818 fatalx("invalid parse return state"); 819 } 820 821 /* Free commands state. */ 822 void 823 args_make_commands_free(struct args_command_state *state) 824 { 825 if (state->cmdlist != NULL) 826 cmd_list_free(state->cmdlist); 827 if (state->pi.c != NULL) 828 server_client_unref(state->pi.c); 829 free(state->cmd); 830 free(state); 831 } 832 833 /* Get prepared command. */ 834 char * 835 args_make_commands_get_command(struct args_command_state *state) 836 { 837 struct cmd *first; 838 int n; 839 char *s; 840 841 if (state->cmdlist != NULL) { 842 first = cmd_list_first(state->cmdlist); 843 if (first == NULL) 844 return (xstrdup("")); 845 return (xstrdup(cmd_get_entry(first)->name)); 846 } 847 n = strcspn(state->cmd, " ,"); 848 xasprintf(&s, "%.*s", n, state->cmd); 849 return (s); 850 } 851 852 /* Get first value in argument. */ 853 struct args_value * 854 args_first_value(struct args *args, u_char flag) 855 { 856 struct args_entry *entry; 857 858 if ((entry = args_find(args, flag)) == NULL) 859 return (NULL); 860 return (TAILQ_FIRST(&entry->values)); 861 } 862 863 /* Get next value in argument. */ 864 struct args_value * 865 args_next_value(struct args_value *value) 866 { 867 return (TAILQ_NEXT(value, entry)); 868 } 869 870 /* Convert an argument value to a number. */ 871 long long 872 args_strtonum(struct args *args, u_char flag, long long minval, 873 long long maxval, char **cause) 874 { 875 const char *errstr; 876 long long ll; 877 struct args_entry *entry; 878 struct args_value *value; 879 880 if ((entry = args_find(args, flag)) == NULL) { 881 *cause = xstrdup("missing"); 882 return (0); 883 } 884 value = TAILQ_LAST(&entry->values, args_values); 885 if (value == NULL || 886 value->type != ARGS_STRING || 887 value->string == NULL) { 888 *cause = xstrdup("missing"); 889 return (0); 890 } 891 892 ll = strtonum(value->string, minval, maxval, &errstr); 893 if (errstr != NULL) { 894 *cause = xstrdup(errstr); 895 return (0); 896 } 897 898 *cause = NULL; 899 return (ll); 900 } 901 902 /* Convert an argument value to a number, and expand formats. */ 903 long long 904 args_strtonum_and_expand(struct args *args, u_char flag, long long minval, 905 long long maxval, struct cmdq_item *item, char **cause) 906 { 907 const char *errstr; 908 char *formatted; 909 long long ll; 910 struct args_entry *entry; 911 struct args_value *value; 912 913 if ((entry = args_find(args, flag)) == NULL) { 914 *cause = xstrdup("missing"); 915 return (0); 916 } 917 value = TAILQ_LAST(&entry->values, args_values); 918 if (value == NULL || 919 value->type != ARGS_STRING || 920 value->string == NULL) { 921 *cause = xstrdup("missing"); 922 return (0); 923 } 924 925 formatted = format_single_from_target(item, value->string); 926 ll = strtonum(formatted, minval, maxval, &errstr); 927 free(formatted); 928 if (errstr != NULL) { 929 *cause = xstrdup(errstr); 930 return (0); 931 } 932 933 *cause = NULL; 934 return (ll); 935 } 936 937 /* Convert an argument to a number which may be a percentage. */ 938 long long 939 args_percentage(struct args *args, u_char flag, long long minval, 940 long long maxval, long long curval, char **cause) 941 { 942 const char *value; 943 struct args_entry *entry; 944 945 if ((entry = args_find(args, flag)) == NULL) { 946 *cause = xstrdup("missing"); 947 return (0); 948 } 949 if (TAILQ_EMPTY(&entry->values)) { 950 *cause = xstrdup("empty"); 951 return (0); 952 } 953 value = TAILQ_LAST(&entry->values, args_values)->string; 954 return (args_string_percentage(value, minval, maxval, curval, cause)); 955 } 956 957 /* Convert a string to a number which may be a percentage. */ 958 long long 959 args_string_percentage(const char *value, long long minval, long long maxval, 960 long long curval, char **cause) 961 { 962 const char *errstr; 963 long long ll; 964 size_t valuelen = strlen(value); 965 char *copy; 966 967 if (valuelen == 0) { 968 *cause = xstrdup("empty"); 969 return (0); 970 } 971 if (value[valuelen - 1] == '%') { 972 copy = xstrdup(value); 973 copy[valuelen - 1] = '\0'; 974 975 ll = strtonum(copy, 0, 100, &errstr); 976 free(copy); 977 if (errstr != NULL) { 978 *cause = xstrdup(errstr); 979 return (0); 980 } 981 ll = (curval * ll) / 100; 982 if (ll < minval) { 983 *cause = xstrdup("too small"); 984 return (0); 985 } 986 if (ll > maxval) { 987 *cause = xstrdup("too large"); 988 return (0); 989 } 990 } else { 991 ll = strtonum(value, minval, maxval, &errstr); 992 if (errstr != NULL) { 993 *cause = xstrdup(errstr); 994 return (0); 995 } 996 } 997 998 *cause = NULL; 999 return (ll); 1000 } 1001 1002 /* 1003 * Convert an argument to a number which may be a percentage, and expand 1004 * formats. 1005 */ 1006 long long 1007 args_percentage_and_expand(struct args *args, u_char flag, long long minval, 1008 long long maxval, long long curval, struct cmdq_item *item, char **cause) 1009 { 1010 const char *value; 1011 struct args_entry *entry; 1012 1013 if ((entry = args_find(args, flag)) == NULL) { 1014 *cause = xstrdup("missing"); 1015 return (0); 1016 } 1017 if (TAILQ_EMPTY(&entry->values)) { 1018 *cause = xstrdup("empty"); 1019 return (0); 1020 } 1021 value = TAILQ_LAST(&entry->values, args_values)->string; 1022 return (args_string_percentage_and_expand(value, minval, maxval, curval, 1023 item, cause)); 1024 } 1025 1026 /* 1027 * Convert a string to a number which may be a percentage, and expand formats. 1028 */ 1029 long long 1030 args_string_percentage_and_expand(const char *value, long long minval, 1031 long long maxval, long long curval, struct cmdq_item *item, char **cause) 1032 { 1033 const char *errstr; 1034 long long ll; 1035 size_t valuelen = strlen(value); 1036 char *copy, *f; 1037 1038 if (value[valuelen - 1] == '%') { 1039 copy = xstrdup(value); 1040 copy[valuelen - 1] = '\0'; 1041 1042 f = format_single_from_target(item, copy); 1043 ll = strtonum(f, 0, 100, &errstr); 1044 free(f); 1045 free(copy); 1046 if (errstr != NULL) { 1047 *cause = xstrdup(errstr); 1048 return (0); 1049 } 1050 ll = (curval * ll) / 100; 1051 if (ll < minval) { 1052 *cause = xstrdup("too small"); 1053 return (0); 1054 } 1055 if (ll > maxval) { 1056 *cause = xstrdup("too large"); 1057 return (0); 1058 } 1059 } else { 1060 f = format_single_from_target(item, value); 1061 ll = strtonum(f, minval, maxval, &errstr); 1062 free(f); 1063 if (errstr != NULL) { 1064 *cause = xstrdup(errstr); 1065 return (0); 1066 } 1067 } 1068 1069 *cause = NULL; 1070 return (ll); 1071 } 1072