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 flag) 59 { 60 struct args_entry entry; 61 62 entry.flag = flag; 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 dquoted[] = " #';${}"; 215 static const char squoted[] = " \""; 216 char *escaped, *result; 217 int flags, quotes = 0; 218 219 if (*s == '\0') { 220 xasprintf(&result, "''"); 221 return (result); 222 } 223 if (s[strcspn(s, dquoted)] != '\0') 224 quotes = '"'; 225 else if (s[strcspn(s, squoted)] != '\0') 226 quotes = '\''; 227 228 if (s[0] != ' ' && 229 s[1] == '\0' && 230 (quotes != 0 || s[0] == '~')) { 231 xasprintf(&escaped, "\\%c", s[0]); 232 return (escaped); 233 } 234 235 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 236 if (quotes == '"') 237 flags |= VIS_DQ; 238 utf8_stravis(&escaped, s, flags); 239 240 if (quotes == '\'') 241 xasprintf(&result, "'%s'", escaped); 242 else if (quotes == '"') { 243 if (*escaped == '~') 244 xasprintf(&result, "\"\\%s\"", escaped); 245 else 246 xasprintf(&result, "\"%s\"", escaped); 247 } else { 248 if (*escaped == '~') 249 xasprintf(&result, "\\%s", escaped); 250 else 251 result = xstrdup(escaped); 252 } 253 free(escaped); 254 return (result); 255 } 256 257 /* Return if an argument is present. */ 258 int 259 args_has(struct args *args, u_char flag) 260 { 261 struct args_entry *entry; 262 263 entry = args_find(args, flag); 264 if (entry == NULL) 265 return (0); 266 return (entry->count); 267 } 268 269 /* Set argument value in the arguments tree. */ 270 void 271 args_set(struct args *args, u_char flag, const char *s) 272 { 273 struct args_entry *entry; 274 struct args_value *value; 275 276 entry = args_find(args, flag); 277 if (entry == NULL) { 278 entry = xcalloc(1, sizeof *entry); 279 entry->flag = flag; 280 entry->count = 1; 281 TAILQ_INIT(&entry->values); 282 RB_INSERT(args_tree, &args->tree, entry); 283 } else 284 entry->count++; 285 286 if (s != NULL) { 287 value = xcalloc(1, sizeof *value); 288 value->value = xstrdup(s); 289 TAILQ_INSERT_TAIL(&entry->values, value, entry); 290 } 291 } 292 293 /* Get argument value. Will be NULL if it isn't present. */ 294 const char * 295 args_get(struct args *args, u_char flag) 296 { 297 struct args_entry *entry; 298 299 if ((entry = args_find(args, flag)) == NULL) 300 return (NULL); 301 if (TAILQ_EMPTY(&entry->values)) 302 return (NULL); 303 return (TAILQ_LAST(&entry->values, args_values)->value); 304 } 305 306 /* Get first argument. */ 307 u_char 308 args_first(struct args *args, struct args_entry **entry) 309 { 310 *entry = RB_MIN(args_tree, &args->tree); 311 if (*entry == NULL) 312 return (0); 313 return ((*entry)->flag); 314 } 315 316 /* Get next argument. */ 317 u_char 318 args_next(struct args_entry **entry) 319 { 320 *entry = RB_NEXT(args_tree, &args->tree, *entry); 321 if (*entry == NULL) 322 return (0); 323 return ((*entry)->flag); 324 } 325 326 /* Get first value in argument. */ 327 const char * 328 args_first_value(struct args *args, u_char flag, struct args_value **value) 329 { 330 struct args_entry *entry; 331 332 if ((entry = args_find(args, flag)) == NULL) 333 return (NULL); 334 335 *value = TAILQ_FIRST(&entry->values); 336 if (*value == NULL) 337 return (NULL); 338 return ((*value)->value); 339 } 340 341 /* Get next value in argument. */ 342 const char * 343 args_next_value(struct args_value **value) 344 { 345 if (*value == NULL) 346 return (NULL); 347 *value = TAILQ_NEXT(*value, entry); 348 if (*value == NULL) 349 return (NULL); 350 return ((*value)->value); 351 } 352 353 /* Convert an argument value to a number. */ 354 long long 355 args_strtonum(struct args *args, u_char flag, long long minval, 356 long long maxval, char **cause) 357 { 358 const char *errstr; 359 long long ll; 360 struct args_entry *entry; 361 struct args_value *value; 362 363 if ((entry = args_find(args, flag)) == NULL) { 364 *cause = xstrdup("missing"); 365 return (0); 366 } 367 value = TAILQ_LAST(&entry->values, args_values); 368 369 ll = strtonum(value->value, minval, maxval, &errstr); 370 if (errstr != NULL) { 371 *cause = xstrdup(errstr); 372 return (0); 373 } 374 375 *cause = NULL; 376 return (ll); 377 } 378 379 /* Convert an argument to a number which may be a percentage. */ 380 long long 381 args_percentage(struct args *args, u_char flag, long long minval, 382 long long maxval, long long curval, char **cause) 383 { 384 const char *value; 385 struct args_entry *entry; 386 387 if ((entry = args_find(args, flag)) == NULL) { 388 *cause = xstrdup("missing"); 389 return (0); 390 } 391 value = TAILQ_LAST(&entry->values, args_values)->value; 392 return (args_string_percentage(value, minval, maxval, curval, cause)); 393 } 394 395 /* Convert a string to a number which may be a percentage. */ 396 long long 397 args_string_percentage(const char *value, long long minval, long long maxval, 398 long long curval, char **cause) 399 { 400 const char *errstr; 401 long long ll; 402 size_t valuelen = strlen(value); 403 char *copy; 404 405 if (value[valuelen - 1] == '%') { 406 copy = xstrdup(value); 407 copy[valuelen - 1] = '\0'; 408 409 ll = strtonum(copy, 0, 100, &errstr); 410 free(copy); 411 if (errstr != NULL) { 412 *cause = xstrdup(errstr); 413 return (0); 414 } 415 ll = (curval * ll) / 100; 416 if (ll < minval) { 417 *cause = xstrdup("too small"); 418 return (0); 419 } 420 if (ll > maxval) { 421 *cause = xstrdup("too large"); 422 return (0); 423 } 424 } else { 425 ll = strtonum(value, minval, maxval, &errstr); 426 if (errstr != NULL) { 427 *cause = xstrdup(errstr); 428 return (0); 429 } 430 } 431 432 *cause = NULL; 433 return (ll); 434 } 435