Code

Merge branch 'sp/refspec-match'
[git.git] / builtin-config.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "color.h"
5 static const char git_config_set_usage[] =
6 "git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default]";
8 static char *key;
9 static regex_t *key_regexp;
10 static regex_t *regexp;
11 static int show_keys;
12 static int use_key_regexp;
13 static int do_all;
14 static int do_not_match;
15 static int seen;
16 static char delim = '=';
17 static char key_delim = ' ';
18 static char term = '\n';
19 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
21 static int show_all_config(const char *key_, const char *value_)
22 {
23         if (value_)
24                 printf("%s%c%s%c", key_, delim, value_, term);
25         else
26                 printf("%s%c", key_, term);
27         return 0;
28 }
30 static int show_config(const char* key_, const char* value_)
31 {
32         char value[256];
33         const char *vptr = value;
34         int dup_error = 0;
36         if (!use_key_regexp && strcmp(key_, key))
37                 return 0;
38         if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
39                 return 0;
40         if (regexp != NULL &&
41                          (do_not_match ^
42                           regexec(regexp, (value_?value_:""), 0, NULL, 0)))
43                 return 0;
45         if (show_keys) {
46                 if (value_)
47                         printf("%s%c", key_, key_delim);
48                 else
49                         printf("%s", key_);
50         }
51         if (seen && !do_all)
52                 dup_error = 1;
53         if (type == T_INT)
54                 sprintf(value, "%d", git_config_int(key_, value_?value_:""));
55         else if (type == T_BOOL)
56                 vptr = git_config_bool(key_, value_) ? "true" : "false";
57         else
58                 vptr = value_?value_:"";
59         seen++;
60         if (dup_error) {
61                 error("More than one value for the key %s: %s",
62                                 key_, vptr);
63         }
64         else
65                 printf("%s%c", vptr, term);
67         return 0;
68 }
70 static int get_value(const char* key_, const char* regex_)
71 {
72         int ret = -1;
73         char *tl;
74         char *global = NULL, *repo_config = NULL;
75         const char *system_wide = NULL, *local;
77         local = getenv(CONFIG_ENVIRONMENT);
78         if (!local) {
79                 const char *home = getenv("HOME");
80                 local = getenv(CONFIG_LOCAL_ENVIRONMENT);
81                 if (!local)
82                         local = repo_config = xstrdup(git_path("config"));
83                 if (home)
84                         global = xstrdup(mkpath("%s/.gitconfig", home));
85                 system_wide = git_etc_gitconfig();
86         }
88         key = xstrdup(key_);
89         for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
90                 *tl = tolower(*tl);
91         for (tl=key; *tl && *tl != '.'; ++tl)
92                 *tl = tolower(*tl);
94         if (use_key_regexp) {
95                 key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
96                 if (regcomp(key_regexp, key, REG_EXTENDED)) {
97                         fprintf(stderr, "Invalid key pattern: %s\n", key_);
98                         goto free_strings;
99                 }
100         }
102         if (regex_) {
103                 if (regex_[0] == '!') {
104                         do_not_match = 1;
105                         regex_++;
106                 }
108                 regexp = (regex_t*)xmalloc(sizeof(regex_t));
109                 if (regcomp(regexp, regex_, REG_EXTENDED)) {
110                         fprintf(stderr, "Invalid pattern: %s\n", regex_);
111                         goto free_strings;
112                 }
113         }
115         if (do_all && system_wide)
116                 git_config_from_file(show_config, system_wide);
117         if (do_all && global)
118                 git_config_from_file(show_config, global);
119         git_config_from_file(show_config, local);
120         if (!do_all && !seen && global)
121                 git_config_from_file(show_config, global);
122         if (!do_all && !seen && system_wide)
123                 git_config_from_file(show_config, system_wide);
125         free(key);
126         if (regexp) {
127                 regfree(regexp);
128                 free(regexp);
129         }
131         if (do_all)
132                 ret = !seen;
133         else
134                 ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
136 free_strings:
137         free(repo_config);
138         free(global);
139         return ret;
142 char *normalize_value(const char *key, const char *value)
144         char *normalized;
146         if (!value)
147                 return NULL;
149         if (type == T_RAW)
150                 normalized = xstrdup(value);
151         else {
152                 normalized = xmalloc(64);
153                 if (type == T_INT) {
154                         int v = git_config_int(key, value);
155                         sprintf(normalized, "%d", v);
156                 }
157                 else if (type == T_BOOL)
158                         sprintf(normalized, "%s",
159                                 git_config_bool(key, value) ? "true" : "false");
160         }
162         return normalized;
165 static int get_color_found;
166 static const char *get_color_slot;
167 static char parsed_color[COLOR_MAXLEN];
169 static int git_get_color_config(const char *var, const char *value)
171         if (!strcmp(var, get_color_slot)) {
172                 color_parse(value, var, parsed_color);
173                 get_color_found = 1;
174         }
175         return 0;
178 static int get_color(int argc, const char **argv)
180         /*
181          * grab the color setting for the given slot from the configuration,
182          * or parse the default value if missing, and return ANSI color
183          * escape sequence.
184          *
185          * e.g.
186          * git config --get-color color.diff.whitespace "blue reverse"
187          */
188         const char *def_color = NULL;
190         switch (argc) {
191         default:
192                 usage(git_config_set_usage);
193         case 2:
194                 def_color = argv[1];
195                 /* fallthru */
196         case 1:
197                 get_color_slot = argv[0];
198                 break;
199         }
201         get_color_found = 0;
202         parsed_color[0] = '\0';
203         git_config(git_get_color_config);
205         if (!get_color_found && def_color)
206                 color_parse(def_color, "command line", parsed_color);
208         fputs(parsed_color, stdout);
209         return 0;
212 int cmd_config(int argc, const char **argv, const char *prefix)
214         int nongit = 0;
215         char* value;
216         const char *file = setup_git_directory_gently(&nongit);
218         while (1 < argc) {
219                 if (!strcmp(argv[1], "--int"))
220                         type = T_INT;
221                 else if (!strcmp(argv[1], "--bool"))
222                         type = T_BOOL;
223                 else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
224                         if (argc != 2)
225                                 usage(git_config_set_usage);
226                         if (git_config(show_all_config) < 0 && file && errno)
227                                 die("unable to read config file %s: %s", file,
228                                     strerror(errno));
229                         return 0;
230                 }
231                 else if (!strcmp(argv[1], "--global")) {
232                         char *home = getenv("HOME");
233                         if (home) {
234                                 char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
235                                 setenv(CONFIG_ENVIRONMENT, user_config, 1);
236                                 free(user_config);
237                         } else {
238                                 die("$HOME not set");
239                         }
240                 }
241                 else if (!strcmp(argv[1], "--system"))
242                         setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
243                 else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
244                         if (argc < 3)
245                                 usage(git_config_set_usage);
246                         if (!is_absolute_path(argv[2]) && file)
247                                 file = prefix_filename(file, strlen(file),
248                                                        argv[2]);
249                         else
250                                 file = argv[2];
251                         setenv(CONFIG_ENVIRONMENT, file, 1);
252                         argc--;
253                         argv++;
254                 }
255                 else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
256                         term = '\0';
257                         delim = '\n';
258                         key_delim = '\n';
259                 }
260                 else if (!strcmp(argv[1], "--rename-section")) {
261                         int ret;
262                         if (argc != 4)
263                                 usage(git_config_set_usage);
264                         ret = git_config_rename_section(argv[2], argv[3]);
265                         if (ret < 0)
266                                 return ret;
267                         if (ret == 0) {
268                                 fprintf(stderr, "No such section!\n");
269                                 return 1;
270                         }
271                         return 0;
272                 }
273                 else if (!strcmp(argv[1], "--remove-section")) {
274                         int ret;
275                         if (argc != 3)
276                                 usage(git_config_set_usage);
277                         ret = git_config_rename_section(argv[2], NULL);
278                         if (ret < 0)
279                                 return ret;
280                         if (ret == 0) {
281                                 fprintf(stderr, "No such section!\n");
282                                 return 1;
283                         }
284                         return 0;
285                 } else if (!strcmp(argv[1], "--get-color")) {
286                         return get_color(argc-2, argv+2);
287                 } else
288                         break;
289                 argc--;
290                 argv++;
291         }
293         switch (argc) {
294         case 2:
295                 return get_value(argv[1], NULL);
296         case 3:
297                 if (!strcmp(argv[1], "--unset"))
298                         return git_config_set(argv[2], NULL);
299                 else if (!strcmp(argv[1], "--unset-all"))
300                         return git_config_set_multivar(argv[2], NULL, NULL, 1);
301                 else if (!strcmp(argv[1], "--get"))
302                         return get_value(argv[2], NULL);
303                 else if (!strcmp(argv[1], "--get-all")) {
304                         do_all = 1;
305                         return get_value(argv[2], NULL);
306                 } else if (!strcmp(argv[1], "--get-regexp")) {
307                         show_keys = 1;
308                         use_key_regexp = 1;
309                         do_all = 1;
310                         return get_value(argv[2], NULL);
311                 } else {
312                         value = normalize_value(argv[1], argv[2]);
313                         return git_config_set(argv[1], value);
314                 }
315         case 4:
316                 if (!strcmp(argv[1], "--unset"))
317                         return git_config_set_multivar(argv[2], NULL, argv[3], 0);
318                 else if (!strcmp(argv[1], "--unset-all"))
319                         return git_config_set_multivar(argv[2], NULL, argv[3], 1);
320                 else if (!strcmp(argv[1], "--get"))
321                         return get_value(argv[2], argv[3]);
322                 else if (!strcmp(argv[1], "--get-all")) {
323                         do_all = 1;
324                         return get_value(argv[2], argv[3]);
325                 } else if (!strcmp(argv[1], "--get-regexp")) {
326                         show_keys = 1;
327                         use_key_regexp = 1;
328                         do_all = 1;
329                         return get_value(argv[2], argv[3]);
330                 } else if (!strcmp(argv[1], "--add")) {
331                         value = normalize_value(argv[2], argv[3]);
332                         return git_config_set_multivar(argv[2], value, "^$", 0);
333                 } else if (!strcmp(argv[1], "--replace-all")) {
334                         value = normalize_value(argv[2], argv[3]);
335                         return git_config_set_multivar(argv[2], value, NULL, 1);
336                 } else {
337                         value = normalize_value(argv[1], argv[2]);
338                         return git_config_set_multivar(argv[1], value, argv[3], 0);
339                 }
340         case 5:
341                 if (!strcmp(argv[1], "--replace-all")) {
342                         value = normalize_value(argv[2], argv[3]);
343                         return git_config_set_multivar(argv[2], value, argv[4], 1);
344                 }
345         case 1:
346         default:
347                 usage(git_config_set_usage);
348         }
349         return 0;