Annotation of src/usr.bin/tmux/arguments.c, Revision 1.45
1.45 ! nicm 1: /* $OpenBSD: arguments.c,v 1.44 2021/08/21 20:57:52 nicm Exp $ */
1.1 nicm 2:
3: /*
1.13 nicm 4: * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.42 nicm 21: #include <ctype.h>
1.1 nicm 22: #include <stdlib.h>
23: #include <string.h>
1.16 nicm 24: #include <vis.h>
1.1 nicm 25:
26: #include "tmux.h"
27:
1.5 nicm 28: /*
29: * Manipulate command arguments.
30: */
1.11 nicm 31:
1.45 ! nicm 32: /* List of argument values. */
1.21 nicm 33: TAILQ_HEAD(args_values, args_value);
34:
1.45 ! nicm 35: /* Single arguments flag. */
1.11 nicm 36: struct args_entry {
37: u_char flag;
1.21 nicm 38: struct args_values values;
1.27 nicm 39: u_int count;
1.11 nicm 40: RB_ENTRY(args_entry) entry;
41: };
1.5 nicm 42:
1.45 ! nicm 43: /* Parsed argument flags and values. */
1.38 nicm 44: struct args {
1.44 nicm 45: struct args_tree tree;
1.43 nicm 46: u_int count;
47: struct args_value *values;
1.38 nicm 48: };
49:
1.45 ! nicm 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:
1.14 nicm 57: static struct args_entry *args_find(struct args *, u_char);
1.5 nicm 58:
1.14 nicm 59: static int args_cmp(struct args_entry *, struct args_entry *);
60: RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
1.5 nicm 61:
62: /* Arguments tree comparison function. */
1.14 nicm 63: static int
1.5 nicm 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. */
1.14 nicm 70: static struct args_entry *
1.32 nicm 71: args_find(struct args *args, u_char flag)
1.5 nicm 72: {
73: struct args_entry entry;
74:
1.32 nicm 75: entry.flag = flag;
1.5 nicm 76: return (RB_FIND(args_tree, &args->tree, &entry));
77: }
78:
1.43 nicm 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:
1.42 nicm 97: /* Get value as string. */
1.43 nicm 98: static const char *
1.42 nicm 99: args_value_as_string(struct args_value *value)
100: {
101: switch (value->type) {
102: case ARGS_NONE:
1.43 nicm 103: return ("");
1.42 nicm 104: case ARGS_COMMANDS:
1.43 nicm 105: if (value->cached == NULL)
106: value->cached = cmd_list_print(value->cmdlist, 0);
107: return (value->cached);
1.42 nicm 108: case ARGS_STRING:
1.43 nicm 109: return (value->string);
1.42 nicm 110: }
111: }
112:
1.36 nicm 113: /* Create an empty arguments set. */
114: struct args *
115: args_create(void)
116: {
117: struct args *args;
118:
119: args = xcalloc(1, sizeof *args);
120: RB_INIT(&args->tree);
121: return (args);
122: }
123:
1.42 nicm 124: /* Parse arguments into a new argument set. */
1.1 nicm 125: struct args *
1.42 nicm 126: args_parse(const struct args_parse *parse, struct args_value *values,
127: u_int count)
1.1 nicm 128: {
1.42 nicm 129: struct args *args;
130: u_int i;
1.43 nicm 131: struct args_value *value, *new;
1.42 nicm 132: u_char flag, argument;
1.43 nicm 133: const char *found, *string, *s;
1.1 nicm 134:
1.42 nicm 135: if (count == 0)
136: return (args_create());
1.1 nicm 137:
1.36 nicm 138: args = args_create();
1.42 nicm 139: for (i = 1; i < count; /* nothing */) {
140: value = &values[i];
141: if (value->type != ARGS_STRING)
142: break;
143:
144: string = value->string;
145: if (*string++ != '-' || *string == '\0')
146: break;
147: i++;
148: if (string[0] == '-' && string[1] == '\0')
149: break;
150:
151: for (;;) {
152: flag = *string++;
153: if (flag == '\0')
154: break;
155: if (!isalnum(flag)) {
156: args_free(args);
157: return (NULL);
158: }
159: found = strchr(parse->template, flag);
160: if (found == NULL) {
161: args_free(args);
162: return (NULL);
163: }
164: argument = *++found;
165: if (argument != ':') {
166: log_debug("%s: add -%c", __func__, flag);
167: args_set(args, flag, NULL);
168: continue;
169: }
1.43 nicm 170: new = xcalloc(1, sizeof *value);
171: if (*string != '\0') {
172: new->type = ARGS_STRING;
173: new->string = xstrdup(string);
174: } else {
1.42 nicm 175: if (i == count) {
176: args_free(args);
177: return (NULL);
178: }
1.43 nicm 179: args_copy_value(new, &values[i++]);
1.42 nicm 180: }
1.43 nicm 181: s = args_value_as_string(new);
1.42 nicm 182: log_debug("%s: add -%c = %s", __func__, flag, s);
1.43 nicm 183: args_set(args, flag, new);
1.42 nicm 184: break;
185: }
186: }
187: log_debug("%s: flags end at %u of %u", __func__, i, count);
188: if (i != count) {
189: for (/* nothing */; i < count; i++) {
190: value = &values[i];
191:
192: s = args_value_as_string(value);
193: log_debug("%s: %u = %s", __func__, i, s);
1.43 nicm 194:
195: args->values = xrecallocarray(args->values,
196: args->count, args->count + 1, sizeof *args->values);
197: args_copy_value(&args->values[args->count++], value);
1.1 nicm 198: }
199: }
200:
1.43 nicm 201: if ((parse->lower != -1 && args->count < (u_int)parse->lower) ||
202: (parse->upper != -1 && args->count > (u_int)parse->upper)) {
1.38 nicm 203: args_free(args);
204: return (NULL);
205: }
1.1 nicm 206: return (args);
1.42 nicm 207: }
208:
209: /* Free a value. */
210: void
211: args_free_value(struct args_value *value)
212: {
213: switch (value->type) {
214: case ARGS_NONE:
215: break;
216: case ARGS_STRING:
217: free(value->string);
218: break;
219: case ARGS_COMMANDS:
220: cmd_list_free(value->cmdlist);
221: break;
222: }
1.43 nicm 223: free(value->cached);
1.1 nicm 224: }
225:
226: /* Free an arguments set. */
227: void
228: args_free(struct args *args)
229: {
1.5 nicm 230: struct args_entry *entry;
231: struct args_entry *entry1;
1.21 nicm 232: struct args_value *value;
233: struct args_value *value1;
1.43 nicm 234: u_int i;
1.1 nicm 235:
1.43 nicm 236: for (i = 0; i < args->count; i++)
237: args_free_value(&args->values[i]);
238: free(args->values);
1.1 nicm 239:
1.5 nicm 240: RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
241: RB_REMOVE(args_tree, &args->tree, entry);
1.21 nicm 242: TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
243: TAILQ_REMOVE(&entry->values, value, entry);
1.44 nicm 244: args_free_value(value);
1.21 nicm 245: free(value);
246: }
1.5 nicm 247: free(entry);
248: }
1.1 nicm 249:
1.4 nicm 250: free(args);
1.1 nicm 251: }
252:
1.38 nicm 253: /* Convert arguments to vector. */
254: void
255: args_vector(struct args *args, int *argc, char ***argv)
256: {
1.43 nicm 257: struct args_value *value;
258: u_int i;
259:
260: *argc = 0;
261: *argv = NULL;
262:
263: for (i = 0; i < args->count; i++) {
264: value = &args->values[i];
265: cmd_append_argv(argc, argv, args_value_as_string(value));
266: }
1.38 nicm 267: }
268:
1.12 nicm 269: /* Add to string. */
270: static void printflike(3, 4)
271: args_print_add(char **buf, size_t *len, const char *fmt, ...)
272: {
1.39 nicm 273: va_list ap;
1.12 nicm 274: char *s;
275: size_t slen;
276:
277: va_start(ap, fmt);
278: slen = xvasprintf(&s, fmt, ap);
279: va_end(ap);
280:
281: *len += slen;
282: *buf = xrealloc(*buf, *len);
283:
284: strlcat(*buf, s, *len);
285: free(s);
286: }
287:
1.43 nicm 288: /* Add value to string. */
1.21 nicm 289: static void
1.43 nicm 290: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21 nicm 291: {
1.43 nicm 292: char *expanded = NULL;
1.21 nicm 293:
294: if (**buf != '\0')
295: args_print_add(buf, len, " ");
296:
1.43 nicm 297: switch (value->type) {
298: case ARGS_NONE:
299: break;
300: case ARGS_COMMANDS:
301: expanded = cmd_list_print(value->cmdlist, 0);
302: args_print_add(buf, len, "{ %s }", expanded);
303: break;
304: case ARGS_STRING:
305: expanded = args_escape(value->string);
306: args_print_add(buf, len, "%s", expanded);
307: break;
308: }
309: free(expanded);
1.21 nicm 310: }
311:
1.1 nicm 312: /* Print a set of arguments. */
1.12 nicm 313: char *
314: args_print(struct args *args)
1.1 nicm 315: {
1.39 nicm 316: size_t len;
1.21 nicm 317: char *buf;
1.43 nicm 318: u_int i, j;
1.5 nicm 319: struct args_entry *entry;
1.21 nicm 320: struct args_value *value;
1.1 nicm 321:
1.12 nicm 322: len = 1;
323: buf = xcalloc(1, len);
1.1 nicm 324:
325: /* Process the flags first. */
1.5 nicm 326: RB_FOREACH(entry, args_tree, &args->tree) {
1.21 nicm 327: if (!TAILQ_EMPTY(&entry->values))
1.1 nicm 328: continue;
329:
1.12 nicm 330: if (*buf == '\0')
331: args_print_add(&buf, &len, "-");
1.27 nicm 332: for (j = 0; j < entry->count; j++)
333: args_print_add(&buf, &len, "%c", entry->flag);
1.1 nicm 334: }
335:
336: /* Then the flags with arguments. */
1.5 nicm 337: RB_FOREACH(entry, args_tree, &args->tree) {
1.38 nicm 338: TAILQ_FOREACH(value, &entry->values, entry) {
339: if (*buf != '\0')
340: args_print_add(&buf, &len, " -%c", entry->flag);
341: else
342: args_print_add(&buf, &len, "-%c", entry->flag);
1.43 nicm 343: args_print_add_value(&buf, &len, value);
1.38 nicm 344: }
1.1 nicm 345: }
346:
347: /* And finally the argument vector. */
1.43 nicm 348: for (i = 0; i < args->count; i++)
349: args_print_add_value(&buf, &len, &args->values[i]);
1.1 nicm 350:
1.12 nicm 351: return (buf);
1.22 nicm 352: }
353:
354: /* Escape an argument. */
355: char *
356: args_escape(const char *s)
357: {
1.35 nicm 358: static const char dquoted[] = " #';${}";
359: static const char squoted[] = " \"";
1.22 nicm 360: char *escaped, *result;
1.35 nicm 361: int flags, quotes = 0;
1.22 nicm 362:
1.30 nicm 363: if (*s == '\0') {
364: xasprintf(&result, "''");
365: return (result);
366: }
1.35 nicm 367: if (s[strcspn(s, dquoted)] != '\0')
368: quotes = '"';
369: else if (s[strcspn(s, squoted)] != '\0')
370: quotes = '\'';
371:
1.26 nicm 372: if (s[0] != ' ' &&
1.35 nicm 373: s[1] == '\0' &&
374: (quotes != 0 || s[0] == '~')) {
1.22 nicm 375: xasprintf(&escaped, "\\%c", s[0]);
1.34 nicm 376: return (escaped);
377: }
378:
1.25 nicm 379: flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35 nicm 380: if (quotes == '"')
1.22 nicm 381: flags |= VIS_DQ;
382: utf8_stravis(&escaped, s, flags);
383:
1.35 nicm 384: if (quotes == '\'')
385: xasprintf(&result, "'%s'", escaped);
386: else if (quotes == '"') {
1.22 nicm 387: if (*escaped == '~')
388: xasprintf(&result, "\"\\%s\"", escaped);
389: else
390: xasprintf(&result, "\"%s\"", escaped);
391: } else {
392: if (*escaped == '~')
393: xasprintf(&result, "\\%s", escaped);
394: else
395: result = xstrdup(escaped);
396: }
397: free(escaped);
398: return (result);
1.1 nicm 399: }
400:
401: /* Return if an argument is present. */
402: int
1.32 nicm 403: args_has(struct args *args, u_char flag)
1.1 nicm 404: {
1.27 nicm 405: struct args_entry *entry;
406:
1.32 nicm 407: entry = args_find(args, flag);
1.27 nicm 408: if (entry == NULL)
409: return (0);
410: return (entry->count);
1.1 nicm 411: }
412:
1.5 nicm 413: /* Set argument value in the arguments tree. */
1.19 nicm 414: void
1.43 nicm 415: args_set(struct args *args, u_char flag, struct args_value *value)
1.1 nicm 416: {
1.5 nicm 417: struct args_entry *entry;
418:
1.32 nicm 419: entry = args_find(args, flag);
1.21 nicm 420: if (entry == NULL) {
1.7 nicm 421: entry = xcalloc(1, sizeof *entry);
1.32 nicm 422: entry->flag = flag;
1.27 nicm 423: entry->count = 1;
1.21 nicm 424: TAILQ_INIT(&entry->values);
1.7 nicm 425: RB_INSERT(args_tree, &args->tree, entry);
1.27 nicm 426: } else
427: entry->count++;
1.43 nicm 428: if (value != NULL && value->type != ARGS_NONE)
1.21 nicm 429: TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1 nicm 430: }
431:
432: /* Get argument value. Will be NULL if it isn't present. */
433: const char *
1.32 nicm 434: args_get(struct args *args, u_char flag)
1.1 nicm 435: {
1.5 nicm 436: struct args_entry *entry;
437:
1.32 nicm 438: if ((entry = args_find(args, flag)) == NULL)
439: return (NULL);
440: if (TAILQ_EMPTY(&entry->values))
1.5 nicm 441: return (NULL);
1.41 nicm 442: return (TAILQ_LAST(&entry->values, args_values)->string);
1.21 nicm 443: }
444:
1.32 nicm 445: /* Get first argument. */
446: u_char
447: args_first(struct args *args, struct args_entry **entry)
448: {
449: *entry = RB_MIN(args_tree, &args->tree);
450: if (*entry == NULL)
451: return (0);
452: return ((*entry)->flag);
453: }
454:
455: /* Get next argument. */
456: u_char
457: args_next(struct args_entry **entry)
458: {
459: *entry = RB_NEXT(args_tree, &args->tree, *entry);
460: if (*entry == NULL)
461: return (0);
462: return ((*entry)->flag);
1.38 nicm 463: }
464:
465: /* Get argument count. */
466: u_int
467: args_count(struct args *args)
468: {
1.43 nicm 469: return (args->count);
470: }
471:
472: /* Get argument value. */
473: struct args_value *
474: args_value(struct args *args, u_int idx)
475: {
476: if (idx >= args->count)
477: return (NULL);
478: return (&args->values[idx]);
1.38 nicm 479: }
480:
481: /* Return argument as string. */
482: const char *
483: args_string(struct args *args, u_int idx)
484: {
1.44 nicm 485: if (idx >= args->count)
1.38 nicm 486: return (NULL);
1.43 nicm 487: return (args_value_as_string(&args->values[idx]));
1.45 ! nicm 488: }
! 489:
! 490: /* Make a command now. */
! 491: struct cmd_list *
! 492: args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx)
! 493: {
! 494: struct args_command_state *state;
! 495: char *error;
! 496: struct cmd_list *cmdlist;
! 497:
! 498: state = args_make_commands_prepare(self, item, idx, NULL, 0, 0);
! 499: cmdlist = args_make_commands(state, 0, NULL, &error);
! 500: args_make_commands_free(state);
! 501: if (cmdlist == NULL) {
! 502: cmdq_error(item, "%s", error);
! 503: free(error);
! 504: }
! 505: return (cmdlist);
! 506: }
! 507:
! 508: /* Save bits to make a command later. */
! 509: struct args_command_state *
! 510: args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
! 511: const char *default_command, int wait, int expand)
! 512: {
! 513: struct args *args = cmd_get_args(self);
! 514: struct cmd_find_state *target = cmdq_get_target(item);
! 515: struct client *tc = cmdq_get_target_client(item);
! 516: struct args_value *value;
! 517: struct args_command_state *state;
! 518: const char *cmd;
! 519:
! 520: state = xcalloc(1, sizeof *state);
! 521:
! 522: if (idx < args->count) {
! 523: value = &args->values[idx];
! 524: if (value->type == ARGS_COMMANDS) {
! 525: state->cmdlist = value->cmdlist;
! 526: state->cmdlist->references++;
! 527: return (state);
! 528: }
! 529: cmd = value->string;
! 530: } else {
! 531: if (default_command == NULL)
! 532: fatalx("argument out of range");
! 533: cmd = default_command;
! 534: }
! 535:
! 536:
! 537: if (expand)
! 538: state->cmd = format_single_from_target(item, cmd);
! 539: else
! 540: state->cmd = xstrdup(cmd);
! 541: log_debug("%s: %s", __func__, state->cmd);
! 542:
! 543: if (wait)
! 544: state->pi.item = item;
! 545: cmd_get_source(self, &state->pi.file, &state->pi.line);
! 546: state->pi.c = tc;
! 547: if (state->pi.c != NULL)
! 548: state->pi.c->references++;
! 549: cmd_find_copy_state(&state->pi.fs, target);
! 550:
! 551: return (state);
! 552: }
! 553:
! 554: /* Return argument as command. */
! 555: struct cmd_list *
! 556: args_make_commands(struct args_command_state *state, int argc, char **argv,
! 557: char **error)
! 558: {
! 559: struct cmd_parse_result *pr;
! 560: char *cmd, *new_cmd;
! 561: int i;
! 562:
! 563: if (state->cmdlist != NULL)
! 564: return (state->cmdlist);
! 565:
! 566: cmd = xstrdup(state->cmd);
! 567: for (i = 0; i < argc; i++) {
! 568: new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
! 569: log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
! 570: free(cmd);
! 571: cmd = new_cmd;
! 572: }
! 573: log_debug("%s: %s", __func__, cmd);
! 574:
! 575: pr = cmd_parse_from_string(cmd, &state->pi);
! 576: free(cmd);
! 577: switch (pr->status) {
! 578: case CMD_PARSE_ERROR:
! 579: *error = pr->error;
! 580: return (NULL);
! 581: case CMD_PARSE_SUCCESS:
! 582: return (pr->cmdlist);
! 583: }
! 584: }
! 585:
! 586: /* Free commands state. */
! 587: void
! 588: args_make_commands_free(struct args_command_state *state)
! 589: {
! 590: if (state->cmdlist != NULL)
! 591: cmd_list_free(state->cmdlist);
! 592: if (state->pi.c != NULL)
! 593: server_client_unref(state->pi.c);
! 594: free(state->cmd);
! 595: free(state);
! 596: }
! 597:
! 598: /* Get prepared command. */
! 599: char *
! 600: args_make_commands_get_command(struct args_command_state *state)
! 601: {
! 602: struct cmd *first;
! 603: int n;
! 604: char *s;
! 605:
! 606: if (state->cmdlist != NULL) {
! 607: first = cmd_list_first(state->cmdlist);
! 608: if (first == NULL)
! 609: return (xstrdup(""));
! 610: return (xstrdup(cmd_get_entry(first)->name));
! 611: }
! 612: n = strcspn(state->cmd, " ,");
! 613: xasprintf(&s, "%.*s", n, state->cmd);
! 614: return (s);
1.32 nicm 615: }
616:
1.21 nicm 617: /* Get first value in argument. */
1.37 nicm 618: struct args_value *
619: args_first_value(struct args *args, u_char flag)
1.21 nicm 620: {
621: struct args_entry *entry;
622:
1.32 nicm 623: if ((entry = args_find(args, flag)) == NULL)
1.21 nicm 624: return (NULL);
1.37 nicm 625: return (TAILQ_FIRST(&entry->values));
1.21 nicm 626: }
627:
628: /* Get next value in argument. */
1.37 nicm 629: struct args_value *
630: args_next_value(struct args_value *value)
1.21 nicm 631: {
1.37 nicm 632: return (TAILQ_NEXT(value, entry));
1.1 nicm 633: }
634:
635: /* Convert an argument value to a number. */
636: long long
1.32 nicm 637: args_strtonum(struct args *args, u_char flag, long long minval,
638: long long maxval, char **cause)
1.1 nicm 639: {
1.5 nicm 640: const char *errstr;
1.39 nicm 641: long long ll;
1.5 nicm 642: struct args_entry *entry;
1.21 nicm 643: struct args_value *value;
1.1 nicm 644:
1.32 nicm 645: if ((entry = args_find(args, flag)) == NULL) {
1.1 nicm 646: *cause = xstrdup("missing");
647: return (0);
648: }
1.21 nicm 649: value = TAILQ_LAST(&entry->values, args_values);
1.1 nicm 650:
1.41 nicm 651: ll = strtonum(value->string, minval, maxval, &errstr);
1.1 nicm 652: if (errstr != NULL) {
653: *cause = xstrdup(errstr);
654: return (0);
1.29 nicm 655: }
656:
657: *cause = NULL;
658: return (ll);
659: }
660:
661: /* Convert an argument to a number which may be a percentage. */
662: long long
1.32 nicm 663: args_percentage(struct args *args, u_char flag, long long minval,
1.29 nicm 664: long long maxval, long long curval, char **cause)
665: {
1.31 nicm 666: const char *value;
1.29 nicm 667: struct args_entry *entry;
668:
1.32 nicm 669: if ((entry = args_find(args, flag)) == NULL) {
1.29 nicm 670: *cause = xstrdup("missing");
671: return (0);
672: }
1.41 nicm 673: value = TAILQ_LAST(&entry->values, args_values)->string;
1.31 nicm 674: return (args_string_percentage(value, minval, maxval, curval, cause));
675: }
676:
677: /* Convert a string to a number which may be a percentage. */
678: long long
679: args_string_percentage(const char *value, long long minval, long long maxval,
680: long long curval, char **cause)
681: {
682: const char *errstr;
1.39 nicm 683: long long ll;
1.31 nicm 684: size_t valuelen = strlen(value);
685: char *copy;
1.29 nicm 686:
1.31 nicm 687: if (value[valuelen - 1] == '%') {
688: copy = xstrdup(value);
1.29 nicm 689: copy[valuelen - 1] = '\0';
690:
691: ll = strtonum(copy, 0, 100, &errstr);
692: free(copy);
693: if (errstr != NULL) {
694: *cause = xstrdup(errstr);
695: return (0);
696: }
697: ll = (curval * ll) / 100;
698: if (ll < minval) {
1.33 nicm 699: *cause = xstrdup("too small");
1.29 nicm 700: return (0);
701: }
702: if (ll > maxval) {
1.33 nicm 703: *cause = xstrdup("too large");
1.29 nicm 704: return (0);
705: }
706: } else {
1.31 nicm 707: ll = strtonum(value, minval, maxval, &errstr);
1.29 nicm 708: if (errstr != NULL) {
709: *cause = xstrdup(errstr);
710: return (0);
711: }
1.1 nicm 712: }
713:
714: *cause = NULL;
715: return (ll);
716: }