1 /* $OpenBSD: arguments.c,v 1.11 2015/08/29 23:19:52 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net> 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 /* Print a set of arguments. */ 132 size_t 133 args_print(struct args *args, char *buf, size_t len) 134 { 135 size_t off, used; 136 int i; 137 const char *quotes; 138 struct args_entry *entry; 139 140 /* There must be at least one byte at the start. */ 141 if (len == 0) 142 return (0); 143 off = 0; 144 145 /* Process the flags first. */ 146 buf[off++] = '-'; 147 RB_FOREACH(entry, args_tree, &args->tree) { 148 if (entry->value != NULL) 149 continue; 150 151 if (off == len - 1) { 152 buf[off] = '\0'; 153 return (len); 154 } 155 buf[off++] = entry->flag; 156 buf[off] = '\0'; 157 } 158 if (off == 1) 159 buf[--off] = '\0'; 160 161 /* Then the flags with arguments. */ 162 RB_FOREACH(entry, args_tree, &args->tree) { 163 if (entry->value == NULL) 164 continue; 165 166 if (off >= len) { 167 /* snprintf will have zero terminated. */ 168 return (len); 169 } 170 171 if (strchr(entry->value, ' ') != NULL) 172 quotes = "\""; 173 else 174 quotes = ""; 175 used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s", 176 off != 0 ? " " : "", entry->flag, quotes, entry->value, 177 quotes); 178 if (used > len - off) 179 used = len - off; 180 off += used; 181 } 182 183 /* And finally the argument vector. */ 184 for (i = 0; i < args->argc; i++) { 185 if (off >= len) { 186 /* snprintf will have zero terminated. */ 187 return (len); 188 } 189 190 if (strchr(args->argv[i], ' ') != NULL) 191 quotes = "\""; 192 else 193 quotes = ""; 194 used = xsnprintf(buf + off, len - off, "%s%s%s%s", 195 off != 0 ? " " : "", quotes, args->argv[i], quotes); 196 if (used > len - off) 197 used = len - off; 198 off += used; 199 } 200 201 return (off); 202 } 203 204 /* Return if an argument is present. */ 205 int 206 args_has(struct args *args, u_char ch) 207 { 208 return (args_find(args, ch) == NULL ? 0 : 1); 209 } 210 211 /* Set argument value in the arguments tree. */ 212 void 213 args_set(struct args *args, u_char ch, const char *value) 214 { 215 struct args_entry *entry; 216 217 /* Replace existing argument. */ 218 if ((entry = args_find(args, ch)) != NULL) { 219 free(entry->value); 220 entry->value = NULL; 221 } else { 222 entry = xcalloc(1, sizeof *entry); 223 entry->flag = ch; 224 RB_INSERT(args_tree, &args->tree, entry); 225 } 226 227 if (value != NULL) 228 entry->value = xstrdup(value); 229 } 230 231 /* Get argument value. Will be NULL if it isn't present. */ 232 const char * 233 args_get(struct args *args, u_char ch) 234 { 235 struct args_entry *entry; 236 237 if ((entry = args_find(args, ch)) == NULL) 238 return (NULL); 239 return (entry->value); 240 } 241 242 /* Convert an argument value to a number. */ 243 long long 244 args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, 245 char **cause) 246 { 247 const char *errstr; 248 long long ll; 249 struct args_entry *entry; 250 251 if ((entry = args_find(args, ch)) == NULL) { 252 *cause = xstrdup("missing"); 253 return (0); 254 } 255 256 ll = strtonum(entry->value, minval, maxval, &errstr); 257 if (errstr != NULL) { 258 *cause = xstrdup(errstr); 259 return (0); 260 } 261 262 *cause = NULL; 263 return (ll); 264 } 265