1 /* $OpenBSD: arguments.c,v 1.15 2016/10/11 13:21:59 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 <getopt.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "tmux.h" 27 28 /* 29 * Manipulate command arguments. 30 */ 31 32 struct args_entry { 33 u_char flag; 34 char *value; 35 RB_ENTRY(args_entry) entry; 36 }; 37 38 static void args_set(struct args *, u_char, const char *); 39 static struct args_entry *args_find(struct args *, u_char); 40 41 static int args_cmp(struct args_entry *, struct args_entry *); 42 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); 43 44 /* Arguments tree comparison function. */ 45 static int 46 args_cmp(struct args_entry *a1, struct args_entry *a2) 47 { 48 return (a1->flag - a2->flag); 49 } 50 51 /* Find a flag in the arguments tree. */ 52 static struct args_entry * 53 args_find(struct args *args, u_char ch) 54 { 55 struct args_entry entry; 56 57 entry.flag = ch; 58 return (RB_FIND(args_tree, &args->tree, &entry)); 59 } 60 61 /* Parse an argv and argc into a new argument set. */ 62 struct args * 63 args_parse(const char *template, int argc, char **argv) 64 { 65 struct args *args; 66 int opt; 67 68 args = xcalloc(1, sizeof *args); 69 70 optreset = 1; 71 optind = 1; 72 73 while ((opt = getopt(argc, argv, template)) != -1) { 74 if (opt < 0) 75 continue; 76 if (opt == '?' || strchr(template, opt) == NULL) { 77 args_free(args); 78 return (NULL); 79 } 80 args_set(args, opt, optarg); 81 } 82 argc -= optind; 83 argv += optind; 84 85 args->argc = argc; 86 args->argv = cmd_copy_argv(argc, argv); 87 88 return (args); 89 } 90 91 /* Free an arguments set. */ 92 void 93 args_free(struct args *args) 94 { 95 struct args_entry *entry; 96 struct args_entry *entry1; 97 98 cmd_free_argv(args->argc, args->argv); 99 100 RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { 101 RB_REMOVE(args_tree, &args->tree, entry); 102 free(entry->value); 103 free(entry); 104 } 105 106 free(args); 107 } 108 109 /* Add to string. */ 110 static void printflike(3, 4) 111 args_print_add(char **buf, size_t *len, const char *fmt, ...) 112 { 113 va_list ap; 114 char *s; 115 size_t slen; 116 117 va_start(ap, fmt); 118 slen = xvasprintf(&s, fmt, ap); 119 va_end(ap); 120 121 *len += slen; 122 *buf = xrealloc(*buf, *len); 123 124 strlcat(*buf, s, *len); 125 free(s); 126 } 127 128 /* Print a set of arguments. */ 129 char * 130 args_print(struct args *args) 131 { 132 size_t len; 133 char *buf; 134 int i; 135 struct args_entry *entry; 136 137 len = 1; 138 buf = xcalloc(1, len); 139 140 /* Process the flags first. */ 141 RB_FOREACH(entry, args_tree, &args->tree) { 142 if (entry->value != NULL) 143 continue; 144 145 if (*buf == '\0') 146 args_print_add(&buf, &len, "-"); 147 args_print_add(&buf, &len, "%c", entry->flag); 148 } 149 150 /* Then the flags with arguments. */ 151 RB_FOREACH(entry, args_tree, &args->tree) { 152 if (entry->value == NULL) 153 continue; 154 155 if (*buf != '\0') 156 args_print_add(&buf, &len, " -%c ", entry->flag); 157 else 158 args_print_add(&buf, &len, "-%c ", entry->flag); 159 if (strchr(entry->value, ' ') != NULL) 160 args_print_add(&buf, &len, "\"%s\"", entry->value); 161 else 162 args_print_add(&buf, &len, "%s", entry->value); 163 } 164 165 /* And finally the argument vector. */ 166 for (i = 0; i < args->argc; i++) { 167 if (*buf != '\0') 168 args_print_add(&buf, &len, " "); 169 if (strchr(args->argv[i], ' ') != NULL) 170 args_print_add(&buf, &len, "\"%s\"", args->argv[i]); 171 else 172 args_print_add(&buf, &len, "%s", args->argv[i]); 173 } 174 175 return (buf); 176 } 177 178 /* Return if an argument is present. */ 179 int 180 args_has(struct args *args, u_char ch) 181 { 182 return (args_find(args, ch) == NULL ? 0 : 1); 183 } 184 185 /* Set argument value in the arguments tree. */ 186 static void 187 args_set(struct args *args, u_char ch, const char *value) 188 { 189 struct args_entry *entry; 190 191 /* Replace existing argument. */ 192 if ((entry = args_find(args, ch)) != NULL) { 193 free(entry->value); 194 entry->value = NULL; 195 } else { 196 entry = xcalloc(1, sizeof *entry); 197 entry->flag = ch; 198 RB_INSERT(args_tree, &args->tree, entry); 199 } 200 201 if (value != NULL) 202 entry->value = xstrdup(value); 203 } 204 205 /* Get argument value. Will be NULL if it isn't present. */ 206 const char * 207 args_get(struct args *args, u_char ch) 208 { 209 struct args_entry *entry; 210 211 if ((entry = args_find(args, ch)) == NULL) 212 return (NULL); 213 return (entry->value); 214 } 215 216 /* Convert an argument value to a number. */ 217 long long 218 args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, 219 char **cause) 220 { 221 const char *errstr; 222 long long ll; 223 struct args_entry *entry; 224 225 if ((entry = args_find(args, ch)) == NULL) { 226 *cause = xstrdup("missing"); 227 return (0); 228 } 229 230 ll = strtonum(entry->value, minval, maxval, &errstr); 231 if (errstr != NULL) { 232 *cause = xstrdup(errstr); 233 return (0); 234 } 235 236 *cause = NULL; 237 return (ll); 238 } 239