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