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