1 /* $OpenBSD$ */ 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 <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_value { 32 char *value; 33 TAILQ_ENTRY(args_value) entry; 34 }; 35 TAILQ_HEAD(args_values, args_value); 36 37 struct args_entry { 38 u_char flag; 39 struct args_values values; 40 u_int count; 41 RB_ENTRY(args_entry) entry; 42 }; 43 44 static struct args_entry *args_find(struct args *, u_char); 45 46 static int args_cmp(struct args_entry *, struct args_entry *); 47 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); 48 49 /* Arguments tree comparison function. */ 50 static int 51 args_cmp(struct args_entry *a1, struct args_entry *a2) 52 { 53 return (a1->flag - a2->flag); 54 } 55 56 /* Find a flag in the arguments tree. */ 57 static struct args_entry * 58 args_find(struct args *args, u_char ch) 59 { 60 struct args_entry entry; 61 62 entry.flag = ch; 63 return (RB_FIND(args_tree, &args->tree, &entry)); 64 } 65 66 /* Parse an argv and argc into a new argument set. */ 67 struct args * 68 args_parse(const char *template, int argc, char **argv) 69 { 70 struct args *args; 71 int opt; 72 73 args = xcalloc(1, sizeof *args); 74 75 optreset = 1; 76 optind = 1; 77 optarg = NULL; 78 79 while ((opt = getopt(argc, argv, template)) != -1) { 80 if (opt < 0) 81 continue; 82 if (opt == '?' || strchr(template, opt) == NULL) { 83 args_free(args); 84 return (NULL); 85 } 86 args_set(args, opt, optarg); 87 optarg = NULL; 88 } 89 argc -= optind; 90 argv += optind; 91 92 args->argc = argc; 93 args->argv = cmd_copy_argv(argc, argv); 94 95 return (args); 96 } 97 98 /* Free an arguments set. */ 99 void 100 args_free(struct args *args) 101 { 102 struct args_entry *entry; 103 struct args_entry *entry1; 104 struct args_value *value; 105 struct args_value *value1; 106 107 cmd_free_argv(args->argc, args->argv); 108 109 RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { 110 RB_REMOVE(args_tree, &args->tree, entry); 111 TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { 112 TAILQ_REMOVE(&entry->values, value, entry); 113 free(value->value); 114 free(value); 115 } 116 free(entry); 117 } 118 119 free(args); 120 } 121 122 /* Add to string. */ 123 static void printflike(3, 4) 124 args_print_add(char **buf, size_t *len, const char *fmt, ...) 125 { 126 va_list ap; 127 char *s; 128 size_t slen; 129 130 va_start(ap, fmt); 131 slen = xvasprintf(&s, fmt, ap); 132 va_end(ap); 133 134 *len += slen; 135 *buf = xrealloc(*buf, *len); 136 137 strlcat(*buf, s, *len); 138 free(s); 139 } 140 141 /* Add value to string. */ 142 static void 143 args_print_add_value(char **buf, size_t *len, struct args_entry *entry, 144 struct args_value *value) 145 { 146 char *escaped; 147 148 if (**buf != '\0') 149 args_print_add(buf, len, " -%c ", entry->flag); 150 else 151 args_print_add(buf, len, "-%c ", entry->flag); 152 153 escaped = args_escape(value->value); 154 args_print_add(buf, len, "%s", escaped); 155 free(escaped); 156 } 157 158 /* Add argument to string. */ 159 static void 160 args_print_add_argument(char **buf, size_t *len, const char *argument) 161 { 162 char *escaped; 163 164 if (**buf != '\0') 165 args_print_add(buf, len, " "); 166 167 escaped = args_escape(argument); 168 args_print_add(buf, len, "%s", escaped); 169 free(escaped); 170 } 171 172 /* Print a set of arguments. */ 173 char * 174 args_print(struct args *args) 175 { 176 size_t len; 177 char *buf; 178 int i; 179 u_int j; 180 struct args_entry *entry; 181 struct args_value *value; 182 183 len = 1; 184 buf = xcalloc(1, len); 185 186 /* Process the flags first. */ 187 RB_FOREACH(entry, args_tree, &args->tree) { 188 if (!TAILQ_EMPTY(&entry->values)) 189 continue; 190 191 if (*buf == '\0') 192 args_print_add(&buf, &len, "-"); 193 for (j = 0; j < entry->count; j++) 194 args_print_add(&buf, &len, "%c", entry->flag); 195 } 196 197 /* Then the flags with arguments. */ 198 RB_FOREACH(entry, args_tree, &args->tree) { 199 TAILQ_FOREACH(value, &entry->values, entry) 200 args_print_add_value(&buf, &len, entry, value); 201 } 202 203 /* And finally the argument vector. */ 204 for (i = 0; i < args->argc; i++) 205 args_print_add_argument(&buf, &len, args->argv[i]); 206 207 return (buf); 208 } 209 210 /* Escape an argument. */ 211 char * 212 args_escape(const char *s) 213 { 214 static const char quoted[] = " #\"';${}"; 215 char *escaped, *result; 216 int flags; 217 218 if (*s == '\0') 219 return (xstrdup(s)); 220 if (s[0] != ' ' && 221 (strchr(quoted, s[0]) != NULL || s[0] == '~') && 222 s[1] == '\0') { 223 xasprintf(&escaped, "\\%c", s[0]); 224 return (escaped); 225 } 226 227 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 228 if (s[strcspn(s, quoted)] != '\0') 229 flags |= VIS_DQ; 230 utf8_stravis(&escaped, s, flags); 231 232 if (flags & VIS_DQ) { 233 if (*escaped == '~') 234 xasprintf(&result, "\"\\%s\"", escaped); 235 else 236 xasprintf(&result, "\"%s\"", escaped); 237 } else { 238 if (*escaped == '~') 239 xasprintf(&result, "\\%s", escaped); 240 else 241 result = xstrdup(escaped); 242 } 243 free(escaped); 244 return (result); 245 } 246 247 /* Return if an argument is present. */ 248 int 249 args_has(struct args *args, u_char ch) 250 { 251 struct args_entry *entry; 252 253 entry = args_find(args, ch); 254 if (entry == NULL) 255 return (0); 256 return (entry->count); 257 } 258 259 /* Set argument value in the arguments tree. */ 260 void 261 args_set(struct args *args, u_char ch, const char *s) 262 { 263 struct args_entry *entry; 264 struct args_value *value; 265 266 entry = args_find(args, ch); 267 if (entry == NULL) { 268 entry = xcalloc(1, sizeof *entry); 269 entry->flag = ch; 270 entry->count = 1; 271 TAILQ_INIT(&entry->values); 272 RB_INSERT(args_tree, &args->tree, entry); 273 } else 274 entry->count++; 275 276 if (s != NULL) { 277 value = xcalloc(1, sizeof *value); 278 value->value = xstrdup(s); 279 TAILQ_INSERT_TAIL(&entry->values, value, entry); 280 } 281 } 282 283 /* Get argument value. Will be NULL if it isn't present. */ 284 const char * 285 args_get(struct args *args, u_char ch) 286 { 287 struct args_entry *entry; 288 289 if ((entry = args_find(args, ch)) == NULL) 290 return (NULL); 291 return (TAILQ_LAST(&entry->values, args_values)->value); 292 } 293 294 /* Get first value in argument. */ 295 const char * 296 args_first_value(struct args *args, u_char ch, struct args_value **value) 297 { 298 struct args_entry *entry; 299 300 if ((entry = args_find(args, ch)) == NULL) 301 return (NULL); 302 303 *value = TAILQ_FIRST(&entry->values); 304 if (*value == NULL) 305 return (NULL); 306 return ((*value)->value); 307 } 308 309 /* Get next value in argument. */ 310 const char * 311 args_next_value(struct args_value **value) 312 { 313 if (*value == NULL) 314 return (NULL); 315 *value = TAILQ_NEXT(*value, entry); 316 if (*value == NULL) 317 return (NULL); 318 return ((*value)->value); 319 } 320 321 /* Convert an argument value to a number. */ 322 long long 323 args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, 324 char **cause) 325 { 326 const char *errstr; 327 long long ll; 328 struct args_entry *entry; 329 struct args_value *value; 330 331 if ((entry = args_find(args, ch)) == NULL) { 332 *cause = xstrdup("missing"); 333 return (0); 334 } 335 value = TAILQ_LAST(&entry->values, args_values); 336 337 ll = strtonum(value->value, minval, maxval, &errstr); 338 if (errstr != NULL) { 339 *cause = xstrdup(errstr); 340 return (0); 341 } 342 343 *cause = NULL; 344 return (ll); 345 } 346