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