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