1 /* $OpenBSD: arguments.c,v 1.35 2020/06/12 07:10:43 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 <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 #include <vis.h> 25 26 #include "tmux.h" 27 28 /* 29 * Manipulate command arguments. 30 */ 31 32 struct args_value { 33 char *value; 34 TAILQ_ENTRY(args_value) entry; 35 }; 36 TAILQ_HEAD(args_values, args_value); 37 38 struct args_entry { 39 u_char flag; 40 struct args_values values; 41 u_int count; 42 RB_ENTRY(args_entry) entry; 43 }; 44 45 static struct args_entry *args_find(struct args *, u_char); 46 47 static int args_cmp(struct args_entry *, struct args_entry *); 48 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); 49 50 /* Arguments tree comparison function. */ 51 static int 52 args_cmp(struct args_entry *a1, struct args_entry *a2) 53 { 54 return (a1->flag - a2->flag); 55 } 56 57 /* Find a flag in the arguments tree. */ 58 static struct args_entry * 59 args_find(struct args *args, u_char flag) 60 { 61 struct args_entry entry; 62 63 entry.flag = flag; 64 return (RB_FIND(args_tree, &args->tree, &entry)); 65 } 66 67 /* Parse an argv and argc into a new argument set. */ 68 struct args * 69 args_parse(const char *template, int argc, char **argv) 70 { 71 struct args *args; 72 int opt; 73 74 args = xcalloc(1, sizeof *args); 75 76 optreset = 1; 77 optind = 1; 78 optarg = NULL; 79 80 while ((opt = getopt(argc, argv, template)) != -1) { 81 if (opt < 0) 82 continue; 83 if (opt == '?' || strchr(template, opt) == NULL) { 84 args_free(args); 85 return (NULL); 86 } 87 args_set(args, opt, optarg); 88 optarg = NULL; 89 } 90 argc -= optind; 91 argv += optind; 92 93 args->argc = argc; 94 args->argv = cmd_copy_argv(argc, argv); 95 96 return (args); 97 } 98 99 /* Free an arguments set. */ 100 void 101 args_free(struct args *args) 102 { 103 struct args_entry *entry; 104 struct args_entry *entry1; 105 struct args_value *value; 106 struct args_value *value1; 107 108 cmd_free_argv(args->argc, args->argv); 109 110 RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { 111 RB_REMOVE(args_tree, &args->tree, entry); 112 TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { 113 TAILQ_REMOVE(&entry->values, value, entry); 114 free(value->value); 115 free(value); 116 } 117 free(entry); 118 } 119 120 free(args); 121 } 122 123 /* Add to string. */ 124 static void printflike(3, 4) 125 args_print_add(char **buf, size_t *len, const char *fmt, ...) 126 { 127 va_list ap; 128 char *s; 129 size_t slen; 130 131 va_start(ap, fmt); 132 slen = xvasprintf(&s, fmt, ap); 133 va_end(ap); 134 135 *len += slen; 136 *buf = xrealloc(*buf, *len); 137 138 strlcat(*buf, s, *len); 139 free(s); 140 } 141 142 /* Add value to string. */ 143 static void 144 args_print_add_value(char **buf, size_t *len, struct args_entry *entry, 145 struct args_value *value) 146 { 147 char *escaped; 148 149 if (**buf != '\0') 150 args_print_add(buf, len, " -%c ", entry->flag); 151 else 152 args_print_add(buf, len, "-%c ", entry->flag); 153 154 escaped = args_escape(value->value); 155 args_print_add(buf, len, "%s", escaped); 156 free(escaped); 157 } 158 159 /* Add argument to string. */ 160 static void 161 args_print_add_argument(char **buf, size_t *len, const char *argument) 162 { 163 char *escaped; 164 165 if (**buf != '\0') 166 args_print_add(buf, len, " "); 167 168 escaped = args_escape(argument); 169 args_print_add(buf, len, "%s", escaped); 170 free(escaped); 171 } 172 173 /* Print a set of arguments. */ 174 char * 175 args_print(struct args *args) 176 { 177 size_t len; 178 char *buf; 179 int i; 180 u_int j; 181 struct args_entry *entry; 182 struct args_value *value; 183 184 len = 1; 185 buf = xcalloc(1, len); 186 187 /* Process the flags first. */ 188 RB_FOREACH(entry, args_tree, &args->tree) { 189 if (!TAILQ_EMPTY(&entry->values)) 190 continue; 191 192 if (*buf == '\0') 193 args_print_add(&buf, &len, "-"); 194 for (j = 0; j < entry->count; j++) 195 args_print_add(&buf, &len, "%c", entry->flag); 196 } 197 198 /* Then the flags with arguments. */ 199 RB_FOREACH(entry, args_tree, &args->tree) { 200 TAILQ_FOREACH(value, &entry->values, entry) 201 args_print_add_value(&buf, &len, entry, value); 202 } 203 204 /* And finally the argument vector. */ 205 for (i = 0; i < args->argc; i++) 206 args_print_add_argument(&buf, &len, args->argv[i]); 207 208 return (buf); 209 } 210 211 /* Escape an argument. */ 212 char * 213 args_escape(const char *s) 214 { 215 static const char dquoted[] = " #';${}"; 216 static const char squoted[] = " \""; 217 char *escaped, *result; 218 int flags, quotes = 0; 219 220 if (*s == '\0') { 221 xasprintf(&result, "''"); 222 return (result); 223 } 224 if (s[strcspn(s, dquoted)] != '\0') 225 quotes = '"'; 226 else if (s[strcspn(s, squoted)] != '\0') 227 quotes = '\''; 228 229 if (s[0] != ' ' && 230 s[1] == '\0' && 231 (quotes != 0 || s[0] == '~')) { 232 xasprintf(&escaped, "\\%c", s[0]); 233 return (escaped); 234 } 235 236 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 237 if (quotes == '"') 238 flags |= VIS_DQ; 239 utf8_stravis(&escaped, s, flags); 240 241 if (quotes == '\'') 242 xasprintf(&result, "'%s'", escaped); 243 else if (quotes == '"') { 244 if (*escaped == '~') 245 xasprintf(&result, "\"\\%s\"", escaped); 246 else 247 xasprintf(&result, "\"%s\"", escaped); 248 } else { 249 if (*escaped == '~') 250 xasprintf(&result, "\\%s", escaped); 251 else 252 result = xstrdup(escaped); 253 } 254 free(escaped); 255 return (result); 256 } 257 258 /* Return if an argument is present. */ 259 int 260 args_has(struct args *args, u_char flag) 261 { 262 struct args_entry *entry; 263 264 entry = args_find(args, flag); 265 if (entry == NULL) 266 return (0); 267 return (entry->count); 268 } 269 270 /* Set argument value in the arguments tree. */ 271 void 272 args_set(struct args *args, u_char flag, const char *s) 273 { 274 struct args_entry *entry; 275 struct args_value *value; 276 277 entry = args_find(args, flag); 278 if (entry == NULL) { 279 entry = xcalloc(1, sizeof *entry); 280 entry->flag = flag; 281 entry->count = 1; 282 TAILQ_INIT(&entry->values); 283 RB_INSERT(args_tree, &args->tree, entry); 284 } else 285 entry->count++; 286 287 if (s != NULL) { 288 value = xcalloc(1, sizeof *value); 289 value->value = xstrdup(s); 290 TAILQ_INSERT_TAIL(&entry->values, value, entry); 291 } 292 } 293 294 /* Get argument value. Will be NULL if it isn't present. */ 295 const char * 296 args_get(struct args *args, u_char flag) 297 { 298 struct args_entry *entry; 299 300 if ((entry = args_find(args, flag)) == NULL) 301 return (NULL); 302 if (TAILQ_EMPTY(&entry->values)) 303 return (NULL); 304 return (TAILQ_LAST(&entry->values, args_values)->value); 305 } 306 307 /* Get first argument. */ 308 u_char 309 args_first(struct args *args, struct args_entry **entry) 310 { 311 *entry = RB_MIN(args_tree, &args->tree); 312 if (*entry == NULL) 313 return (0); 314 return ((*entry)->flag); 315 } 316 317 /* Get next argument. */ 318 u_char 319 args_next(struct args_entry **entry) 320 { 321 *entry = RB_NEXT(args_tree, &args->tree, *entry); 322 if (*entry == NULL) 323 return (0); 324 return ((*entry)->flag); 325 } 326 327 /* Get first value in argument. */ 328 const char * 329 args_first_value(struct args *args, u_char flag, struct args_value **value) 330 { 331 struct args_entry *entry; 332 333 if ((entry = args_find(args, flag)) == NULL) 334 return (NULL); 335 336 *value = TAILQ_FIRST(&entry->values); 337 if (*value == NULL) 338 return (NULL); 339 return ((*value)->value); 340 } 341 342 /* Get next value in argument. */ 343 const char * 344 args_next_value(struct args_value **value) 345 { 346 if (*value == NULL) 347 return (NULL); 348 *value = TAILQ_NEXT(*value, entry); 349 if (*value == NULL) 350 return (NULL); 351 return ((*value)->value); 352 } 353 354 /* Convert an argument value to a number. */ 355 long long 356 args_strtonum(struct args *args, u_char flag, long long minval, 357 long long maxval, char **cause) 358 { 359 const char *errstr; 360 long long ll; 361 struct args_entry *entry; 362 struct args_value *value; 363 364 if ((entry = args_find(args, flag)) == NULL) { 365 *cause = xstrdup("missing"); 366 return (0); 367 } 368 value = TAILQ_LAST(&entry->values, args_values); 369 370 ll = strtonum(value->value, minval, maxval, &errstr); 371 if (errstr != NULL) { 372 *cause = xstrdup(errstr); 373 return (0); 374 } 375 376 *cause = NULL; 377 return (ll); 378 } 379 380 /* Convert an argument to a number which may be a percentage. */ 381 long long 382 args_percentage(struct args *args, u_char flag, long long minval, 383 long long maxval, long long curval, char **cause) 384 { 385 const char *value; 386 struct args_entry *entry; 387 388 if ((entry = args_find(args, flag)) == NULL) { 389 *cause = xstrdup("missing"); 390 return (0); 391 } 392 value = TAILQ_LAST(&entry->values, args_values)->value; 393 return (args_string_percentage(value, minval, maxval, curval, cause)); 394 } 395 396 /* Convert a string to a number which may be a percentage. */ 397 long long 398 args_string_percentage(const char *value, long long minval, long long maxval, 399 long long curval, char **cause) 400 { 401 const char *errstr; 402 long long ll; 403 size_t valuelen = strlen(value); 404 char *copy; 405 406 if (value[valuelen - 1] == '%') { 407 copy = xstrdup(value); 408 copy[valuelen - 1] = '\0'; 409 410 ll = strtonum(copy, 0, 100, &errstr); 411 free(copy); 412 if (errstr != NULL) { 413 *cause = xstrdup(errstr); 414 return (0); 415 } 416 ll = (curval * ll) / 100; 417 if (ll < minval) { 418 *cause = xstrdup("too small"); 419 return (0); 420 } 421 if (ll > maxval) { 422 *cause = xstrdup("too large"); 423 return (0); 424 } 425 } else { 426 ll = strtonum(value, minval, maxval, &errstr); 427 if (errstr != NULL) { 428 *cause = xstrdup(errstr); 429 return (0); 430 } 431 } 432 433 *cause = NULL; 434 return (ll); 435 } 436