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