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