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.
15 #include "tig/types.h"
19 #include "tig/refdb.h"
20 #include "tig/options.h"
21 #include "tig/request.h"
30 #define DEFINE_OPTION_VARIABLES(name, type, flags) type opt_##name;
31 OPTION_INFO(DEFINE_OPTION_VARIABLES
);
33 static struct option_info option_info
[] = {
34 #define DEFINE_OPTION_INFO(name, type, flags) { #name, STRING_SIZE(#name), #type, &opt_##name },
35 OPTION_INFO(DEFINE_OPTION_INFO
)
39 find_option_info(struct option_info
*option
, size_t options
, const char *name
)
41 size_t namelen
= strlen(name
);
44 for (i
= 0; i
< options
; i
++)
45 if (enum_equals(option
[i
], name
, namelen
))
52 mark_option_seen(void *value
)
56 for (i
= 0; i
< ARRAY_SIZE(option_info
); i
++)
57 if (option_info
[i
].value
== value
) {
58 option_info
[i
].seen
= TRUE
;
67 iconv_t opt_iconv_out
= ICONV_NONE
;
68 char opt_editor
[SIZEOF_STR
] = "";
69 const char **opt_cmdline_argv
= NULL
;
70 const char **opt_rev_argv
= NULL
;
71 const char **opt_file_argv
= NULL
;
72 char opt_env_lines
[64] = "";
73 char opt_env_columns
[64] = "";
74 char *opt_env
[] = { opt_env_lines
, opt_env_columns
, NULL
};
77 * Mapping between options and command argument mapping.
83 static char opt_diff_context_arg
[9] = "";
85 if (!string_format(opt_diff_context_arg
, "-U%u", opt_diff_context
))
86 string_ncopy(opt_diff_context_arg
, "-U3", 3);
88 return opt_diff_context_arg
;
92 #define ENUM_ARG(enum_name, arg_string) ENUM_MAP_ENTRY(arg_string, enum_name)
94 static const struct enum_map_entry ignore_space_arg_map
[] = {
95 ENUM_ARG(IGNORE_SPACE_NO
, ""),
96 ENUM_ARG(IGNORE_SPACE_ALL
, "--ignore-all-space"),
97 ENUM_ARG(IGNORE_SPACE_SOME
, "--ignore-space-change"),
98 ENUM_ARG(IGNORE_SPACE_AT_EOL
, "--ignore-space-at-eol"),
104 return ignore_space_arg_map
[opt_ignore_space
].name
;
107 static const struct enum_map_entry commit_order_arg_map
[] = {
108 ENUM_ARG(COMMIT_ORDER_DEFAULT
, ""),
109 ENUM_ARG(COMMIT_ORDER_TOPO
, "--topo-order"),
110 ENUM_ARG(COMMIT_ORDER_DATE
, "--date-order"),
111 ENUM_ARG(COMMIT_ORDER_AUTHOR_DATE
, "--author-date-order"),
112 ENUM_ARG(COMMIT_ORDER_REVERSE
, "--reverse"),
118 return commit_order_arg_map
[opt_commit_order
].name
;
121 /* Use --show-notes to support Git >= 1.7.6 */
122 #define NOTES_ARG "--show-notes"
123 #define NOTES_EQ_ARG NOTES_ARG "="
125 static char opt_notes_arg
[SIZEOF_STR
] = NOTES_ARG
;
131 return opt_notes_arg
;
132 /* Notes are disabled by default when passing --pretty args. */
137 update_options_from_argv(const char *argv
[])
141 for (next
= flags_pos
= 0; argv
[next
]; next
++) {
142 const char *flag
= argv
[next
];
145 if (map_enum(&value
, commit_order_arg_map
, flag
)) {
146 opt_commit_order
= value
;
147 mark_option_seen(&opt_commit_order
);
151 if (map_enum(&value
, ignore_space_arg_map
, flag
)) {
152 opt_ignore_space
= value
;
153 mark_option_seen(&opt_ignore_space
);
157 if (!strcmp(flag
, "--no-notes")) {
158 opt_show_notes
= FALSE
;
159 mark_option_seen(&opt_show_notes
);
163 if (!prefixcmp(flag
, "--show-notes") ||
164 !prefixcmp(flag
, "--notes")) {
165 opt_show_notes
= TRUE
;
166 string_ncopy(opt_notes_arg
, flag
, strlen(flag
));
167 mark_option_seen(&opt_show_notes
);
171 if (!prefixcmp(flag
, "-U")
172 && parse_int(&value
, flag
+ 2, 0, 999999) == SUCCESS
) {
173 opt_diff_context
= value
;
174 mark_option_seen(&opt_diff_context
);
178 argv
[flags_pos
++] = flag
;
181 argv
[flags_pos
] = NULL
;
185 * User config file handling.
188 static const struct enum_map_entry color_map
[] = {
189 #define COLOR_MAP(name) ENUM_MAP_ENTRY(#name, COLOR_##name)
201 static const struct enum_map_entry attr_map
[] = {
202 #define ATTR_MAP(name) ENUM_MAP_ENTRY(#name, A_##name)
212 #define set_attribute(attr, name) map_enum(attr, attr_map, name)
215 parse_step(double *opt
, const char *arg
)
218 if (!strchr(arg
, '%'))
221 /* "Shift down" so 100% and 1 does not conflict. */
222 *opt
= (*opt
- 1) / 100;
225 return error("Percentage is larger than 100%%");
229 return error("Percentage is less than 0%%");
235 parse_int(int *opt
, const char *arg
, int min
, int max
)
237 int value
= atoi(arg
);
239 if (min
<= value
&& value
<= max
) {
244 return error("Value must be between %d and %d", min
, max
);
248 set_color(int *color
, const char *name
)
250 if (map_enum(color
, color_map
, name
))
252 if (!prefixcmp(name
, "color"))
253 return parse_int(color
, name
+ 5, 0, 255) == SUCCESS
;
254 /* Used when reading git colors. Git expects a plain int w/o prefix. */
255 return parse_int(color
, name
, 0, 255) == SUCCESS
;
258 #define is_quoted(c) ((c) == '"' || (c) == '\'')
260 static enum status_code
261 parse_color_name(const char *color
, struct line_rule
*rule
, const char **prefix_ptr
)
263 const char *prefixend
= is_quoted(*color
) ? NULL
: strchr(color
, '.');
266 struct keymap
*keymap
= get_keymap(color
, prefixend
- color
);
269 return error("Unknown key map: %.*s", (int) (prefixend
- color
), color
);
271 *prefix_ptr
= keymap
->name
;
272 color
= prefixend
+ 1;
275 memset(rule
, 0, sizeof(*rule
));
276 if (is_quoted(*color
)) {
277 rule
->line
= color
+ 1;
278 rule
->linelen
= strlen(color
) - 2;
281 rule
->namelen
= strlen(color
);
288 find_remapped(const char *remapped
[][2], size_t remapped_size
, const char *arg
)
290 size_t arglen
= strlen(arg
);
293 for (i
= 0; i
< remapped_size
; i
++) {
294 const char *name
= remapped
[i
][0];
295 size_t namelen
= strlen(name
);
297 if (arglen
== namelen
&&
298 !string_enum_compare(arg
, name
, namelen
))
305 /* Wants: object fgcolor bgcolor [attribute] */
306 static enum status_code
307 option_color_command(int argc
, const char *argv
[])
309 struct line_rule rule
= {};
310 const char *prefix
= NULL
;
311 struct line_info
*info
;
312 enum status_code code
;
315 return error("Invalid color mapping: color area fgcolor bgcolor [attrs]");
317 code
= parse_color_name(argv
[0], &rule
, &prefix
);
321 info
= add_line_rule(prefix
, &rule
);
323 static const char *obsolete
[][2] = {
324 { "acked", "' Acked-by'" },
325 { "diff-copy-from", "'copy from '" },
326 { "diff-copy-to", "'copy to '" },
327 { "diff-deleted-file-mode", "'deleted file mode '" },
328 { "diff-dissimilarity", "'dissimilarity '" },
329 { "diff-rename-from", "'rename from '" },
330 { "diff-rename-to", "'rename to '" },
331 { "diff-tree", "'diff-tree '" },
332 { "filename", "file" },
333 { "pp-adate", "'AuthorDate: '" },
334 { "pp-author", "'Author: '" },
335 { "pp-cdate", "'CommitDate: '" },
336 { "pp-commit", "'Commit: '" },
337 { "pp-date", "'Date: '" },
338 { "reviewed", "' Reviewed-by'" },
339 { "signoff", "' Signed-off-by'" },
340 { "tested", "' Tested-by'" },
341 { "tree-dir", "tree.directory" },
342 { "tree-file", "tree.file" },
346 index
= find_remapped(obsolete
, ARRAY_SIZE(obsolete
), rule
.name
);
348 /* Keep the initial prefix if defined. */
349 code
= parse_color_name(obsolete
[index
][1], &rule
, prefix
? NULL
: &prefix
);
352 info
= add_line_rule(prefix
, &rule
);
356 return error("Unknown color name: %s", argv
[0]);
358 code
= error("%s has been replaced by %s",
359 obsolete
[index
][0], obsolete
[index
][1]);
362 if (!set_color(&info
->fg
, argv
[1]))
363 return error("Unknown color: %s", argv
[1]);
365 if (!set_color(&info
->bg
, argv
[2]))
366 return error("Unknown color: %s", argv
[2]);
372 if (!set_attribute(&attr
, argv
[argc
]))
373 return error("Unknown color attribute: %s", argv
[argc
]);
380 static enum status_code
381 parse_bool(bool *opt
, const char *arg
)
383 *opt
= (!strcmp(arg
, "1") || !strcmp(arg
, "true") || !strcmp(arg
, "yes"))
385 if (*opt
|| !strcmp(arg
, "0") || !strcmp(arg
, "false") || !strcmp(arg
, "no"))
387 return error("Non-boolean value treated as false: %s", arg
);
390 static enum status_code
391 parse_enum(unsigned int *opt
, const char *arg
, const struct enum_map
*map
)
395 assert(map
->size
> 1);
397 if (map_enum_do(map
->entries
, map
->size
, (int *) opt
, arg
))
400 parse_bool(&is_true
, arg
);
401 *opt
= is_true
? map
->entries
[1].value
: map
->entries
[0].value
;
405 static enum status_code
406 parse_string(char *opt
, const char *arg
, size_t optsize
)
408 int arglen
= strlen(arg
);
413 if (arglen
== 1 || arg
[arglen
- 1] != arg
[0])
414 return ERROR_UNMATCHED_QUOTATION
;
415 arg
+= 1; arglen
-= 2;
417 string_ncopy_do(opt
, optsize
, arg
, arglen
);
422 static enum status_code
423 parse_encoding(struct encoding
**encoding_ref
, const char *arg
, bool priority
)
425 char buf
[SIZEOF_STR
];
426 enum status_code code
= parse_string(buf
, arg
, sizeof(buf
));
428 if (code
== SUCCESS
) {
429 struct encoding
*encoding
= *encoding_ref
;
431 if (encoding
&& !priority
)
433 encoding
= encoding_open(buf
);
435 *encoding_ref
= encoding
;
441 static enum status_code
442 parse_args(const char ***args
, const char *argv
[])
444 if (!argv_copy(args
, argv
))
445 return ERROR_OUT_OF_MEMORY
;
450 parse_option(struct option_info
*option
, const char *prefix
, const char *arg
)
452 char name
[SIZEOF_STR
];
454 if (!enum_name_prefixed(name
, sizeof(name
), prefix
, option
->name
))
455 return error("Failed to parse option");
457 if (!strcmp("show-notes", name
)) {
458 bool *value
= option
->value
;
459 enum status_code res
;
461 if (parse_bool(option
->value
, arg
) == SUCCESS
)
465 string_copy(opt_notes_arg
, NOTES_EQ_ARG
);
466 res
= parse_string(opt_notes_arg
+ STRING_SIZE(NOTES_EQ_ARG
), arg
,
467 sizeof(opt_notes_arg
) - STRING_SIZE(NOTES_EQ_ARG
));
468 if (res
== SUCCESS
&& !opt_notes_arg
[STRING_SIZE(NOTES_EQ_ARG
)])
469 opt_notes_arg
[STRING_SIZE(NOTES_ARG
)] = 0;
473 if (!strcmp(option
->type
, "bool"))
474 return parse_bool(option
->value
, arg
);
476 if (!strcmp(option
->type
, "double"))
477 return parse_step(option
->value
, arg
);
479 if (!strncmp(option
->type
, "enum", 4)) {
480 const char *type
= option
->type
+ STRING_SIZE("enum ");
481 const struct enum_map
*map
= find_enum_map(type
);
483 return parse_enum(option
->value
, arg
, map
);
486 if (!strcmp(option
->type
, "int")) {
487 if (strstr(name
, "title-overflow")) {
488 bool enabled
= FALSE
;
489 int *value
= option
->value
;
491 /* We try to parse it as a boolean (and set the
492 * value to 0 if fale), otherwise we parse it as
493 * an integer and use the given value. */
494 if (parse_bool(&enabled
, arg
) == SUCCESS
) {
503 if (!strcmp(name
, "line-number-interval") ||
504 !strcmp(name
, "tab-size"))
505 return parse_int(option
->value
, arg
, 1, 1024);
506 else if (!strcmp(name
, "id-width"))
507 return parse_int(option
->value
, arg
, 0, SIZEOF_REV
- 1);
509 return parse_int(option
->value
, arg
, 0, 1024);
512 return error("Unhandled option: %s", name
);
520 static struct view_config view_configs
[] = {
521 { "blame-view", &opt_blame_view
},
522 { "blob-view", &opt_blob_view
},
523 { "diff-view", &opt_diff_view
},
524 { "grep-view", &opt_grep_view
},
525 { "log-view", &opt_log_view
},
526 { "main-view", &opt_main_view
},
527 { "pager-view", &opt_pager_view
},
528 { "refs-view", &opt_refs_view
},
529 { "stage-view", &opt_stage_view
},
530 { "stash-view", &opt_stash_view
},
531 { "status-view", &opt_status_view
},
532 { "tree-view", &opt_tree_view
},
535 static enum status_code
536 check_view_config(struct option_info
*option
, const char *argv
[])
538 const char *name
= enum_name(option
->name
);
541 for (i
= 0; i
< ARRAY_SIZE(view_configs
); i
++)
542 if (!strcmp(name
, view_configs
[i
].name
))
543 return parse_view_config(name
, argv
);
548 /* Wants: name = value */
549 static enum status_code
550 option_set_command(int argc
, const char *argv
[])
552 struct option_info
*option
;
555 return error("Invalid set command: set option = value");
557 if (strcmp(argv
[1], "="))
558 return error("No value assigned to %s", argv
[0]);
560 if (!strcmp(argv
[0], "reference-format"))
561 return parse_ref_formats(argv
+ 2);
563 option
= find_option_info(option_info
, ARRAY_SIZE(option_info
), argv
[0]);
565 enum status_code code
;
570 if (!strcmp(option
->type
, "const char **")) {
571 code
= check_view_config(option
, argv
+ 2);
574 return parse_args(option
->value
, argv
+ 2);
577 code
= parse_option(option
, "", argv
[2]);
578 if (code
== SUCCESS
&& argc
!= 3)
579 return error("Option %s only takes one value", argv
[0]);
586 const char *obsolete
[][2] = {
587 { "author-width", "author" },
588 { "filename-width", "file-name" },
589 { "line-number-interval", "line-number" },
590 { "show-author", "author" },
591 { "show-date", "date" },
592 { "show-file-size", "file-size" },
593 { "show-filename", "file-name" },
595 { "show-line-numbers", "line-number" },
596 { "show-refs", "commit-title" },
597 { "show-rev-graph", "commit-title" },
598 { "title-overflow", "commit-title and text" },
600 int index
= find_remapped(obsolete
, ARRAY_SIZE(obsolete
), argv
[0]);
603 return error("%s is obsolete; use the %s view column options instead",
604 obsolete
[index
][0], obsolete
[index
][1]);
607 return error("Unknown option name: %s", argv
[0]);
610 /* Wants: mode request key */
611 static enum status_code
612 option_bind_command(int argc
, const char *argv
[])
616 enum request request
;
617 struct keymap
*keymap
;
621 return error("Invalid key binding: bind keymap key action");
623 if (!(keymap
= get_keymap(argv
[0], strlen(argv
[0])))) {
624 if (!strcmp(argv
[0], "branch"))
625 keymap
= get_keymap("refs", strlen("refs"));
627 return error("Unknown key map: %s", argv
[0]);
630 for (keys
= 0, key_arg
= argv
[1]; *key_arg
&& keys
< ARRAY_SIZE(key
); keys
++) {
631 if (get_key_value(&key_arg
, &key
[keys
]) == ERR
)
632 return error("Unknown key combo: %s", argv
[1]);
635 if (*key_arg
&& keys
== ARRAY_SIZE(key
))
636 return error("Max %zu keys are allowed in key combos: %s", ARRAY_SIZE(key
), argv
[1]);
638 request
= get_request(argv
[2]);
639 if (request
== REQ_UNKNOWN
) {
640 static const char *obsolete
[][2] = {
641 { "view-branch", "view-refs" },
643 static const char *toggles
[][2] = {
644 { "diff-context-down", "diff-context" },
645 { "diff-context-up", "diff-context" },
646 { "toggle-author", "show-author" },
647 { "toggle-changes", "show-changes" },
648 { "toggle-commit-order", "show-commit-order" },
649 { "toggle-date", "show-date" },
650 { "toggle-file-filter", "file-filter" },
651 { "toggle-file-size", "show-file-size" },
652 { "toggle-filename", "show-filename" },
653 { "toggle-graphic", "show-graphic" },
654 { "toggle-id", "show-id" },
655 { "toggle-ignore-space", "show-ignore-space" },
656 { "toggle-lineno", "show-line-numbers" },
657 { "toggle-refs", "show-refs" },
658 { "toggle-rev-graph", "show-rev-graph" },
659 { "toggle-sort-field", "sort-field" },
660 { "toggle-sort-order", "sort-order" },
661 { "toggle-title-overflow", "show-title-overflow" },
662 { "toggle-untracked-dirs", "status-untracked-dirs" },
663 { "toggle-vertical-split", "show-vertical-split" },
667 alias
= find_remapped(obsolete
, ARRAY_SIZE(obsolete
), argv
[2]);
669 const char *action
= obsolete
[alias
][1];
671 add_keybinding(keymap
, get_request(action
), key
, keys
);
672 return error("%s has been renamed to %s",
673 obsolete
[alias
][0], action
);
676 alias
= find_remapped(toggles
, ARRAY_SIZE(toggles
), argv
[2]);
678 const char *action
= toggles
[alias
][0];
679 const char *arg
= prefixcmp(action
, "diff-context-")
680 ? NULL
: (strstr(action
, "-down") ? "-1" : "+1");
681 const char *toggle
[] = { ":toggle", toggles
[alias
][1], arg
, NULL
};
682 enum status_code code
= add_run_request(keymap
, key
, keys
, toggle
);
685 code
= error("%s has been replaced by `:toggle %s%s%s'",
686 action
, toggles
[alias
][1],
687 arg
? " " : "", arg
? arg
: "");
692 if (request
== REQ_UNKNOWN
)
693 return add_run_request(keymap
, key
, keys
, argv
+ 2);
695 return add_keybinding(keymap
, request
, key
, keys
);
699 static enum status_code
load_option_file(const char *path
);
701 static enum status_code
702 option_source_command(int argc
, const char *argv
[])
704 enum status_code code
;
707 return error("Invalid source command: source path");
709 code
= load_option_file(argv
[0]);
711 return code
== ERROR_FILE_DOES_NOT_EXIST
712 ? error("File does not exist: %s", argv
[0]) : code
;
716 set_option(const char *opt
, int argc
, const char *argv
[])
718 if (!strcmp(opt
, "color"))
719 return option_color_command(argc
, argv
);
721 if (!strcmp(opt
, "set"))
722 return option_set_command(argc
, argv
);
724 if (!strcmp(opt
, "bind"))
725 return option_bind_command(argc
, argv
);
727 if (!strcmp(opt
, "source"))
728 return option_source_command(argc
, argv
);
730 return error("Unknown option command: %s", opt
);
733 struct config_state
{
740 read_option(char *opt
, size_t optlen
, char *value
, size_t valuelen
, void *data
)
742 struct config_state
*config
= data
;
743 enum status_code status
= ERROR_NO_OPTION_VALUE
;
747 /* Check for comment markers, since read_properties() will
748 * only ensure opt and value are split at first " \t". */
749 optlen
= strcspn(opt
, "#");
753 if (opt
[optlen
] == 0) {
754 /* Look for comment endings in the value. */
755 size_t len
= strcspn(value
, "#");
756 const char *argv
[SIZEOF_ARG
];
759 if (len
< valuelen
) {
764 if (!argv_from_string(argv
, &argc
, value
))
765 status
= error("Too many option arguments for %s", opt
);
767 status
= set_option(opt
, argc
, argv
);
770 if (status
!= SUCCESS
) {
771 warn("%s:%d: %s", config
->path
, config
->lineno
,
772 get_status_message(status
));
773 config
->errors
= TRUE
;
776 /* Always keep going if errors are encountered. */
780 static enum status_code
781 load_option_file(const char *path
)
783 struct config_state config
= { path
, 0, FALSE
};
785 char buf
[SIZEOF_STR
];
787 /* Do not read configuration from stdin if set to "" */
788 if (!path
|| !strlen(path
))
791 if (!prefixcmp(path
, "~/")) {
792 const char *home
= getenv("HOME");
794 if (!home
|| !string_format(buf
, "%s/%s", home
, path
+ 2))
795 return error("Failed to expand ~ to user home directory");
799 /* It's OK that the file doesn't exist. */
800 if (!io_open(&io
, "%s", path
)) {
801 /* XXX: Must return ERROR_FILE_DOES_NOT_EXIST so missing
802 * system tigrc is detected properly. */
803 if (io_error(&io
) == ENOENT
)
804 return ERROR_FILE_DOES_NOT_EXIST
;
805 return error("Error loading file %s: %s", path
, strerror(io_error(&io
)));
808 if (io_load(&io
, " \t", read_option
, &config
) == ERR
||
809 config
.errors
== TRUE
)
810 warn("Errors while loading %s.", path
);
814 extern const char *builtin_config
;
819 const char *tigrc_user
= getenv("TIGRC_USER");
820 const char *tigrc_system
= getenv("TIGRC_SYSTEM");
821 const char *tig_diff_opts
= getenv("TIG_DIFF_OPTS");
822 const bool diff_opts_from_args
= !!opt_diff_options
;
823 bool custom_tigrc_system
= !!tigrc_system
;
825 opt_file_filter
= TRUE
;
827 if (!custom_tigrc_system
)
828 tigrc_system
= SYSCONFDIR
"/tigrc";
830 if (load_option_file(tigrc_system
) == ERROR_FILE_DOES_NOT_EXIST
&& !custom_tigrc_system
) {
831 struct config_state config
= { "<built-in>", 0, FALSE
};
834 if (!io_from_string(&io
, builtin_config
) ||
835 !io_load(&io
, " \t", read_option
, &config
) == ERR
||
836 config
.errors
== TRUE
)
837 die("Error in built-in config");
841 tigrc_user
= "~/.tigrc";
842 load_option_file(tigrc_user
);
844 if (!diff_opts_from_args
&& tig_diff_opts
&& *tig_diff_opts
) {
845 static const char *diff_opts
[SIZEOF_ARG
] = { NULL
};
846 char buf
[SIZEOF_STR
];
849 if (!string_format(buf
, "%s", tig_diff_opts
) ||
850 !argv_from_string(diff_opts
, &argc
, buf
))
851 die("TIG_DIFF_OPTS contains too many arguments");
852 else if (!argv_copy(&opt_diff_options
, diff_opts
))
853 die("Failed to format TIG_DIFF_OPTS arguments");
860 * Repository properties
864 set_remote_branch(const char *name
, const char *value
, size_t valuelen
)
866 if (!strcmp(name
, ".remote")) {
867 string_ncopy(repo
.remote
, value
, valuelen
);
869 } else if (*repo
.remote
&& !strcmp(name
, ".merge")) {
870 size_t from
= strlen(repo
.remote
);
872 if (!prefixcmp(value
, "refs/heads/"))
873 value
+= STRING_SIZE("refs/heads/");
875 if (!string_format_from(repo
.remote
, &from
, "/%s", value
))
881 set_repo_config_option(char *name
, char *value
, enum status_code (*cmd
)(int, const char **))
883 const char *argv
[SIZEOF_ARG
] = { name
, "=" };
884 int argc
= 1 + (cmd
== option_set_command
);
885 enum status_code code
;
887 if (!argv_from_string(argv
, &argc
, value
))
888 code
= error("Too many arguments");
890 code
= cmd(argc
, argv
);
893 warn("Option 'tig.%s': %s", name
, get_status_message(code
));
897 set_work_tree(const char *value
)
899 char cwd
[SIZEOF_STR
];
901 if (!getcwd(cwd
, sizeof(cwd
)))
902 die("Failed to get cwd path: %s", strerror(errno
));
904 die("Failed to chdir(%s): %s", cwd
, strerror(errno
));
905 if (chdir(repo
.git_dir
) < 0)
906 die("Failed to chdir(%s): %s", repo
.git_dir
, strerror(errno
));
907 if (!getcwd(repo
.git_dir
, sizeof(repo
.git_dir
)))
908 die("Failed to get git path: %s", strerror(errno
));
909 if (chdir(value
) < 0)
910 die("Failed to chdir(%s): %s", value
, strerror(errno
));
911 if (!getcwd(cwd
, sizeof(cwd
)))
912 die("Failed to get cwd path: %s", strerror(errno
));
913 if (setenv("GIT_WORK_TREE", cwd
, TRUE
))
914 die("Failed to set GIT_WORK_TREE to '%s'", cwd
);
915 if (setenv("GIT_DIR", repo
.git_dir
, TRUE
))
916 die("Failed to set GIT_DIR to '%s'", repo
.git_dir
);
917 repo
.is_inside_work_tree
= TRUE
;
920 static struct line_info
*
921 parse_git_color_option(struct line_info
*info
, char *value
)
923 const char *argv
[SIZEOF_ARG
];
925 bool first_color
= TRUE
;
928 if (!argv_from_string(argv
, &argc
, value
))
931 info
->fg
= COLOR_DEFAULT
;
932 info
->bg
= COLOR_DEFAULT
;
935 for (i
= 0; i
< argc
; i
++) {
938 if (set_attribute(&attr
, argv
[i
])) {
941 } else if (set_color(&attr
, argv
[i
])) {
953 set_git_color_option(const char *name
, char *value
)
955 static const char *git_colors
[][2] = {
956 { "branch.current", "main-head" },
957 { "branch.local", "main-ref" },
958 { "branch.plain", "main-ref" },
959 { "branch.remote", "main-remote" },
961 { "diff.meta", "diff-header" },
962 { "diff.meta", "diff-index" },
963 { "diff.meta", "diff-oldmode" },
964 { "diff.meta", "diff-newmode" },
965 { "diff.frag", "diff-chunk" },
966 { "diff.old", "diff-del" },
967 { "diff.new", "diff-add" },
969 { "grep.filename", "grep.file" },
970 { "grep.linenumber", "grep.line-number" },
971 { "grep.separator", "grep.delimiter" },
973 { "status.branch", "stat-head" },
974 { "status.added", "stat-staged" },
975 { "status.updated", "stat-staged" },
976 { "status.changed", "stat-unstaged" },
977 { "status.untracked", "stat-untracked" },
979 struct line_info parsed
= {};
982 if (!opt_read_git_colors
)
985 i
= find_remapped(git_colors
, ARRAY_SIZE(git_colors
), name
);
986 if (i
< 0 || !parse_git_color_option(&parsed
, value
))
989 for (; i
< ARRAY_SIZE(git_colors
) && !strcasecmp(git_colors
[i
][0], name
); i
++) {
990 struct line_rule rule
= {};
991 const char *prefix
= NULL
;
992 struct line_info
*info
;
994 if (parse_color_name(git_colors
[i
][1], &rule
, &prefix
) == SUCCESS
&&
995 (info
= add_line_rule(prefix
, &rule
))) {
996 info
->fg
= parsed
.fg
;
997 info
->bg
= parsed
.bg
;
998 info
->attr
= parsed
.attr
;
1004 set_encoding(struct encoding
**encoding_ref
, const char *arg
, bool priority
)
1006 if (parse_encoding(encoding_ref
, arg
, priority
) == SUCCESS
)
1007 encoding_arg
[0] = 0;
1011 read_repo_config_option(char *name
, size_t namelen
, char *value
, size_t valuelen
, void *data
)
1013 if (!strcmp(name
, "i18n.commitencoding"))
1014 set_encoding(&default_encoding
, value
, FALSE
);
1016 else if (!strcmp(name
, "gui.encoding"))
1017 set_encoding(&default_encoding
, value
, TRUE
);
1019 else if (!strcmp(name
, "core.editor"))
1020 string_ncopy(opt_editor
, value
, valuelen
);
1022 else if (!strcmp(name
, "core.worktree"))
1023 set_work_tree(value
);
1025 else if (!strcmp(name
, "core.abbrev"))
1026 parse_int(&opt_id_width
, value
, 0, SIZEOF_REV
- 1);
1028 else if (!prefixcmp(name
, "tig.color."))
1029 set_repo_config_option(name
+ 10, value
, option_color_command
);
1031 else if (!prefixcmp(name
, "tig.bind."))
1032 set_repo_config_option(name
+ 9, value
, option_bind_command
);
1034 else if (!prefixcmp(name
, "tig."))
1035 set_repo_config_option(name
+ 4, value
, option_set_command
);
1037 else if (!prefixcmp(name
, "color."))
1038 set_git_color_option(name
+ STRING_SIZE("color."), value
);
1040 else if (*repo
.head
&& !prefixcmp(name
, "branch.") &&
1041 !strncmp(name
+ 7, repo
.head
, strlen(repo
.head
)))
1042 set_remote_branch(name
+ 7 + strlen(repo
.head
), value
, valuelen
);
1048 load_git_config(void)
1050 const char *config_list_argv
[] = { "git", "config", "--list", NULL
};
1052 return io_run_load(config_list_argv
, "=", read_repo_config_option
, NULL
);
1055 /* vim: set ts=8 sw=8 noexpandtab: */