Imported Upstream version 3.7
[platform/upstream/ccache.git] / src / conf.c
1 // Copyright (C) 2011-2019 Joel Rosdahl
2 //
3 // This program is free software; you can redistribute it and/or modify it
4 // under the terms of the GNU General Public License as published by the Free
5 // Software Foundation; either version 3 of the License, or (at your option)
6 // any later version.
7 //
8 // This program is distributed in the hope that it will be useful, but WITHOUT
9 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 // more details.
12 //
13 // You should have received a copy of the GNU General Public License along with
14 // this program; if not, write to the Free Software Foundation, Inc., 51
15 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17 #include "conf.h"
18 #include "confitems.h"
19 #include "envtoconfitems.h"
20 #include "ccache.h"
21
22 enum handle_conf_result {
23         HANDLE_CONF_OK,
24         HANDLE_CONF_UNKNOWN,
25         HANDLE_CONF_FAIL
26 };
27
28 static const struct conf_item *
29 find_conf(const char *name)
30 {
31         return confitems_get(name, strlen(name));
32 }
33
34 static const struct env_to_conf_item *
35 find_env_to_conf(const char *name)
36 {
37         return envtoconfitems_get(name, strlen(name));
38 }
39
40 static enum handle_conf_result
41 handle_conf_setting(struct conf *conf, const char *key, const char *value,
42                     char **errmsg, bool from_env_variable, bool negate_boolean,
43                     const char *origin)
44 {
45         const struct conf_item *item = find_conf(key);
46         if (!item) {
47                 return HANDLE_CONF_UNKNOWN;
48         }
49
50         if (from_env_variable && item->parser == confitem_parse_bool) {
51                 // Special rule for boolean settings from the environment: "0", "false",
52                 // "disable" and "no" (case insensitive) are invalid, and all other values
53                 // mean true.
54                 //
55                 // Previously any value meant true, but this was surprising to users, who
56                 // might do something like CCACHE_DISABLE=0 and expect ccache to be
57                 // enabled.
58                 if (str_eq(value, "0") || strcasecmp(value, "false") == 0
59                     || strcasecmp(value, "disable") == 0 || strcasecmp(value, "no") == 0) {
60                         fatal("invalid boolean environment variable value \"%s\"", value);
61                 }
62
63                 bool *boolvalue = (bool *)((char *)conf + item->offset);
64                 *boolvalue = !negate_boolean;
65                 goto out;
66         }
67
68         if (!item->parser(value, (char *)conf + item->offset, errmsg)) {
69                 return HANDLE_CONF_FAIL;
70         }
71         if (item->verifier && !item->verifier((char *)conf + item->offset, errmsg)) {
72                 return HANDLE_CONF_FAIL;
73         }
74
75 out:
76         conf->item_origins[item->number] = origin;
77         return HANDLE_CONF_OK;
78 }
79
80 static bool
81 parse_line(const char *line, char **key, char **value, char **errmsg)
82 {
83 #define SKIP_WS(x) while (isspace(*x)) { ++x; }
84
85         *key = NULL;
86         *value = NULL;
87
88         const char *p = line;
89         SKIP_WS(p);
90         if (*p == '\0' || *p == '#') {
91                 return true;
92         }
93         const char *q = p;
94         while (isalpha(*q) || *q == '_') {
95                 ++q;
96         }
97         *key = x_strndup(p, q - p);
98         p = q;
99         SKIP_WS(p);
100         if (*p != '=') {
101                 *errmsg = x_strdup("missing equal sign");
102                 free(*key);
103                 *key = NULL;
104                 return false;
105         }
106         ++p;
107
108         // Skip leading whitespace.
109         SKIP_WS(p);
110         q = p;
111         while (*q) {
112                 ++q;
113         }
114         // Skip trailing whitespace.
115         while (isspace(q[-1])) {
116                 --q;
117         }
118         *value = x_strndup(p, q - p);
119
120         return true;
121
122 #undef SKIP_WS
123 }
124
125 // Create a conf struct with default values.
126 struct conf *
127 conf_create(void)
128 {
129         struct conf *conf = x_malloc(sizeof(*conf));
130         conf->base_dir = x_strdup("");
131         conf->cache_dir = format("%s/.ccache", get_home_directory());
132         conf->cache_dir_levels = 2;
133         conf->compiler = x_strdup("");
134         conf->compiler_check = x_strdup("mtime");
135         conf->compression = false;
136         conf->compression_level = 6;
137         conf->cpp_extension = x_strdup("");
138         conf->debug = false;
139         conf->depend_mode = false;
140         conf->direct_mode = true;
141         conf->disable = false;
142         conf->extra_files_to_hash = x_strdup("");
143         conf->hard_link = false;
144         conf->hash_dir = true;
145         conf->ignore_headers_in_manifest = x_strdup("");
146         conf->keep_comments_cpp = false;
147         conf->limit_multiple = 0.8;
148         conf->log_file = x_strdup("");
149         conf->max_files = 0;
150         conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000;
151         conf->path = x_strdup("");
152         conf->pch_external_checksum = false;
153         conf->prefix_command = x_strdup("");
154         conf->prefix_command_cpp = x_strdup("");
155         conf->read_only = false;
156         conf->read_only_direct = false;
157         conf->recache = false;
158         conf->run_second_cpp = true;
159         conf->sloppiness = 0;
160         conf->stats = true;
161         conf->temporary_dir = x_strdup("");
162         conf->umask = UINT_MAX; // Default: don't set umask.
163         conf->unify = false;
164         conf->item_origins = x_malloc(confitems_count() * sizeof(char *));
165         for (size_t i = 0; i < confitems_count(); ++i) {
166                 conf->item_origins[i] = "default";
167         }
168         return conf;
169 }
170
171 void
172 conf_free(struct conf *conf)
173 {
174         if (!conf) {
175                 return;
176         }
177         free(conf->base_dir);
178         free(conf->cache_dir);
179         free(conf->compiler);
180         free(conf->compiler_check);
181         free(conf->cpp_extension);
182         free(conf->extra_files_to_hash);
183         free(conf->ignore_headers_in_manifest);
184         free(conf->log_file);
185         free(conf->path);
186         free(conf->prefix_command);
187         free(conf->prefix_command_cpp);
188         free(conf->temporary_dir);
189         free((void *)conf->item_origins); // Workaround for MSVC warning
190         free(conf);
191 }
192
193 // Note: The path pointer is stored in conf, so path must outlive conf.
194 //
195 // On failure, if an I/O error occured errno is set appropriately, otherwise
196 // errno is set to zero indicating that config itself was invalid.
197 bool
198 conf_read(struct conf *conf, const char *path, char **errmsg)
199 {
200         assert(errmsg);
201         *errmsg = NULL;
202
203         FILE *f = fopen(path, "r");
204         if (!f) {
205                 *errmsg = format("%s: %s", path, strerror(errno));
206                 return false;
207         }
208
209         unsigned line_number = 0;
210         bool result = true;
211         char buf[10000];
212         while (fgets(buf, sizeof(buf), f)) {
213                 ++line_number;
214
215                 char *key;
216                 char *value;
217                 char *errmsg2;
218                 enum handle_conf_result hcr = HANDLE_CONF_OK;
219                 bool ok = parse_line(buf, &key, &value, &errmsg2);
220                 if (ok && key) { // key == NULL if comment or blank line.
221                         hcr =
222                                 handle_conf_setting(conf, key, value, &errmsg2, false, false, path);
223                         ok = hcr != HANDLE_CONF_FAIL; // unknown is OK
224                 }
225                 free(key);
226                 free(value);
227                 if (!ok) {
228                         *errmsg = format("%s:%u: %s", path, line_number, errmsg2);
229                         free(errmsg2);
230                         errno = 0;
231                         result = false;
232                         goto out;
233                 }
234         }
235         if (ferror(f)) {
236                 *errmsg = x_strdup(strerror(errno));
237                 result = false;
238         }
239
240 out:
241         fclose(f);
242         return result;
243 }
244
245 bool
246 conf_update_from_environment(struct conf *conf, char **errmsg)
247 {
248         for (char **p = environ; *p; ++p) {
249                 if (!str_startswith(*p, "CCACHE_")) {
250                         continue;
251                 }
252                 char *q = strchr(*p, '=');
253                 if (!q) {
254                         continue;
255                 }
256
257                 bool negate;
258                 size_t key_start;
259                 if (str_startswith(*p + 7, "NO")) {
260                         negate = true;
261                         key_start = 9;
262                 } else {
263                         negate = false;
264                         key_start = 7;
265                 }
266                 char *key = x_strndup(*p + key_start, q - *p - key_start);
267
268                 ++q; // Now points to the value.
269
270                 const struct env_to_conf_item *env_to_conf_item = find_env_to_conf(key);
271                 if (!env_to_conf_item) {
272                         free(key);
273                         continue;
274                 }
275
276                 char *errmsg2 = NULL;
277                 enum handle_conf_result hcr = handle_conf_setting(
278                         conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate,
279                         "environment");
280                 if (hcr != HANDLE_CONF_OK) {
281                         *errmsg = format("%s: %s", key, errmsg2);
282                         free(errmsg2);
283                         free(key);
284                         return false;
285                 }
286
287                 free(key);
288         }
289
290         return true;
291 }
292
293 bool
294 conf_set_value_in_file(const char *path, const char *key, const char *value,
295                        char **errmsg)
296 {
297         const struct conf_item *item = find_conf(key);
298         if (!item) {
299                 *errmsg = format("unknown configuration option \"%s\"", key);
300                 return false;
301         }
302
303         FILE *infile = fopen(path, "r");
304         if (!infile) {
305                 *errmsg = format("%s: %s", path, strerror(errno));
306                 return false;
307         }
308
309         char *outpath = format("%s.tmp", path);
310         FILE *outfile = create_tmp_file(&outpath, "w");
311         if (!outfile) {
312                 *errmsg = format("%s: %s", outpath, strerror(errno));
313                 free(outpath);
314                 fclose(infile);
315                 return false;
316         }
317
318         bool found = false;
319         char buf[10000];
320         while (fgets(buf, sizeof(buf), infile)) {
321                 char *key2;
322                 char *value2;
323                 char *errmsg2;
324                 bool ok = parse_line(buf, &key2, &value2, &errmsg2);
325                 if (ok && key2 && str_eq(key2, key)) {
326                         found = true;
327                         fprintf(outfile, "%s = %s\n", key, value);
328                 } else {
329                         fputs(buf, outfile);
330                 }
331                 free(key2);
332                 free(value2);
333         }
334
335         if (!found) {
336                 fprintf(outfile, "%s = %s\n", key, value);
337         }
338
339         fclose(infile);
340         fclose(outfile);
341         if (x_rename(outpath, path) != 0) {
342                 *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno));
343                 return false;
344         }
345         free(outpath);
346
347         return true;
348 }
349
350 bool
351 conf_print_value(struct conf *conf, const char *key,
352                  FILE *file, char **errmsg)
353 {
354         const struct conf_item *item = find_conf(key);
355         if (!item) {
356                 *errmsg = format("unknown configuration option \"%s\"", key);
357                 return false;
358         }
359         void *value = (char *)conf + item->offset;
360         char *str = item->formatter(value);
361         fprintf(file, "%s\n", str);
362         free(str);
363         return true;
364 }
365
366 static bool
367 print_item(struct conf *conf, const char *key,
368            void (*printer)(const char *descr, const char *origin,
369                            void *context),
370            void *context)
371 {
372         const struct conf_item *item = find_conf(key);
373         if (!item) {
374                 return false;
375         }
376         void *value = (char *)conf + item->offset;
377         char *str = item->formatter(value);
378         char *buf = x_strdup("");
379         reformat(&buf, "%s = %s", key, str);
380         printer(buf, conf->item_origins[item->number], context);
381         free(buf);
382         free(str);
383         return true;
384 }
385
386 bool
387 conf_print_items(struct conf *conf,
388                  void (*printer)(const char *descr, const char *origin,
389                                  void *context),
390                  void *context)
391 {
392         bool ok = true;
393         ok &= print_item(conf, "base_dir", printer, context);
394         ok &= print_item(conf, "cache_dir", printer, context);
395         ok &= print_item(conf, "cache_dir_levels", printer, context);
396         ok &= print_item(conf, "compiler", printer, context);
397         ok &= print_item(conf, "compiler_check", printer, context);
398         ok &= print_item(conf, "compression", printer, context);
399         ok &= print_item(conf, "compression_level", printer, context);
400         ok &= print_item(conf, "cpp_extension", printer, context);
401         ok &= print_item(conf, "debug", printer, context);
402         ok &= print_item(conf, "depend_mode", printer, context);
403         ok &= print_item(conf, "direct_mode", printer, context);
404         ok &= print_item(conf, "disable", printer, context);
405         ok &= print_item(conf, "extra_files_to_hash", printer, context);
406         ok &= print_item(conf, "hard_link", printer, context);
407         ok &= print_item(conf, "hash_dir", printer, context);
408         ok &= print_item(conf, "ignore_headers_in_manifest", printer, context);
409         ok &= print_item(conf, "keep_comments_cpp", printer, context);
410         ok &= print_item(conf, "limit_multiple", printer, context);
411         ok &= print_item(conf, "log_file", printer, context);
412         ok &= print_item(conf, "max_files", printer, context);
413         ok &= print_item(conf, "max_size", printer, context);
414         ok &= print_item(conf, "path", printer, context);
415         ok &= print_item(conf, "pch_external_checksum", printer, context);
416         ok &= print_item(conf, "prefix_command", printer, context);
417         ok &= print_item(conf, "prefix_command_cpp", printer, context);
418         ok &= print_item(conf, "read_only", printer, context);
419         ok &= print_item(conf, "read_only_direct", printer, context);
420         ok &= print_item(conf, "recache", printer, context);
421         ok &= print_item(conf, "run_second_cpp", printer, context);
422         ok &= print_item(conf, "sloppiness", printer, context);
423         ok &= print_item(conf, "stats", printer, context);
424         ok &= print_item(conf, "temporary_dir", printer, context);
425         ok &= print_item(conf, "umask", printer, context);
426         ok &= print_item(conf, "unify", printer, context);
427         return ok;
428 }