Partly restore old refresh behavior
[tig.git] / src / argv.c
blob29f3a5febc20af3b2ca3a81e6dc9a31ca72458ae
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 const int arglen = get_arg_valuelen(arg, &quoted);
167 const int unquotedlen = arglen - 1 - (arg[arglen - 1] == quoted);
168 char *unquoted;
170 if (!quoted)
171 continue;
173 unquoted = strndup(arg + 1, unquotedlen);
174 if (!unquoted)
175 return FALSE;
176 free((void *) arg);
177 argv[argc] = unquoted;
180 return TRUE;
183 bool
184 argv_copy(const char ***dst, const char *src[])
186 int argc;
188 argv_free(*dst);
189 for (argc = 0; src[argc]; argc++)
190 if (!argv_append(dst, src[argc]))
191 return FALSE;
192 return TRUE;
196 * Argument formatting.
199 struct format_var {
200 const char *name;
201 size_t namelen;
202 const char *value;
203 const char *value_if_empty;
206 struct format_context {
207 struct format_var *vars;
208 size_t vars_size;
209 char buf[SIZEOF_STR];
210 size_t bufpos;
211 bool file_filter;
214 #define ARGV_ENV_INIT(name, ifempty, initval) initval
216 struct argv_env argv_env = {
217 ARGV_ENV_INFO(ARGV_ENV_INIT)
220 static bool
221 format_expand_arg(struct format_context *format, const char *name, const char *end)
223 struct format_var *vars = format->vars;
224 int i;
226 if (!prefixcmp(name, "%(prompt")) {
227 const char *prompt = "Command argument: ";
228 char msgbuf[SIZEOF_STR];
229 const char *value;
230 const char *msgstart = name + STRING_SIZE("%(prompt");
231 const int msglen = end - msgstart - 1;
233 if (end && msglen > 0 && string_format(msgbuf, "%.*s", msglen, msgstart)) {
234 const char *msg = msgbuf;
236 while (isspace(*msg))
237 msg++;
238 if (*msg)
239 prompt = msg;
242 value = read_prompt(prompt);
243 if (value == NULL)
244 return FALSE;
245 return string_format_from(format->buf, &format->bufpos, "%s", value);
248 for (i = 0; i < format->vars_size; i++) {
249 const char *value;
251 if (strncmp(name, vars[i].name, vars[i].namelen))
252 continue;
254 if (vars[i].value == argv_env.file && !format->file_filter)
255 return TRUE;
257 value = *vars[i].value ? vars[i].value : vars[i].value_if_empty;
258 if (!*value)
259 return TRUE;
261 return string_format_from(format->buf, &format->bufpos, "%s", value);
264 return FALSE;
267 static bool
268 format_append_arg(struct format_context *format, const char ***dst_argv, const char *arg)
270 memset(format->buf, 0, sizeof(format->buf));
271 format->bufpos = 0;
273 while (arg) {
274 const char *var = strstr(arg, "%(");
275 const char *closing = var ? strchr(var, ')') : NULL;
276 const char *next = closing ? closing + 1 : NULL;
277 const int len = var ? var - arg : strlen(arg);
279 if (var && !closing)
280 return FALSE;
282 if (len && !string_format_from(format->buf, &format->bufpos, "%.*s", len, arg))
283 return FALSE;
285 if (var && !format_expand_arg(format, var, next))
286 return FALSE;
288 arg = next;
291 return argv_append(dst_argv, format->buf);
294 static bool
295 format_append_argv(struct format_context *format, const char ***dst_argv, const char *src_argv[])
297 int argc;
299 if (!src_argv)
300 return TRUE;
302 for (argc = 0; src_argv[argc]; argc++)
303 if (!format_append_arg(format, dst_argv, src_argv[argc]))
304 return FALSE;
306 return src_argv[argc] == NULL;
309 bool
310 argv_format(struct argv_env *argv_env, const char ***dst_argv, const char *src_argv[], bool first, bool file_filter)
312 struct format_var vars[] = {
313 #define FORMAT_VAR(name, ifempty, initval) \
314 { "%(" #name ")", STRING_SIZE("%(" #name ")"), argv_env->name, ifempty }
315 ARGV_ENV_INFO(FORMAT_VAR)
317 struct format_context format = { vars, ARRAY_SIZE(vars), "", 0, file_filter };
318 int argc;
320 argv_free(*dst_argv);
322 for (argc = 0; src_argv[argc]; argc++) {
323 const char *arg = src_argv[argc];
325 if (!strcmp(arg, "%(fileargs)")) {
326 if (file_filter && !argv_append_array(dst_argv, opt_file_argv))
327 break;
329 } else if (!strcmp(arg, "%(diffargs)")) {
330 if (!format_append_argv(&format, dst_argv, opt_diff_options))
331 break;
333 } else if (!strcmp(arg, "%(blameargs)")) {
334 if (!format_append_argv(&format, dst_argv, opt_blame_options))
335 break;
337 } else if (!strcmp(arg, "%(cmdlineargs)")) {
338 if (!format_append_argv(&format, dst_argv, opt_cmdline_argv))
339 break;
341 } else if (!strcmp(arg, "%(revargs)") ||
342 (first && !strcmp(arg, "%(commit)"))) {
343 if (!argv_append_array(dst_argv, opt_rev_argv))
344 break;
346 } else if (!format_append_arg(&format, dst_argv, arg)) {
347 break;
351 return src_argv[argc] == NULL;
354 static inline bool
355 argv_find_rev_flag(const char *argv[], size_t argc, const char *arg, size_t arglen,
356 size_t *search_offset, bool *with_graph, bool *with_reflog)
358 int i;
360 for (i = 0; i < argc; i++) {
361 const char *flag = argv[i];
362 size_t flaglen = strlen(flag);
364 if (flaglen > arglen || strncmp(arg, flag, flaglen))
365 continue;
367 if (search_offset)
368 *search_offset = flaglen;
369 else if (flaglen != arglen && flag[flaglen - 1] != '=')
370 continue;
372 if (with_graph)
373 *with_graph = FALSE;
374 if (with_reflog)
375 *with_reflog = TRUE;
377 return TRUE;
380 return FALSE;
383 bool
384 argv_parse_rev_flag(const char *arg, struct rev_flags *rev_flags)
386 static const char *with_graph[] = {
387 "--after=",
388 "--all",
389 "--all-match",
390 "--ancestry-path",
391 "--author-date-order",
392 "--author=",
393 "--basic-regexp",
394 "--before=",
395 "--boundary",
396 "--branches",
397 "--branches=",
398 "--cherry",
399 "--cherry-mark",
400 "--cherry-pick",
401 "--committer=",
402 "--date-order",
403 "--dense",
404 "--extended-regexp",
405 "--first-parent",
406 "--fixed-strings",
407 "--full-history",
408 "--graph",
409 "--glob=",
410 "--left-only",
411 "--max-parents=",
412 "--merge",
413 "--merges",
414 "--min-parents=",
415 "--no-max-parents",
416 "--no-merges",
417 "--no-min-parents",
418 "--no-walk",
419 "--perl-regexp",
420 "--pickaxe-all",
421 "--pickaxe-regex",
422 "--regexp-ignore-case",
423 "--remotes",
424 "--remotes=",
425 "--remove-empty",
426 "--reverse",
427 "--right-only",
428 "--simplify-by-decoration",
429 "--simplify-merges",
430 "--since=",
431 "--skip=",
432 "--sparse",
433 "--stdin",
434 "--tags",
435 "--tags=",
436 "--topo-order",
437 "--until=",
438 "-E",
439 "-F",
440 "-i",
442 static const char *no_graph[] = {
443 "--follow",
445 static const char *with_reflog[] = {
446 "--walk-reflogs",
447 "-g",
449 static const char *search_no_graph[] = {
450 "--grep-reflog=",
451 "--grep=",
452 "-G",
453 "-S",
455 size_t arglen = strlen(arg);
456 bool graph = TRUE;
457 bool reflog = FALSE;
458 size_t search = 0;
460 if (argv_find_rev_flag(with_graph, ARRAY_SIZE(with_graph), arg, arglen, NULL, NULL, NULL) ||
461 argv_find_rev_flag(no_graph, ARRAY_SIZE(no_graph), arg, arglen, NULL, &graph, NULL) ||
462 argv_find_rev_flag(with_reflog, ARRAY_SIZE(with_reflog), arg, arglen, NULL, NULL, &reflog) ||
463 argv_find_rev_flag(search_no_graph, ARRAY_SIZE(search_no_graph), arg, arglen, &search, &graph, NULL)) {
464 if (rev_flags) {
465 rev_flags->search_offset = search ? search : arglen;
466 rev_flags->with_graph = graph;
467 rev_flags->with_reflog = reflog;
469 return TRUE;
472 return FALSE;
475 char *
476 argv_format_arg(struct argv_env *argv_env, const char *src_arg)
478 const char *src_argv[] = { src_arg, NULL };
479 const char **dst_argv = NULL;
480 char *dst_arg = NULL;
482 if (argv_format(argv_env, &dst_argv, src_argv, FALSE, TRUE))
483 dst_arg = (char *) dst_argv[0];
485 free(dst_argv);
486 return dst_arg;
489 /* vim: set ts=8 sw=8 noexpandtab: */