Unify view refresh checking
[tig.git] / src / argv.c
blob162cb05a7b19f6f2d98df6effcccf902728d5360
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/argv.h"
16 #include "tig/options.h"
17 #include "tig/prompt.h"
19 bool
20 argv_to_string(const char *argv[SIZEOF_ARG], char *buf, size_t buflen, const char *sep)
22 size_t bufpos, argc;
24 for (bufpos = 0, argc = 0; argv[argc]; argc++)
25 if (!string_nformat(buf, buflen, &bufpos, "%s%s",
26 argc ? sep : "", argv[argc]))
27 return FALSE;
29 return TRUE;
32 static inline int
33 get_arg_valuelen(const char *arg, char *quoted)
35 if (*arg == '"' || *arg == '\'') {
36 const char *end = *arg == '"' ? "\"" : "'";
37 int valuelen = strcspn(arg + 1, end);
39 if (quoted)
40 *quoted = *arg;
41 return valuelen > 0 ? valuelen + 2 : strlen(arg);
42 } else {
43 if (quoted)
44 *quoted = 0;
45 return strcspn(arg, " \t");
49 static bool
50 split_argv_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd, bool remove_quotes)
52 while (*cmd && *argc < SIZEOF_ARG) {
53 char quoted = 0;
54 int valuelen = get_arg_valuelen(cmd, &quoted);
55 bool advance = cmd[valuelen] != 0;
56 int quote_offset = !!(quoted && remove_quotes);
58 cmd[valuelen - quote_offset] = 0;
59 argv[(*argc)++] = chomp_string(cmd + quote_offset);
60 cmd = chomp_string(cmd + valuelen + advance);
63 if (*argc < SIZEOF_ARG)
64 argv[*argc] = NULL;
65 return *argc < SIZEOF_ARG;
68 bool
69 argv_from_string_no_quotes(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
71 return split_argv_string(argv, argc, cmd, TRUE);
74 bool
75 argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
77 return split_argv_string(argv, argc, cmd, FALSE);
80 bool
81 argv_from_env(const char **argv, const char *name)
83 char *env = argv ? getenv(name) : NULL;
84 int argc = 0;
86 if (env && *env)
87 env = strdup(env);
88 return !env || argv_from_string(argv, &argc, env);
91 void
92 argv_free(const char *argv[])
94 int argc;
96 if (!argv)
97 return;
98 for (argc = 0; argv[argc]; argc++)
99 free((void *) argv[argc]);
100 argv[0] = NULL;
103 size_t
104 argv_size(const char **argv)
106 int argc = 0;
108 while (argv && argv[argc])
109 argc++;
111 return argc;
114 bool
115 argv_contains(const char **argv, const char *arg)
117 int i;
119 for (i = 0; argv && argv[i]; i++)
120 if (!strcmp(argv[i], arg))
121 return TRUE;
122 return FALSE;
125 DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG)
127 bool
128 argv_append(const char ***argv, const char *arg)
130 size_t argc = argv_size(*argv);
131 char *alloc;
133 if (!*arg && argc > 0)
134 return TRUE;
136 if (!argv_realloc(argv, argc, 2))
137 return FALSE;
139 alloc = strdup(arg);
141 (*argv)[argc++] = alloc;
142 (*argv)[argc] = NULL;
144 return alloc != NULL;
147 bool
148 argv_append_array(const char ***dst_argv, const char *src_argv[])
150 int i;
152 for (i = 0; src_argv && src_argv[i]; i++)
153 if (!argv_append(dst_argv, src_argv[i]))
154 return FALSE;
155 return TRUE;
158 bool
159 argv_remove_quotes(const char *argv[])
161 int argc;
163 for (argc = 0; argv[argc]; argc++) {
164 char quoted = 0;
165 const char *arg = argv[argc];
166 int arglen = get_arg_valuelen(arg, &quoted);
167 int unquotedlen = arglen - 1 - (arg[arglen - 1] == quoted);
168 char *unquoted;
170 if (!quoted)
171 continue;
173 unquoted = malloc(unquotedlen + 1);
174 if (!unquoted)
175 return FALSE;
176 strncpy(unquoted, arg + 1, unquotedlen);
177 unquoted[unquotedlen] = 0;
178 free((void *) arg);
179 argv[argc] = unquoted;
182 return TRUE;
185 bool
186 argv_copy(const char ***dst, const char *src[])
188 int argc;
190 argv_free(*dst);
191 for (argc = 0; src[argc]; argc++)
192 if (!argv_append(dst, src[argc]))
193 return FALSE;
194 return TRUE;
198 * Argument formatting.
201 struct format_var {
202 const char *name;
203 size_t namelen;
204 const char *value;
205 const char *value_if_empty;
208 struct format_context {
209 struct format_var *vars;
210 size_t vars_size;
211 char buf[SIZEOF_STR];
212 size_t bufpos;
213 bool file_filter;
216 #define ARGV_ENV_INIT(name, ifempty, initval) initval
218 struct argv_env argv_env = {
219 ARGV_ENV_INFO(ARGV_ENV_INIT)
222 static bool
223 format_expand_arg(struct format_context *format, const char *name, const char *end)
225 struct format_var *vars = format->vars;
226 int i;
228 if (!prefixcmp(name, "%(prompt")) {
229 const char *prompt = "Command argument: ";
230 char msgbuf[SIZEOF_STR];
231 const char *value;
232 const char *msgstart = name + STRING_SIZE("%(prompt");
233 int msglen = end - msgstart - 1;
235 if (end && msglen > 0 && string_format(msgbuf, "%.*s", msglen, msgstart)) {
236 const char *msg = msgbuf;
238 while (isspace(*msg))
239 msg++;
240 if (*msg)
241 prompt = msg;
244 value = read_prompt(prompt);
245 if (value == NULL)
246 return FALSE;
247 return string_format_from(format->buf, &format->bufpos, "%s", value);
250 for (i = 0; i < format->vars_size; i++) {
251 const char *value;
253 if (strncmp(name, vars[i].name, vars[i].namelen))
254 continue;
256 if (vars[i].value == argv_env.file && !format->file_filter)
257 return TRUE;
259 value = *vars[i].value ? vars[i].value : vars[i].value_if_empty;
260 if (!*value)
261 return TRUE;
263 return string_format_from(format->buf, &format->bufpos, "%s", value);
266 return FALSE;
269 static bool
270 format_append_arg(struct format_context *format, const char ***dst_argv, const char *arg)
272 memset(format->buf, 0, sizeof(format->buf));
273 format->bufpos = 0;
275 while (arg) {
276 char *var = strstr(arg, "%(");
277 int len = var ? var - arg : strlen(arg);
278 char *next = var ? strchr(var, ')') + 1 : NULL;
280 if (len && !string_format_from(format->buf, &format->bufpos, "%.*s", len, arg))
281 return FALSE;
283 if (var && !format_expand_arg(format, var, next))
284 return FALSE;
286 arg = next;
289 return argv_append(dst_argv, format->buf);
292 static bool
293 format_append_argv(struct format_context *format, const char ***dst_argv, const char *src_argv[])
295 int argc;
297 if (!src_argv)
298 return TRUE;
300 for (argc = 0; src_argv[argc]; argc++)
301 if (!format_append_arg(format, dst_argv, src_argv[argc]))
302 return FALSE;
304 return src_argv[argc] == NULL;
307 bool
308 argv_format(struct argv_env *argv_env, const char ***dst_argv, const char *src_argv[], bool first, bool file_filter)
310 struct format_var vars[] = {
311 #define FORMAT_VAR(name, ifempty, initval) \
312 { "%(" #name ")", STRING_SIZE("%(" #name ")"), argv_env->name, ifempty }
313 ARGV_ENV_INFO(FORMAT_VAR)
315 struct format_context format = { vars, ARRAY_SIZE(vars), "", 0, file_filter };
316 int argc;
318 argv_free(*dst_argv);
320 for (argc = 0; src_argv[argc]; argc++) {
321 const char *arg = src_argv[argc];
323 if (!strcmp(arg, "%(fileargs)")) {
324 if (file_filter && !argv_append_array(dst_argv, opt_file_argv))
325 break;
327 } else if (!strcmp(arg, "%(diffargs)")) {
328 if (!format_append_argv(&format, dst_argv, opt_diff_options))
329 break;
331 } else if (!strcmp(arg, "%(blameargs)")) {
332 if (!format_append_argv(&format, dst_argv, opt_blame_options))
333 break;
335 } else if (!strcmp(arg, "%(cmdlineargs)")) {
336 if (!format_append_argv(&format, dst_argv, opt_cmdline_argv))
337 break;
339 } else if (!strcmp(arg, "%(revargs)") ||
340 (first && !strcmp(arg, "%(commit)"))) {
341 if (!argv_append_array(dst_argv, opt_rev_argv))
342 break;
344 } else if (!format_append_arg(&format, dst_argv, arg)) {
345 break;
349 return src_argv[argc] == NULL;
352 static inline bool
353 argv_find_rev_flag(const char *argv[], size_t argc, const char *arg, size_t arglen,
354 size_t *search_offset, bool *with_graph, bool *with_reflog)
356 int i;
358 for (i = 0; i < argc; i++) {
359 const char *flag = argv[i];
360 size_t flaglen = strlen(flag);
362 if (flaglen > arglen || strncmp(arg, flag, flaglen))
363 continue;
365 if (search_offset)
366 *search_offset = flaglen;
367 else if (flaglen != arglen && flag[flaglen - 1] != '=')
368 continue;
370 if (with_graph)
371 *with_graph = FALSE;
372 if (with_reflog)
373 *with_reflog = TRUE;
375 return TRUE;
378 return FALSE;
381 bool
382 argv_parse_rev_flag(const char *arg, struct rev_flags *rev_flags)
384 static const char *with_graph[] = {
385 "--after=",
386 "--all",
387 "--all-match",
388 "--ancestry-path",
389 "--author-date-order",
390 "--author=",
391 "--basic-regexp",
392 "--before=",
393 "--boundary",
394 "--branches",
395 "--branches=",
396 "--cherry",
397 "--cherry-mark",
398 "--cherry-pick",
399 "--committer=",
400 "--date-order",
401 "--dense",
402 "--extended-regexp",
403 "--first-parent",
404 "--fixed-strings",
405 "--full-history",
406 "--graph",
407 "--glob=",
408 "--left-only",
409 "--max-parents=",
410 "--merge",
411 "--merges",
412 "--min-parents=",
413 "--no-max-parents",
414 "--no-merges",
415 "--no-min-parents",
416 "--no-walk",
417 "--perl-regexp",
418 "--pickaxe-all",
419 "--pickaxe-regex",
420 "--regexp-ignore-case",
421 "--remotes",
422 "--remotes=",
423 "--remove-empty",
424 "--reverse",
425 "--right-only",
426 "--simplify-by-decoration",
427 "--simplify-merges",
428 "--since=",
429 "--skip=",
430 "--sparse",
431 "--stdin",
432 "--tags",
433 "--tags=",
434 "--topo-order",
435 "--until=",
436 "-E",
437 "-F",
438 "-i",
440 static const char *no_graph[] = {
441 "--follow",
443 static const char *with_reflog[] = {
444 "--walk-reflogs",
445 "-g",
447 static const char *search_no_graph[] = {
448 "--grep-reflog=",
449 "--grep=",
450 "-G",
451 "-S",
453 size_t arglen = strlen(arg);
454 bool graph = TRUE;
455 bool reflog = FALSE;
456 size_t search = 0;
458 if (argv_find_rev_flag(with_graph, ARRAY_SIZE(with_graph), arg, arglen, NULL, NULL, NULL) ||
459 argv_find_rev_flag(no_graph, ARRAY_SIZE(no_graph), arg, arglen, NULL, &graph, NULL) ||
460 argv_find_rev_flag(with_reflog, ARRAY_SIZE(with_reflog), arg, arglen, NULL, NULL, &reflog) ||
461 argv_find_rev_flag(search_no_graph, ARRAY_SIZE(search_no_graph), arg, arglen, &search, &graph, NULL)) {
462 if (rev_flags) {
463 rev_flags->search_offset = search ? search : arglen;
464 rev_flags->with_graph = graph;
465 rev_flags->with_reflog = reflog;
467 return TRUE;
470 return FALSE;
473 char *
474 argv_format_arg(struct argv_env *argv_env, const char *src_arg)
476 const char *src_argv[] = { src_arg, NULL };
477 const char **dst_argv = NULL;
478 char *dst_arg = NULL;
480 if (argv_format(argv_env, &dst_argv, src_argv, FALSE, TRUE))
481 dst_arg = (char *) dst_argv[0];
483 free(dst_argv);
484 return dst_arg;
487 /* vim: set ts=8 sw=8 noexpandtab: */