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