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