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.
16 #include "tig/options.h"
17 #include "tig/prompt.h"
20 argv_to_string(const char *argv
[SIZEOF_ARG
], char *buf
, size_t buflen
, const char *sep
)
24 for (bufpos
= 0, argc
= 0; argv
[argc
]; argc
++)
25 if (!string_nformat(buf
, buflen
, &bufpos
, "%s%s",
26 argc
? sep
: "", argv
[argc
]))
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
);
41 return valuelen
> 0 ? valuelen
+ 2 : strlen(arg
);
45 return strcspn(arg
, " \t");
50 split_argv_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
, bool remove_quotes
)
52 while (*cmd
&& *argc
< SIZEOF_ARG
) {
54 int valuelen
= get_arg_valuelen(cmd
, "ed
);
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
)
65 return *argc
< SIZEOF_ARG
;
69 argv_from_string_no_quotes(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
71 return split_argv_string(argv
, argc
, cmd
, TRUE
);
75 argv_from_string(const char *argv
[SIZEOF_ARG
], int *argc
, char *cmd
)
77 return split_argv_string(argv
, argc
, cmd
, FALSE
);
81 argv_from_env(const char **argv
, const char *name
)
83 char *env
= argv
? getenv(name
) : NULL
;
88 return !env
|| argv_from_string(argv
, &argc
, env
);
92 argv_free(const char *argv
[])
98 for (argc
= 0; argv
[argc
]; argc
++)
99 free((void *) argv
[argc
]);
104 argv_size(const char **argv
)
108 while (argv
&& argv
[argc
])
115 argv_contains(const char **argv
, const char *arg
)
119 for (i
= 0; argv
&& argv
[i
]; i
++)
120 if (!strcmp(argv
[i
], arg
))
125 DEFINE_ALLOCATOR(argv_realloc
, const char *, SIZEOF_ARG
)
128 argv_append(const char ***argv
, const char *arg
)
130 size_t argc
= argv_size(*argv
);
133 if (!*arg
&& argc
> 0)
136 if (!argv_realloc(argv
, argc
, 2))
141 (*argv
)[argc
++] = alloc
;
142 (*argv
)[argc
] = NULL
;
144 return alloc
!= NULL
;
148 argv_append_array(const char ***dst_argv
, const char *src_argv
[])
152 for (i
= 0; src_argv
&& src_argv
[i
]; i
++)
153 if (!argv_append(dst_argv
, src_argv
[i
]))
159 argv_remove_quotes(const char *argv
[])
163 for (argc
= 0; argv
[argc
]; argc
++) {
165 const char *arg
= argv
[argc
];
166 const int arglen
= get_arg_valuelen(arg
, "ed
);
167 const int unquotedlen
= arglen
- 1 - (arg
[arglen
- 1] == quoted
);
173 unquoted
= strndup(arg
+ 1, unquotedlen
);
177 argv
[argc
] = unquoted
;
184 argv_copy(const char ***dst
, const char *src
[])
189 for (argc
= 0; src
[argc
]; argc
++)
190 if (!argv_append(dst
, src
[argc
]))
196 * Argument formatting.
203 const char *value_if_empty
;
206 struct format_context
{
207 struct format_var
*vars
;
209 char buf
[SIZEOF_STR
];
214 #define ARGV_ENV_INIT(name, ifempty, initval) initval
216 struct argv_env argv_env
= {
217 ARGV_ENV_INFO(ARGV_ENV_INIT
)
221 format_expand_arg(struct format_context
*format
, const char *name
, const char *end
)
223 struct format_var
*vars
= format
->vars
;
226 if (!prefixcmp(name
, "%(prompt")) {
227 const char *prompt
= "Command argument: ";
228 char msgbuf
[SIZEOF_STR
];
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
))
242 value
= read_prompt(prompt
);
245 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
248 for (i
= 0; i
< format
->vars_size
; i
++) {
251 if (strncmp(name
, vars
[i
].name
, vars
[i
].namelen
))
254 if (vars
[i
].value
== argv_env
.file
&& !format
->file_filter
)
257 value
= *vars
[i
].value
? vars
[i
].value
: vars
[i
].value_if_empty
;
261 return string_format_from(format
->buf
, &format
->bufpos
, "%s", value
);
268 format_append_arg(struct format_context
*format
, const char ***dst_argv
, const char *arg
)
270 memset(format
->buf
, 0, sizeof(format
->buf
));
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
);
282 if (len
&& !string_format_from(format
->buf
, &format
->bufpos
, "%.*s", len
, arg
))
285 if (var
&& !format_expand_arg(format
, var
, next
))
291 return argv_append(dst_argv
, format
->buf
);
295 format_append_argv(struct format_context
*format
, const char ***dst_argv
, const char *src_argv
[])
302 for (argc
= 0; src_argv
[argc
]; argc
++)
303 if (!format_append_arg(format
, dst_argv
, src_argv
[argc
]))
306 return src_argv
[argc
] == NULL
;
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
};
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
))
329 } else if (!strcmp(arg
, "%(diffargs)")) {
330 if (!format_append_argv(&format
, dst_argv
, opt_diff_options
))
333 } else if (!strcmp(arg
, "%(blameargs)")) {
334 if (!format_append_argv(&format
, dst_argv
, opt_blame_options
))
337 } else if (!strcmp(arg
, "%(cmdlineargs)")) {
338 if (!format_append_argv(&format
, dst_argv
, opt_cmdline_argv
))
341 } else if (!strcmp(arg
, "%(revargs)") ||
342 (first
&& !strcmp(arg
, "%(commit)"))) {
343 if (!argv_append_array(dst_argv
, opt_rev_argv
))
346 } else if (!format_append_arg(&format
, dst_argv
, arg
)) {
351 return src_argv
[argc
] == NULL
;
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
)
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
))
368 *search_offset
= flaglen
;
369 else if (flaglen
!= arglen
&& flag
[flaglen
- 1] != '=')
384 argv_parse_rev_flag(const char *arg
, struct rev_flags
*rev_flags
)
386 static const char *with_graph
[] = {
391 "--author-date-order",
422 "--regexp-ignore-case",
428 "--simplify-by-decoration",
442 static const char *no_graph
[] = {
445 static const char *with_reflog
[] = {
449 static const char *search_no_graph
[] = {
455 size_t arglen
= strlen(arg
);
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
)) {
465 rev_flags
->search_offset
= search
? search
: arglen
;
466 rev_flags
->with_graph
= graph
;
467 rev_flags
->with_reflog
= reflog
;
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];
489 /* vim: set ts=8 sw=8 noexpandtab: */