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.
16 #include "tig/options.h"
17 #include "tig/prompt.h"
20 concat_argv(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
, bool quoted
)
24 for (bufpos
= 0, argc
= 0; argv
[argc
]; argc
++) {
25 const char *arg_sep
= argc
? sep
: "";
26 const char *arg
= argv
[argc
];
29 if (quoted
&& arg
[(pos
= strcspn(arg
, " \t\""))]) {
30 if (!string_nformat(buf
, buflen
, &bufpos
, "%s\"", arg_sep
))
34 int pos
= strcspn(arg
, "\"");
35 const char *qesc
= arg
[pos
] == '"' ? "\\\"" : "";
37 if (!string_nformat(buf
, buflen
, &bufpos
, "%.*s%s", pos
, arg
, qesc
))
42 if (!string_nformat(buf
, buflen
, &bufpos
, "\""))
48 if (!string_nformat(buf
, buflen
, &bufpos
, "%s%s", arg_sep
, arg
))
56 argv_to_string_quoted(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
)
58 return concat_argv(argv
, buf
, buflen
, sep
, TRUE
);
62 argv_to_string(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
)
64 return concat_argv(argv
, buf
, buflen
, sep
, FALSE
);
68 parse_arg(char **cmd
, bool remove_quotes
)
74 for (pos
= next
= arg
; *pos
; pos
++) {
77 if (c
== '"' || c
== '\'') {
99 } else if (quote
&& c
== '\\') {
113 if (!quote
&& isspace(c
))
124 return (!remove_quotes
|| !quote
) ? arg
: NULL
;
128 split_argv_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
, bool remove_quotes
)
130 while (*cmd
&& *argc
< SIZEOF_ARG
) {
131 char *arg
= parse_arg(&cmd
, remove_quotes
);
135 argv
[(*argc
)++] = arg
;
136 cmd
= chomp_string(cmd
);
139 if (*argc
< SIZEOF_ARG
)
141 return *argc
< SIZEOF_ARG
;
145 argv_from_string_no_quotes(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
147 return split_argv_string(argv
, argc
, cmd
, TRUE
);
151 argv_from_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
153 return split_argv_string(argv
, argc
, cmd
, FALSE
);
157 argv_from_env(const char **argv
, const char *name
)
159 char *env
= argv
? getenv(name
) : NULL
;
164 return !env
|| argv_from_string(argv
, &argc
, env
);
168 argv_free(const char *argv
[])
174 for (argc
= 0; argv
[argc
]; argc
++)
175 free((void *) argv
[argc
]);
180 argv_size(const char **argv
)
184 while (argv
&& argv
[argc
])
191 argv_contains(const char **argv
, const char *arg
)
195 for (i
= 0; argv
&& argv
[i
]; i
++)
196 if (!strcmp(argv
[i
], arg
))
201 DEFINE_ALLOCATOR(argv_realloc
, const char *, SIZEOF_ARG
)
204 argv_append(const char ***argv
, const char *arg
)
206 size_t argc
= argv_size(*argv
);
209 if (!*arg
&& argc
> 0)
212 if (!argv_realloc(argv
, argc
, 2))
217 (*argv
)[argc
++] = alloc
;
218 (*argv
)[argc
] = NULL
;
220 return alloc
!= NULL
;
224 argv_append_array(const char ***dst_argv
, const char *src_argv
[])
228 for (i
= 0; src_argv
&& src_argv
[i
]; i
++)
229 if (!argv_append(dst_argv
, src_argv
[i
]))
235 argv_copy(const char ***dst
, const char *src
[])
240 for (argc
= 0; src
[argc
]; argc
++)
241 if (!argv_append(dst
, src
[argc
]))
247 * Argument formatting.
250 struct format_context
;
255 bool (*formatter
)(struct format_context
*, struct format_var
*);
257 const char *value_if_empty
;
260 struct format_context
{
261 struct format_var
*vars
;
263 char buf
[SIZEOF_STR
];
268 #define ARGV_ENV_INIT(type, name, ifempty, initval) initval,
270 struct argv_env argv_env
= {
271 ARGV_ENV_INFO(ARGV_ENV_INIT
)
275 format_expand_arg(struct format_context
*format
, const char *name
, const char *end
)
277 struct format_var
*vars
= format
->vars
;
280 if (!prefixcmp(name
, "%(prompt")) {
281 const char *prompt
= "Command argument: ";
282 char msgbuf
[SIZEOF_STR
];
284 const char *msgstart
= name
+ STRING_SIZE("%(prompt");
285 const int msglen
= end
- msgstart
- 1;
287 if (end
&& msglen
> 0 && string_format(msgbuf
, "%.*s", msglen
, msgstart
)) {
288 const char *msg
= msgbuf
;
290 while (isspace(*msg
))
296 value
= read_prompt(prompt
);
299 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
302 for (i
= 0; i
< format
->vars_size
; i
++) {
303 if (strncmp(name
, vars
[i
].name
, vars
[i
].namelen
))
306 if (vars
[i
].value_ref
== &argv_env
.file
&& !format
->file_filter
)
309 return vars
[i
].formatter(format
, &vars
[i
]);
316 format_append_arg(struct format_context
*format
, const char ***dst_argv
, const char *arg
)
318 memset(format
->buf
, 0, sizeof(format
->buf
));
322 const char *var
= strstr(arg
, "%(");
323 const char *closing
= var
? strchr(var
, ')') : NULL
;
324 const char *next
= closing
? closing
+ 1 : NULL
;
325 const int len
= var
? var
- arg
: strlen(arg
);
330 if (len
&& !string_format_from(format
->buf
, &format
->bufpos
, "%.*s", len
, arg
))
333 if (var
&& !format_expand_arg(format
, var
, next
))
339 return argv_append(dst_argv
, format
->buf
);
343 format_append_argv(struct format_context
*format
, const char ***dst_argv
, const char *src_argv
[])
350 for (argc
= 0; src_argv
[argc
]; argc
++)
351 if (!format_append_arg(format
, dst_argv
, src_argv
[argc
]))
354 return src_argv
[argc
] == NULL
;
358 argv_string_formatter(struct format_context
*format
, struct format_var
*var
)
360 argv_string
*value_ref
= var
->value_ref
;
361 const char *value
= *value_ref
;
364 value
= var
->value_if_empty
;
369 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
373 argv_number_formatter(struct format_context
*format
, struct format_var
*var
)
375 unsigned long value
= *(unsigned long *) var
->value_ref
;
377 return string_format_from(format
->buf
, &format
->bufpos
, "%ld", value
);
381 argv_format(struct argv_env
*argv_env
, const char ***dst_argv
, const char *src_argv
[], bool first
, bool file_filter
)
383 struct format_var vars
[] = {
384 #define FORMAT_VAR(type, name, ifempty, initval) \
385 { "%(" #name ")", STRING_SIZE("%(" #name ")"), type ## _formatter, &argv_env->name, ifempty },
386 ARGV_ENV_INFO(FORMAT_VAR
)
388 struct format_context format
= { vars
, ARRAY_SIZE(vars
), "", 0, file_filter
};
391 argv_free(*dst_argv
);
393 for (argc
= 0; src_argv
[argc
]; argc
++) {
394 const char *arg
= src_argv
[argc
];
396 if (!strcmp(arg
, "%(fileargs)")) {
397 if (file_filter
&& !argv_append_array(dst_argv
, opt_file_args
))
400 } else if (!strcmp(arg
, "%(diffargs)")) {
401 if (!format_append_argv(&format
, dst_argv
, opt_diff_options
))
404 } else if (!strcmp(arg
, "%(blameargs)")) {
405 if (!format_append_argv(&format
, dst_argv
, opt_blame_options
))
408 } else if (!strcmp(arg
, "%(logargs)")) {
409 if (!format_append_argv(&format
, dst_argv
, opt_log_options
))
412 } else if (!strcmp(arg
, "%(mainargs)")) {
413 if (!format_append_argv(&format
, dst_argv
, opt_main_options
))
416 } else if (!strcmp(arg
, "%(cmdlineargs)")) {
417 if (!format_append_argv(&format
, dst_argv
, opt_cmdline_args
))
420 } else if (!strcmp(arg
, "%(revargs)") ||
421 (first
&& !strcmp(arg
, "%(commit)"))) {
422 if (!argv_append_array(dst_argv
, opt_rev_args
))
425 } else if (!format_append_arg(&format
, dst_argv
, arg
)) {
430 return src_argv
[argc
] == NULL
;
434 argv_find_rev_flag(const char *argv
[], size_t argc
, const char *arg
, size_t arglen
,
435 size_t *search_offset
, bool *with_graph
, bool *with_reflog
)
439 for (i
= 0; i
< argc
; i
++) {
440 const char *flag
= argv
[i
];
441 size_t flaglen
= strlen(flag
);
443 if (flaglen
> arglen
|| strncmp(arg
, flag
, flaglen
))
447 *search_offset
= flaglen
;
448 else if (flaglen
!= arglen
&& flag
[flaglen
- 1] != '=')
463 argv_parse_rev_flag(const char *arg
, struct rev_flags
*rev_flags
)
465 static const char *with_graph
[] = {
470 "--author-date-order",
501 "--regexp-ignore-case",
507 "--simplify-by-decoration",
521 static const char *no_graph
[] = {
524 static const char *with_reflog
[] = {
528 static const char *search_no_graph
[] = {
534 size_t arglen
= strlen(arg
);
539 if (argv_find_rev_flag(with_graph
, ARRAY_SIZE(with_graph
), arg
, arglen
, NULL
, NULL
, NULL
) ||
540 argv_find_rev_flag(no_graph
, ARRAY_SIZE(no_graph
), arg
, arglen
, NULL
, &graph
, NULL
) ||
541 argv_find_rev_flag(with_reflog
, ARRAY_SIZE(with_reflog
), arg
, arglen
, NULL
, NULL
, &reflog
) ||
542 argv_find_rev_flag(search_no_graph
, ARRAY_SIZE(search_no_graph
), arg
, arglen
, &search
, &graph
, NULL
)) {
544 rev_flags
->search_offset
= search
? search
: arglen
;
545 rev_flags
->with_graph
= graph
;
546 rev_flags
->with_reflog
= reflog
;
555 argv_format_arg(struct argv_env
*argv_env
, const char *src_arg
)
557 const char *src_argv
[] = { src_arg
, NULL
};
558 const char **dst_argv
= NULL
;
559 char *dst_arg
= NULL
;
561 if (argv_format(argv_env
, &dst_argv
, src_argv
, FALSE
, TRUE
))
562 dst_arg
= (char *) dst_argv
[0];
568 /* vim: set ts=8 sw=8 noexpandtab: */