Make git config variable names case-insensitive
[git.git] / config.c
1 #include <ctype.h>
2
3 #include "cache.h"
4
5 #define MAXNAME (256)
6
7 static FILE *config_file;
8 static int config_linenr;
9 static int get_next_char(void)
10 {
11         int c;
12         FILE *f;
13
14         c = '\n';
15         if ((f = config_file) != NULL) {
16                 c = fgetc(f);
17                 if (c == '\n')
18                         config_linenr++;
19                 if (c == EOF) {
20                         config_file = NULL;
21                         c = '\n';
22                 }
23         }
24         return c;
25 }
26
27 static char *parse_value(void)
28 {
29         static char value[1024];
30         int quote = 0, comment = 0, len = 0, space = 0;
31
32         for (;;) {
33                 int c = get_next_char();
34                 if (len >= sizeof(value))
35                         return NULL;
36                 if (c == '\n') {
37                         if (quote)
38                                 return NULL;
39                         value[len] = 0;
40                         return value;
41                 }
42                 if (comment)
43                         continue;
44                 if (isspace(c) && !quote) {
45                         space = 1;
46                         continue;
47                 }
48                 if (space) {
49                         if (len)
50                                 value[len++] = ' ';
51                         space = 0;
52                 }
53                 if (c == '\\') {
54                         c = get_next_char();
55                         switch (c) {
56                         case '\n':
57                                 continue;
58                         case 't':
59                                 c = '\t';
60                                 break;
61                         case 'b':
62                                 c = '\b';
63                                 break;
64                         case 'n':
65                                 c = '\n';
66                                 break;
67                         /* Some characters escape as themselves */
68                         case '\\': case '"':
69                                 break;
70                         /* Reject unknown escape sequences */
71                         default:
72                                 return NULL;
73                         }
74                         value[len++] = c;
75                         continue;
76                 }
77                 if (c == '"') {
78                         quote = 1-quote;
79                         continue;
80                 }
81                 if (!quote) {
82                         if (c == ';' || c == '#') {
83                                 comment = 1;
84                                 continue;
85                         }
86                 }
87                 value[len++] = c;
88         }
89 }
90
91 static int get_value(config_fn_t fn, char *name, unsigned int len)
92 {
93         int c;
94         char *value;
95
96         /* Get the full name */
97         for (;;) {
98                 c = get_next_char();
99                 if (c == EOF)
100                         break;
101                 if (!isalnum(c))
102                         break;
103                 name[len++] = tolower(c);
104                 if (len >= MAXNAME)
105                         return -1;
106         }
107         name[len] = 0;
108         while (c == ' ' || c == '\t')
109                 c = get_next_char();
110
111         value = NULL;
112         if (c != '\n') {
113                 if (c != '=')
114                         return -1;
115                 value = parse_value();
116                 if (!value)
117                         return -1;
118         }
119         return fn(name, value);
120 }
121
122 static int get_base_var(char *name)
123 {
124         int baselen = 0;
125
126         for (;;) {
127                 int c = get_next_char();
128                 if (c == EOF)
129                         return -1;
130                 if (c == ']')
131                         return baselen;
132                 if (!isalnum(c))
133                         return -1;
134                 if (baselen > MAXNAME / 2)
135                         return -1;
136                 name[baselen++] = tolower(c);
137         }
138 }
139
140 static int git_parse_file(config_fn_t fn)
141 {
142         int comment = 0;
143         int baselen = 0;
144         static char var[MAXNAME];
145
146         for (;;) {
147                 int c = get_next_char();
148                 if (c == '\n') {
149                         /* EOF? */
150                         if (!config_file)
151                                 return 0;
152                         comment = 0;
153                         continue;
154                 }
155                 if (comment || isspace(c))
156                         continue;
157                 if (c == '#' || c == ';') {
158                         comment = 1;
159                         continue;
160                 }
161                 if (c == '[') {
162                         baselen = get_base_var(var);
163                         if (baselen <= 0)
164                                 break;
165                         var[baselen++] = '.';
166                         var[baselen] = 0;
167                         continue;
168                 }
169                 if (!isalpha(c))
170                         break;
171                 var[baselen] = tolower(c);
172                 if (get_value(fn, var, baselen+1) < 0)
173                         break;
174         }
175         die("bad config file line %d", config_linenr);
176 }
177
178 int git_config_int(const char *name, const char *value)
179 {
180         if (value && *value) {
181                 char *end;
182                 int val = strtol(value, &end, 0);
183                 if (!*end)
184                         return val;
185         }
186         die("bad config value for '%s'", name);
187 }
188
189 int git_config_bool(const char *name, const char *value)
190 {
191         if (!value)
192                 return 1;
193         if (!*value)
194                 return 0;
195         if (!strcasecmp(value, "true"))
196                 return 1;
197         if (!strcasecmp(value, "false"))
198                 return 0;
199         return git_config_int(name, value) != 0;
200 }
201
202 int git_default_config(const char *var, const char *value)
203 {
204         /* This needs a better name */
205         if (!strcmp(var, "core.filemode")) {
206                 trust_executable_bit = git_config_bool(var, value);
207                 return 0;
208         }
209
210         if (!strcmp(var, "user.name")) {
211                 strncpy(git_default_name, value, sizeof(git_default_name));
212                 return 0;
213         }
214
215         if (!strcmp(var, "user.email")) {
216                 strncpy(git_default_email, value, sizeof(git_default_email));
217                 return 0;
218         }
219
220         /* Add other config variables here.. */
221         return 0;
222 }
223
224 int git_config(config_fn_t fn)
225 {
226         int ret;
227         FILE *f = fopen(git_path("config"), "r");
228
229         ret = -1;
230         if (f) {
231                 config_file = f;
232                 config_linenr = 1;
233                 ret = git_parse_file(fn);
234                 fclose(f);
235         }
236         return ret;
237 }