4 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
29 * Manipulate command arguments.
32 /* List of argument values. */
33 TAILQ_HEAD(args_values
, args_value
);
35 /* Single arguments flag. */
38 struct args_values values
;
42 #define ARGS_ENTRY_OPTIONAL_VALUE 0x1
44 RB_ENTRY(args_entry
) entry
;
47 /* Parsed argument flags and values. */
49 struct args_tree tree
;
51 struct args_value
*values
;
54 /* Prepared command state. */
55 struct args_command_state
{
56 struct cmd_list
*cmdlist
;
58 struct cmd_parse_input pi
;
61 static struct args_entry
*args_find(struct args
*, u_char
);
63 static int args_cmp(struct args_entry
*, struct args_entry
*);
64 RB_GENERATE_STATIC(args_tree
, args_entry
, entry
, args_cmp
);
66 /* Arguments tree comparison function. */
68 args_cmp(struct args_entry
*a1
, struct args_entry
*a2
)
70 return (a1
->flag
- a2
->flag
);
73 /* Find a flag in the arguments tree. */
74 static struct args_entry
*
75 args_find(struct args
*args
, u_char flag
)
77 struct args_entry entry
;
80 return (RB_FIND(args_tree
, &args
->tree
, &entry
));
85 args_copy_value(struct args_value
*to
, struct args_value
*from
)
87 to
->type
= from
->type
;
92 to
->cmdlist
= from
->cmdlist
;
93 to
->cmdlist
->references
++;
96 to
->string
= xstrdup(from
->string
);
101 /* Type to string. */
103 args_type_to_string (enum args_type type
)
117 /* Get value as string. */
119 args_value_as_string(struct args_value
*value
)
121 switch (value
->type
) {
125 if (value
->cached
== NULL
)
126 value
->cached
= cmd_list_print(value
->cmdlist
, 0);
127 return (value
->cached
);
129 return (value
->string
);
131 fatalx("unexpected argument type");
134 /* Create an empty arguments set. */
140 args
= xcalloc(1, sizeof *args
);
141 RB_INIT(&args
->tree
);
145 /* Parse a single flag. */
147 args_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
)
151 struct args_value
*argument
, *new;
154 new = xcalloc(1, sizeof *new);
155 if (*string
!= '\0') {
156 new->type
= ARGS_STRING
;
157 new->string
= xstrdup(string
);
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);
172 if (argument
== NULL
) {
173 args_free_value(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 */
180 xasprintf(cause
, "-%c expects an argument", flag
);
183 args_copy_value(new, argument
);
187 s
= args_value_as_string(new);
188 log_debug("%s: -%c = %s", __func__
, flag
, s
);
189 args_set(args
, flag
, new, 0);
193 /* Parse flags argument. */
195 args_parse_flags(const struct args_parse
*parse
, struct args_value
*values
,
196 u_int count
, char **cause
, struct args
*args
, u_int
*i
)
198 struct args_value
*value
;
200 const char *found
, *string
;
201 int optional_argument
;
204 if (value
->type
!= ARGS_STRING
)
207 string
= value
->string
;
208 log_debug("%s: next %s", __func__
, string
);
209 if (*string
++ != '-' || *string
== '\0')
212 if (string
[0] == '-' && string
[1] == '\0')
221 if (!isalnum(flag
)) {
222 xasprintf(cause
, "invalid flag -%c", flag
);
226 found
= strchr(parse
->template, flag
);
228 xasprintf(cause
, "unknown flag -%c", flag
);
231 if (found
[1] != ':') {
232 log_debug("%s: -%c", __func__
, flag
);
233 args_set(args
, flag
, NULL
, 0);
236 optional_argument
= (found
[2] == ':');
237 return (args_parse_flag_argument(values
, count
, cause
, args
, i
,
238 string
, flag
, optional_argument
));
242 /* Parse arguments into a new argument set. */
244 args_parse(const struct args_parse
*parse
, struct args_value
*values
,
245 u_int count
, char **cause
)
249 enum args_parse_type type
;
250 struct args_value
*value
, *new;
255 return (args_create());
257 args
= args_create();
258 for (i
= 1; i
< count
; /* nothing */) {
259 stop
= args_parse_flags(parse
, values
, count
, cause
, args
, &i
);
267 log_debug("%s: flags end at %u of %u", __func__
, i
, count
);
269 for (/* nothing */; i
< count
; i
++) {
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
));
276 if (parse
->cb
!= NULL
) {
277 type
= parse
->cb(args
, args
->count
, cause
);
278 if (type
== ARGS_PARSE_INVALID
) {
283 type
= ARGS_PARSE_STRING
;
285 args
->values
= xrecallocarray(args
->values
,
286 args
->count
, args
->count
+ 1, sizeof *args
->values
);
287 new = &args
->values
[args
->count
++];
290 case ARGS_PARSE_INVALID
:
291 fatalx("unexpected argument type");
292 case ARGS_PARSE_STRING
:
293 if (value
->type
!= ARGS_STRING
) {
295 "argument %u must be \"string\"",
300 args_copy_value(new, value
);
302 case ARGS_PARSE_COMMANDS_OR_STRING
:
303 args_copy_value(new, value
);
305 case ARGS_PARSE_COMMANDS
:
306 if (value
->type
!= ARGS_COMMANDS
) {
308 "argument %u must be { commands }",
313 args_copy_value(new, value
);
319 if (parse
->lower
!= -1 && args
->count
< (u_int
)parse
->lower
) {
321 "too few arguments (need at least %u)",
326 if (parse
->upper
!= -1 && args
->count
> (u_int
)parse
->upper
) {
328 "too many arguments (need at most %u)",
336 /* Copy and expand a value. */
338 args_copy_copy_value(struct args_value
*to
, struct args_value
*from
, int argc
,
344 to
->type
= from
->type
;
345 switch (from
->type
) {
349 expanded
= xstrdup(from
->string
);
350 for (i
= 0; i
< argc
; i
++) {
351 s
= cmd_template_replace(expanded
, argv
[i
], i
+ 1);
355 to
->string
= expanded
;
358 to
->cmdlist
= cmd_list_copy(from
->cmdlist
, argc
, argv
);
363 /* Copy an arguments set. */
365 args_copy(struct args
*args
, int argc
, char **argv
)
367 struct args
*new_args
;
368 struct args_entry
*entry
;
369 struct args_value
*value
, *new_value
;
372 cmd_log_argv(argc
, argv
, "%s", __func__
);
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);
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);
387 if (args
->count
== 0)
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
);
400 args_free_value(struct args_value
*value
)
402 switch (value
->type
) {
409 cmd_list_free(value
->cmdlist
);
417 args_free_values(struct args_value
*values
, u_int count
)
421 for (i
= 0; i
< count
; i
++)
422 args_free_value(&values
[i
]);
425 /* Free an arguments set. */
427 args_free(struct args
*args
)
429 struct args_entry
*entry
;
430 struct args_entry
*entry1
;
431 struct args_value
*value
;
432 struct args_value
*value1
;
434 args_free_values(args
->values
, args
->count
);
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
);
450 /* Convert arguments to vector. */
452 args_to_vector(struct args
*args
, int *argc
, char ***argv
)
460 for (i
= 0; i
< args
->count
; i
++) {
461 switch (args
->values
[i
].type
) {
465 cmd_append_argv(argc
, argv
, args
->values
[i
].string
);
468 s
= cmd_list_print(args
->values
[i
].cmdlist
, 0);
469 cmd_append_argv(argc
, argv
, s
);
476 /* Convert arguments from vector. */
478 args_from_vector(int argc
, char **argv
)
480 struct args_value
*values
;
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
]);
492 static void printflike(3, 4)
493 args_print_add(char **buf
, size_t *len
, const char *fmt
, ...)
500 slen
= xvasprintf(&s
, fmt
, ap
);
504 *buf
= xrealloc(*buf
, *len
);
506 strlcat(*buf
, s
, *len
);
510 /* Add value to string. */
512 args_print_add_value(char **buf
, size_t *len
, struct args_value
*value
)
514 char *expanded
= NULL
;
517 args_print_add(buf
, len
, " ");
519 switch (value
->type
) {
523 expanded
= cmd_list_print(value
->cmdlist
, 0);
524 args_print_add(buf
, len
, "{ %s }", expanded
);
527 expanded
= args_escape(value
->string
);
528 args_print_add(buf
, len
, "%s", expanded
);
534 /* Print a set of arguments. */
536 args_print(struct args
*args
)
541 struct args_entry
*entry
;
542 struct args_entry
*last
= NULL
;
543 struct args_value
*value
;
546 buf
= xcalloc(1, len
);
548 /* Process the flags first. */
549 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
550 if (entry
->flags
& ARGS_ENTRY_OPTIONAL_VALUE
)
552 if (!TAILQ_EMPTY(&entry
->values
))
556 args_print_add(&buf
, &len
, "-");
557 for (j
= 0; j
< entry
->count
; j
++)
558 args_print_add(&buf
, &len
, "%c", entry
->flag
);
561 /* Then the flags with arguments. */
562 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
563 if (entry
->flags
& ARGS_ENTRY_OPTIONAL_VALUE
) {
565 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
567 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
571 if (TAILQ_EMPTY(&entry
->values
))
573 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
575 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
577 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
578 args_print_add_value(&buf
, &len
, value
);
582 if (last
&& (last
->flags
& ARGS_ENTRY_OPTIONAL_VALUE
))
583 args_print_add(&buf
, &len
, " --");
585 /* And finally the argument vector. */
586 for (i
= 0; i
< args
->count
; i
++)
587 args_print_add_value(&buf
, &len
, &args
->values
[i
]);
592 /* Escape an argument. */
594 args_escape(const char *s
)
596 static const char dquoted
[] = " #';${}%";
597 static const char squoted
[] = " \"";
598 char *escaped
, *result
;
599 int flags
, quotes
= 0;
602 xasprintf(&result
, "''");
605 if (s
[strcspn(s
, dquoted
)] != '\0')
607 else if (s
[strcspn(s
, squoted
)] != '\0')
612 (quotes
!= 0 || s
[0] == '~')) {
613 xasprintf(&escaped
, "\\%c", s
[0]);
617 flags
= VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
|VIS_NL
;
620 utf8_stravis(&escaped
, s
, flags
);
623 xasprintf(&result
, "'%s'", escaped
);
624 else if (quotes
== '"') {
626 xasprintf(&result
, "\"\\%s\"", escaped
);
628 xasprintf(&result
, "\"%s\"", escaped
);
631 xasprintf(&result
, "\\%s", escaped
);
633 result
= xstrdup(escaped
);
639 /* Return if an argument is present. */
641 args_has(struct args
*args
, u_char flag
)
643 struct args_entry
*entry
;
645 entry
= args_find(args
, flag
);
648 return (entry
->count
);
651 /* Set argument value in the arguments tree. */
653 args_set(struct args
*args
, u_char flag
, struct args_value
*value
, int flags
)
655 struct args_entry
*entry
;
657 entry
= args_find(args
, flag
);
659 entry
= xcalloc(1, sizeof *entry
);
662 entry
->flags
= flags
;
663 TAILQ_INIT(&entry
->values
);
664 RB_INSERT(args_tree
, &args
->tree
, entry
);
667 if (value
!= NULL
&& value
->type
!= ARGS_NONE
)
668 TAILQ_INSERT_TAIL(&entry
->values
, value
, entry
);
673 /* Get argument value. Will be NULL if it isn't present. */
675 args_get(struct args
*args
, u_char flag
)
677 struct args_entry
*entry
;
679 if ((entry
= args_find(args
, flag
)) == NULL
)
681 if (TAILQ_EMPTY(&entry
->values
))
683 return (TAILQ_LAST(&entry
->values
, args_values
)->string
);
686 /* Get first argument. */
688 args_first(struct args
*args
, struct args_entry
**entry
)
690 *entry
= RB_MIN(args_tree
, &args
->tree
);
693 return ((*entry
)->flag
);
696 /* Get next argument. */
698 args_next(struct args_entry
**entry
)
700 *entry
= RB_NEXT(args_tree
, &args
->tree
, *entry
);
703 return ((*entry
)->flag
);
706 /* Get argument count. */
708 args_count(struct args
*args
)
710 return (args
->count
);
713 /* Get argument values. */
715 args_values(struct args
*args
)
717 return (args
->values
);
720 /* Get argument value. */
722 args_value(struct args
*args
, u_int idx
)
724 if (idx
>= args
->count
)
726 return (&args
->values
[idx
]);
729 /* Return argument as string. */
731 args_string(struct args
*args
, u_int idx
)
733 if (idx
>= args
->count
)
735 return (args_value_as_string(&args
->values
[idx
]));
738 /* Make a command now. */
740 args_make_commands_now(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
743 struct args_command_state
*state
;
745 struct cmd_list
*cmdlist
;
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
);
754 cmdlist
->references
++;
755 args_make_commands_free(state
);
759 /* Save bits to make a command later. */
760 struct args_command_state
*
761 args_make_commands_prepare(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
762 const char *default_command
, int wait
, int expand
)
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
;
772 state
= xcalloc(1, sizeof *state
);
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
++;
783 if (default_command
== NULL
)
784 fatalx("argument out of range");
785 cmd
= default_command
;
790 state
->cmd
= format_single_from_target(item
, cmd
);
792 state
->cmd
= xstrdup(cmd
);
793 log_debug("%s: %s", __func__
, state
->cmd
);
796 state
->pi
.item
= item
;
797 cmd_get_source(self
, &file
, &state
->pi
.line
);
799 state
->pi
.file
= xstrdup(file
);
801 if (state
->pi
.c
!= NULL
)
802 state
->pi
.c
->references
++;
803 cmd_find_copy_state(&state
->pi
.fs
, target
);
808 /* Return argument as command. */
810 args_make_commands(struct args_command_state
*state
, int argc
, char **argv
,
813 struct cmd_parse_result
*pr
;
817 if (state
->cmdlist
!= NULL
) {
819 return (state
->cmdlist
);
820 return (cmd_list_copy(state
->cmdlist
, argc
, argv
));
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
);
832 log_debug("%s: %s", __func__
, cmd
);
834 pr
= cmd_parse_from_string(cmd
, &state
->pi
);
836 switch (pr
->status
) {
837 case CMD_PARSE_ERROR
:
840 case CMD_PARSE_SUCCESS
:
841 return (pr
->cmdlist
);
843 fatalx("invalid parse return state");
846 /* Free commands state. */
848 args_make_commands_free(struct args_command_state
*state
)
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
);
859 /* Get prepared command. */
861 args_make_commands_get_command(struct args_command_state
*state
)
867 if (state
->cmdlist
!= NULL
) {
868 first
= cmd_list_first(state
->cmdlist
);
870 return (xstrdup(""));
871 return (xstrdup(cmd_get_entry(first
)->name
));
873 n
= strcspn(state
->cmd
, " ,");
874 xasprintf(&s
, "%.*s", n
, state
->cmd
);
878 /* Get first value in argument. */
880 args_first_value(struct args
*args
, u_char flag
)
882 struct args_entry
*entry
;
884 if ((entry
= args_find(args
, flag
)) == NULL
)
886 return (TAILQ_FIRST(&entry
->values
));
889 /* Get next value in argument. */
891 args_next_value(struct args_value
*value
)
893 return (TAILQ_NEXT(value
, entry
));
896 /* Convert an argument value to a number. */
898 args_strtonum(struct args
*args
, u_char flag
, long long minval
,
899 long long maxval
, char **cause
)
903 struct args_entry
*entry
;
904 struct args_value
*value
;
906 if ((entry
= args_find(args
, flag
)) == NULL
) {
907 *cause
= xstrdup("missing");
910 value
= TAILQ_LAST(&entry
->values
, args_values
);
912 value
->type
!= ARGS_STRING
||
913 value
->string
== NULL
) {
914 *cause
= xstrdup("missing");
918 ll
= strtonum(value
->string
, minval
, maxval
, &errstr
);
919 if (errstr
!= NULL
) {
920 *cause
= xstrdup(errstr
);
928 /* Convert an argument value to a number, and expand formats. */
930 args_strtonum_and_expand(struct args
*args
, u_char flag
, long long minval
,
931 long long maxval
, struct cmdq_item
*item
, char **cause
)
936 struct args_entry
*entry
;
937 struct args_value
*value
;
939 if ((entry
= args_find(args
, flag
)) == NULL
) {
940 *cause
= xstrdup("missing");
943 value
= TAILQ_LAST(&entry
->values
, args_values
);
945 value
->type
!= ARGS_STRING
||
946 value
->string
== NULL
) {
947 *cause
= xstrdup("missing");
951 formatted
= format_single_from_target(item
, value
->string
);
952 ll
= strtonum(formatted
, minval
, maxval
, &errstr
);
954 if (errstr
!= NULL
) {
955 *cause
= xstrdup(errstr
);
963 /* Convert an argument to a number which may be a percentage. */
965 args_percentage(struct args
*args
, u_char flag
, long long minval
,
966 long long maxval
, long long curval
, char **cause
)
969 struct args_entry
*entry
;
971 if ((entry
= args_find(args
, flag
)) == NULL
) {
972 *cause
= xstrdup("missing");
975 if (TAILQ_EMPTY(&entry
->values
)) {
976 *cause
= xstrdup("empty");
979 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
980 return (args_string_percentage(value
, minval
, maxval
, curval
, cause
));
983 /* Convert a string to a number which may be a percentage. */
985 args_string_percentage(const char *value
, long long minval
, long long maxval
,
986 long long curval
, char **cause
)
990 size_t valuelen
= strlen(value
);
994 *cause
= xstrdup("empty");
997 if (value
[valuelen
- 1] == '%') {
998 copy
= xstrdup(value
);
999 copy
[valuelen
- 1] = '\0';
1001 ll
= strtonum(copy
, 0, 100, &errstr
);
1003 if (errstr
!= NULL
) {
1004 *cause
= xstrdup(errstr
);
1007 ll
= (curval
* ll
) / 100;
1009 *cause
= xstrdup("too small");
1013 *cause
= xstrdup("too large");
1017 ll
= strtonum(value
, minval
, maxval
, &errstr
);
1018 if (errstr
!= NULL
) {
1019 *cause
= xstrdup(errstr
);
1029 * Convert an argument to a number which may be a percentage, and expand
1033 args_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
)
1037 struct args_entry
*entry
;
1039 if ((entry
= args_find(args
, flag
)) == NULL
) {
1040 *cause
= xstrdup("missing");
1043 if (TAILQ_EMPTY(&entry
->values
)) {
1044 *cause
= xstrdup("empty");
1047 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
1048 return (args_string_percentage_and_expand(value
, minval
, maxval
, curval
,
1053 * Convert a string to a number which may be a percentage, and expand formats.
1056 args_string_percentage_and_expand(const char *value
, long long minval
,
1057 long long maxval
, long long curval
, struct cmdq_item
*item
, char **cause
)
1061 size_t valuelen
= strlen(value
);
1064 if (value
[valuelen
- 1] == '%') {
1065 copy
= xstrdup(value
);
1066 copy
[valuelen
- 1] = '\0';
1068 f
= format_single_from_target(item
, copy
);
1069 ll
= strtonum(f
, 0, 100, &errstr
);
1072 if (errstr
!= NULL
) {
1073 *cause
= xstrdup(errstr
);
1076 ll
= (curval
* ll
) / 100;
1078 *cause
= xstrdup("too small");
1082 *cause
= xstrdup("too large");
1086 f
= format_single_from_target(item
, value
);
1087 ll
= strtonum(f
, minval
, maxval
, &errstr
);
1089 if (errstr
!= NULL
) {
1090 *cause
= xstrdup(errstr
);