1 /* Copyright (c) 2006-2015 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.
17 #include "tig/options.h"
18 #include "tig/prompt.h"
21 concat_argv(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
, bool quoted
)
25 for (bufpos
= 0, argc
= 0; argv
[argc
]; argc
++) {
26 const char *arg_sep
= argc
? sep
: "";
27 const char *arg
= argv
[argc
];
30 if (quoted
&& arg
[(pos
= strcspn(arg
, " \t\""))]) {
31 if (!string_nformat(buf
, buflen
, &bufpos
, "%s\"", arg_sep
))
35 int pos
= strcspn(arg
, "\"");
36 const char *qesc
= arg
[pos
] == '"' ? "\\\"" : "";
38 if (!string_nformat(buf
, buflen
, &bufpos
, "%.*s%s", pos
, arg
, qesc
))
43 if (!string_nformat(buf
, buflen
, &bufpos
, "\""))
49 if (!string_nformat(buf
, buflen
, &bufpos
, "%s%s", arg_sep
, arg
))
57 argv_to_string_quoted(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
)
59 return concat_argv(argv
, buf
, buflen
, sep
, true);
63 argv_to_string(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
)
65 return concat_argv(argv
, buf
, buflen
, sep
, false);
69 parse_arg(char **cmd
, bool remove_quotes
)
75 for (pos
= next
= arg
; *pos
; pos
++) {
78 if (c
== '"' || c
== '\'') {
100 } else if (quote
&& c
== '\\') {
114 if (!quote
&& isspace(c
))
125 return (!remove_quotes
|| !quote
) ? arg
: NULL
;
129 split_argv_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
, bool remove_quotes
)
131 while (*cmd
&& *argc
< SIZEOF_ARG
) {
132 char *arg
= parse_arg(&cmd
, remove_quotes
);
136 argv
[(*argc
)++] = arg
;
137 cmd
= chomp_string(cmd
);
140 if (*argc
< SIZEOF_ARG
)
142 return *argc
< SIZEOF_ARG
;
146 argv_from_string_no_quotes(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
148 return split_argv_string(argv
, argc
, cmd
, true);
152 argv_from_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
154 return split_argv_string(argv
, argc
, cmd
, false);
158 argv_from_env(const char **argv
, const char *name
)
160 char *env
= argv
? getenv(name
) : NULL
;
165 return !env
|| argv_from_string(argv
, &argc
, env
);
169 argv_free(const char *argv
[])
175 for (argc
= 0; argv
[argc
]; argc
++)
176 free((void *) argv
[argc
]);
181 argv_size(const char **argv
)
185 while (argv
&& argv
[argc
])
192 argv_contains(const char **argv
, const char *arg
)
196 for (i
= 0; argv
&& argv
[i
]; i
++)
197 if (!strcmp(argv
[i
], arg
))
202 DEFINE_ALLOCATOR(argv_realloc
, const char *, SIZEOF_ARG
)
205 argv_append(const char ***argv
, const char *arg
)
207 size_t argc
= argv_size(*argv
);
210 if (!*arg
&& argc
> 0)
213 if (!argv_realloc(argv
, argc
, 2))
218 (*argv
)[argc
++] = alloc
;
219 (*argv
)[argc
] = NULL
;
221 return alloc
!= NULL
;
225 argv_append_array(const char ***dst_argv
, const char *src_argv
[])
229 for (i
= 0; src_argv
&& src_argv
[i
]; i
++)
230 if (!argv_append(dst_argv
, src_argv
[i
]))
236 argv_copy(const char ***dst
, const char *src
[])
241 for (argc
= 0; src
[argc
]; argc
++)
242 if (!argv_append(dst
, src
[argc
]))
248 * Argument formatting.
251 struct format_context
;
256 bool (*formatter
)(struct format_context
*, struct format_var
*);
258 const char *value_if_empty
;
261 struct format_context
{
262 struct format_var
*vars
;
264 char buf
[SIZEOF_STR
];
269 #define ARGV_ENV_INIT(type, name, ifempty, initval) initval,
271 struct argv_env argv_env
= {
272 ARGV_ENV_INFO(ARGV_ENV_INIT
)
276 format_expand_arg(struct format_context
*format
, const char *name
, const char *end
)
278 struct format_var
*vars
= format
->vars
;
281 if (!prefixcmp(name
, "%(prompt")) {
282 const char *prompt
= "Command argument: ";
283 char msgbuf
[SIZEOF_STR
];
285 const char *msgstart
= name
+ STRING_SIZE("%(prompt");
286 const int msglen
= end
- msgstart
- 1;
288 if (end
&& msglen
> 0 && string_format(msgbuf
, "%.*s", msglen
, msgstart
)) {
289 const char *msg
= msgbuf
;
291 while (isspace(*msg
))
297 value
= read_prompt(prompt
);
300 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
303 for (i
= 0; i
< format
->vars_size
; i
++) {
304 if (string_enum_compare(name
, vars
[i
].name
, vars
[i
].namelen
))
307 if (vars
[i
].value_ref
== &argv_env
.file
&& !format
->file_filter
)
310 return vars
[i
].formatter(format
, &vars
[i
]);
317 format_append_arg(struct format_context
*format
, const char ***dst_argv
, const char *arg
)
319 memset(format
->buf
, 0, sizeof(format
->buf
));
323 const char *var
= strstr(arg
, "%(");
324 const char *closing
= var
? strchr(var
, ')') : NULL
;
325 const char *next
= closing
? closing
+ 1 : NULL
;
326 const int len
= var
? var
- arg
: strlen(arg
);
331 if (len
&& !string_format_from(format
->buf
, &format
->bufpos
, "%.*s", len
, arg
))
334 if (var
&& !format_expand_arg(format
, var
, next
))
340 return argv_append(dst_argv
, format
->buf
);
344 format_append_argv(struct format_context
*format
, const char ***dst_argv
, const char *src_argv
[])
351 for (argc
= 0; src_argv
[argc
]; argc
++)
352 if (!format_append_arg(format
, dst_argv
, src_argv
[argc
]))
355 return src_argv
[argc
] == NULL
;
359 argv_string_formatter(struct format_context
*format
, struct format_var
*var
)
361 argv_string
*value_ref
= var
->value_ref
;
362 const char *value
= *value_ref
;
365 value
= var
->value_if_empty
;
370 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
374 argv_number_formatter(struct format_context
*format
, struct format_var
*var
)
376 unsigned long value
= *(unsigned long *) var
->value_ref
;
378 return string_format_from(format
->buf
, &format
->bufpos
, "%ld", value
);
382 bool_formatter(struct format_context
*format
, struct format_var
*var
)
384 bool value
= *(bool *)var
->value_ref
;
386 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
? "true" : "false");
390 repo_str_formatter(struct format_context
*format
, struct format_var
*var
)
392 return argv_string_formatter(format
, var
);
396 repo_ref_formatter(struct format_context
*format
, struct format_var
*var
)
398 return argv_string_formatter(format
, var
);
402 repo_rev_formatter(struct format_context
*format
, struct format_var
*var
)
404 return argv_string_formatter(format
, var
);
408 argv_format(struct argv_env
*argv_env
, const char ***dst_argv
, const char *src_argv
[], bool first
, bool file_filter
)
410 struct format_var vars
[] = {
411 #define FORMAT_VAR(type, name, ifempty, initval) \
412 { "%(" #name ")", STRING_SIZE("%(" #name ")"), type ## _formatter, &argv_env->name, ifempty },
413 ARGV_ENV_INFO(FORMAT_VAR
)
414 #define FORMAT_REPO_VAR(type, name) \
415 { "%(repo:" #name ")", STRING_SIZE("%(repo:" #name ")"), type ## _formatter, &repo.name, "" },
416 REPO_INFO(FORMAT_REPO_VAR
)
418 struct format_context format
= { vars
, ARRAY_SIZE(vars
), "", 0, file_filter
};
421 argv_free(*dst_argv
);
423 for (argc
= 0; src_argv
[argc
]; argc
++) {
424 const char *arg
= src_argv
[argc
];
426 if (!strcmp(arg
, "%(fileargs)")) {
427 if (file_filter
&& !argv_append_array(dst_argv
, opt_file_args
))
430 } else if (!strcmp(arg
, "%(diffargs)")) {
431 if (!format_append_argv(&format
, dst_argv
, opt_diff_options
))
434 } else if (!strcmp(arg
, "%(blameargs)")) {
435 if (!format_append_argv(&format
, dst_argv
, opt_blame_options
))
438 } else if (!strcmp(arg
, "%(logargs)")) {
439 if (!format_append_argv(&format
, dst_argv
, opt_log_options
))
442 } else if (!strcmp(arg
, "%(mainargs)")) {
443 if (!format_append_argv(&format
, dst_argv
, opt_main_options
))
446 } else if (!strcmp(arg
, "%(cmdlineargs)")) {
447 if (!format_append_argv(&format
, dst_argv
, opt_cmdline_args
))
450 } else if (!strcmp(arg
, "%(revargs)") ||
451 (first
&& !strcmp(arg
, "%(commit)"))) {
452 if (!argv_append_array(dst_argv
, opt_rev_args
))
455 } else if (!format_append_arg(&format
, dst_argv
, arg
)) {
460 return src_argv
[argc
] == NULL
;
464 argv_find_rev_flag(const char *argv
[], size_t argc
, const char *arg
, size_t arglen
,
465 size_t *search_offset
, bool *with_graph
, bool *with_reflog
)
469 for (i
= 0; i
< argc
; i
++) {
470 const char *flag
= argv
[i
];
471 size_t flaglen
= strlen(flag
);
473 if (flaglen
> arglen
|| strncmp(arg
, flag
, flaglen
))
477 *search_offset
= flaglen
;
478 else if (flaglen
!= arglen
&& flag
[flaglen
- 1] != '=')
493 argv_parse_rev_flag(const char *arg
, struct rev_flags
*rev_flags
)
495 static const char *with_graph
[] = {
500 "--author-date-order",
531 "--regexp-ignore-case",
537 "--simplify-by-decoration",
551 static const char *no_graph
[] = {
554 static const char *with_reflog
[] = {
558 static const char *search_no_graph
[] = {
564 size_t arglen
= strlen(arg
);
569 if (argv_find_rev_flag(with_graph
, ARRAY_SIZE(with_graph
), arg
, arglen
, NULL
, NULL
, NULL
) ||
570 argv_find_rev_flag(no_graph
, ARRAY_SIZE(no_graph
), arg
, arglen
, NULL
, &graph
, NULL
) ||
571 argv_find_rev_flag(with_reflog
, ARRAY_SIZE(with_reflog
), arg
, arglen
, NULL
, NULL
, &reflog
) ||
572 argv_find_rev_flag(search_no_graph
, ARRAY_SIZE(search_no_graph
), arg
, arglen
, &search
, &graph
, NULL
)) {
574 rev_flags
->search_offset
= search
? search
: arglen
;
575 rev_flags
->with_graph
= graph
;
576 rev_flags
->with_reflog
= reflog
;
585 argv_format_arg(struct argv_env
*argv_env
, const char *src_arg
)
587 const char *src_argv
[] = { src_arg
, NULL
};
588 const char **dst_argv
= NULL
;
589 char *dst_arg
= NULL
;
591 if (argv_format(argv_env
, &dst_argv
, src_argv
, false, true))
592 dst_arg
= (char *) dst_argv
[0];
598 /* vim: set ts=8 sw=8 noexpandtab: */