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