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