Imported Upstream version 3.2.6
[platform/upstream/ccache.git] / conf.c
1 /*
2  * Copyright (C) 2011-2014 Joel Rosdahl
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 3 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 51
16  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "conf.h"
20 #include "ccache.h"
21
22 typedef bool (*conf_item_parser)(const char *str, void *result, char **errmsg);
23 typedef bool (*conf_item_verifier)(void *value, char **errmsg);
24
25 struct conf_item {
26         const char *name;
27         size_t number;
28         conf_item_parser parser;
29         size_t offset;
30         conf_item_verifier verifier;
31 };
32
33 struct env_to_conf_item {
34         const char *env_name;
35         const char *conf_name;
36 };
37
38 static bool
39 parse_bool(const char *str, void *result, char **errmsg)
40 {
41         bool *value = (bool *)result;
42
43         if (str_eq(str, "true")) {
44                 *value = true;
45                 return true;
46         } else if (str_eq(str, "false")) {
47                 *value = false;
48                 return true;
49         } else {
50                 *errmsg = format("not a boolean value: \"%s\"", str);
51                 return false;
52         }
53 }
54
55 static bool
56 parse_env_string(const char *str, void *result, char **errmsg)
57 {
58         char **value = (char **)result;
59         free(*value);
60         *value = subst_env_in_string(str, errmsg);
61         return *value != NULL;
62 }
63
64 static bool
65 parse_size(const char *str, void *result, char **errmsg)
66 {
67         uint64_t *value = (uint64_t *)result;
68         uint64_t size;
69         *errmsg = NULL;
70         if (parse_size_with_suffix(str, &size)) {
71                 *value = size;
72                 return true;
73         } else {
74                 *errmsg = format("invalid size: \"%s\"", str);
75                 return false;
76         }
77 }
78
79 static bool
80 parse_sloppiness(const char *str, void *result, char **errmsg)
81 {
82         unsigned *value = (unsigned *)result;
83         char *word, *p, *q, *saveptr = NULL;
84
85         if (!str) {
86                 return *value;
87         }
88         p = x_strdup(str);
89         q = p;
90         while ((word = strtok_r(q, ", ", &saveptr))) {
91                 if (str_eq(word, "file_macro")) {
92                         *value |= SLOPPY_FILE_MACRO;
93                 } else if (str_eq(word, "file_stat_matches")) {
94                         *value |= SLOPPY_FILE_STAT_MATCHES;
95                 } else if (str_eq(word, "include_file_ctime")) {
96                         *value |= SLOPPY_INCLUDE_FILE_CTIME;
97                 } else if (str_eq(word, "include_file_mtime")) {
98                         *value |= SLOPPY_INCLUDE_FILE_MTIME;
99                 } else if (str_eq(word, "pch_defines")) {
100                         *value |= SLOPPY_PCH_DEFINES;
101                 } else if (str_eq(word, "time_macros")) {
102                         *value |= SLOPPY_TIME_MACROS;
103                 } else {
104                         *errmsg = format("unknown sloppiness: \"%s\"", word);
105                         free(p);
106                         return false;
107                 }
108                 q = NULL;
109         }
110         free(p);
111         return true;
112 }
113
114 static bool
115 parse_string(const char *str, void *result, char **errmsg)
116 {
117         char **value = (char **)result;
118         (void)errmsg;
119         free(*value);
120         *value = x_strdup(str);
121         return true;
122 }
123
124 static bool
125 parse_umask(const char *str, void *result, char **errmsg)
126 {
127         unsigned *value = (unsigned *)result;
128         char *endptr;
129         if (str_eq(str, "")) {
130                 *value = UINT_MAX;
131                 return true;
132         }
133         errno = 0;
134         *value = strtoul(str, &endptr, 8);
135         if (errno == 0 && *str != '\0' && *endptr == '\0') {
136                 return true;
137         } else {
138                 *errmsg = format("not an octal integer: \"%s\"", str);
139                 return false;
140         }
141 }
142
143 static bool
144 parse_unsigned(const char *str, void *result, char **errmsg)
145 {
146         unsigned *value = (unsigned *)result;
147         long x;
148         char *endptr;
149         errno = 0;
150         x = strtol(str, &endptr, 10);
151         if (errno == 0 && x >= 0 && *str != '\0' && *endptr == '\0') {
152                 *value = x;
153                 return true;
154         } else {
155                 *errmsg = format("invalid unsigned integer: \"%s\"", str);
156                 return false;
157         }
158 }
159
160 static const char *
161 bool_to_string(bool value)
162 {
163         return value ? "true" : "false";
164 }
165
166 static bool
167 verify_absolute_path(void *value, char **errmsg)
168 {
169         char **path = (char **)value;
170         assert(*path);
171         if (str_eq(*path, "")) {
172                 /* The empty string means "disable" in this case. */
173                 return true;
174         } else if (is_absolute_path(*path)) {
175                 return true;
176         } else {
177                 *errmsg = format("not an absolute path: \"%s\"", *path);
178                 return false;
179         }
180 }
181
182 static bool
183 verify_dir_levels(void *value, char **errmsg)
184 {
185         unsigned *levels = (unsigned *)value;
186         assert(levels);
187         if (*levels >= 1 && *levels <= 8) {
188                 return true;
189         } else {
190                 *errmsg = format("cache directory levels must be between 1 and 8");
191                 return false;
192         }
193 }
194
195 #define ITEM(name, type) \
196   parse_ ## type, offsetof(struct conf, name), NULL
197 #define ITEM_V(name, type, verification) \
198   parse_ ## type, offsetof(struct conf, name), verify_ ## verification
199
200 #include "confitems_lookup.c"
201 #include "envtoconfitems_lookup.c"
202
203 static const struct conf_item *
204 find_conf(const char *name)
205 {
206         return confitems_get(name, strlen(name));
207 }
208
209 static const struct env_to_conf_item *
210 find_env_to_conf(const char *name)
211 {
212         return envtoconfitems_get(name, strlen(name));
213 }
214
215 static bool
216 handle_conf_setting(struct conf *conf, const char *key, const char *value,
217                     char **errmsg, bool from_env_variable, bool negate_boolean,
218                     const char *origin)
219 {
220         const struct conf_item *item;
221
222         item = find_conf(key);
223         if (!item) {
224                 *errmsg = format("unknown configuration option \"%s\"", key);
225                 return false;
226         }
227
228         if (from_env_variable && item->parser == parse_bool) {
229                 /*
230                  * Special rule for boolean settings from the environment: any value means
231                  * true.
232                  */
233                 bool *value = (bool *)((char *)conf + item->offset);
234                 *value = !negate_boolean;
235                 goto out;
236         }
237
238         if (!item->parser(value, (char *)conf + item->offset, errmsg)) {
239                 return false;
240         }
241         if (item->verifier && !item->verifier((char *)conf + item->offset, errmsg)) {
242                 return false;
243         }
244
245 out:
246         conf->item_origins[item->number] = origin;
247         return true;
248 }
249
250 static bool
251 parse_line(const char *line, char **key, char **value, char **errmsg)
252 {
253         const char *p, *q;
254
255 #define SKIP_WS(x) while (isspace(*x)) { ++x; }
256
257         *key = NULL;
258         *value = NULL;
259
260         p = line;
261         SKIP_WS(p);
262         if (*p == '\0' || *p == '#') {
263                 return true;
264         }
265         q = p;
266         while (isalpha(*q) || *q == '_') {
267                 ++q;
268         }
269         *key = x_strndup(p, q - p);
270         p = q;
271         SKIP_WS(p);
272         if (*p != '=') {
273                 *errmsg = x_strdup("missing equal sign");
274                 free(*key);
275                 *key = NULL;
276                 return false;
277         }
278         ++p;
279
280         /* Skip leading whitespace. */
281         SKIP_WS(p);
282         q = p;
283         while (*q) {
284                 ++q;
285         }
286         /* Skip trailing whitespace. */
287         while (isspace(q[-1])) {
288                 --q;
289         }
290         *value = x_strndup(p, q - p);
291
292         return true;
293
294 #undef SKIP_WS
295 }
296
297 /* Create a conf struct with default values. */
298 struct conf *
299 conf_create(void)
300 {
301         size_t i;
302         struct conf *conf = x_malloc(sizeof(*conf));
303         conf->base_dir = x_strdup("");
304         conf->cache_dir = format("%s/.ccache", get_home_directory());
305         conf->cache_dir_levels = 2;
306         conf->compiler = x_strdup("");
307         conf->compiler_check = x_strdup("mtime");
308         conf->compression = false;
309         conf->compression_level = 6;
310         conf->cpp_extension = x_strdup("");
311         conf->direct_mode = true;
312         conf->disable = false;
313         conf->extra_files_to_hash = x_strdup("");
314         conf->hard_link = false;
315         conf->hash_dir = false;
316         conf->log_file = x_strdup("");
317         conf->max_files = 0;
318         conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000;
319         conf->path = x_strdup("");
320         conf->prefix_command = x_strdup("");
321         conf->read_only = false;
322         conf->read_only_direct = false;
323         conf->recache = false;
324         conf->run_second_cpp = false;
325         conf->sloppiness = 0;
326         conf->stats = true;
327         conf->temporary_dir = x_strdup("");
328         conf->umask = UINT_MAX; /* default: don't set umask */
329         conf->unify = false;
330         conf->item_origins = x_malloc(CONFITEMS_TOTAL_KEYWORDS * sizeof(char *));
331         for (i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) {
332                 conf->item_origins[i] = "default";
333         }
334         return conf;
335 }
336
337 void
338 conf_free(struct conf *conf)
339 {
340         if (!conf) {
341                 return;
342         }
343         free(conf->base_dir);
344         free(conf->cache_dir);
345         free(conf->compiler);
346         free(conf->compiler_check);
347         free(conf->cpp_extension);
348         free(conf->extra_files_to_hash);
349         free(conf->log_file);
350         free(conf->path);
351         free(conf->prefix_command);
352         free(conf->temporary_dir);
353         free(conf->item_origins);
354         free(conf);
355 }
356
357 /* Note: The path pointer is stored in conf, so path must outlive conf. */
358 bool
359 conf_read(struct conf *conf, const char *path, char **errmsg)
360 {
361         FILE *f;
362         char buf[10000];
363         bool result = true;
364         unsigned line_number;
365
366         assert(errmsg);
367         *errmsg = NULL;
368
369         f = fopen(path, "r");
370         if (!f) {
371                 *errmsg = format("%s: %s", path, strerror(errno));
372                 return false;
373         }
374
375         line_number = 0;
376         while (fgets(buf, sizeof(buf), f)) {
377                 char *errmsg2, *key, *value;
378                 bool ok;
379                 ++line_number;
380                 ok = parse_line(buf, &key, &value, &errmsg2);
381                 if (ok && key) { /* key == NULL if comment or blank line */
382                         ok = handle_conf_setting(conf, key, value, &errmsg2, false, false, path);
383                 }
384                 free(key);
385                 free(value);
386                 if (!ok) {
387                         *errmsg = format("%s:%u: %s", path, line_number, errmsg2);
388                         free(errmsg2);
389                         result = false;
390                         goto out;
391                 }
392         }
393         if (ferror(f)) {
394                 *errmsg = x_strdup(strerror(errno));
395                 result = false;
396         }
397
398 out:
399         fclose(f);
400         return result;
401 }
402
403 bool
404 conf_update_from_environment(struct conf *conf, char **errmsg)
405 {
406         char **p;
407         char *q;
408         char *key;
409         char *errmsg2;
410         const struct env_to_conf_item *env_to_conf_item;
411         bool negate;
412         size_t key_start;
413
414         for (p = environ; *p; ++p) {
415                 if (!str_startswith(*p, "CCACHE_")) {
416                         continue;
417                 }
418                 q = strchr(*p, '=');
419                 if (!q) {
420                         continue;
421                 }
422
423                 if (str_startswith(*p + 7, "NO")) {
424                         negate = true;
425                         key_start = 9;
426                 } else {
427                         negate = false;
428                         key_start = 7;
429                 }
430                 key = x_strndup(*p + key_start, q - *p - key_start);
431
432                 ++q; /* Now points to the value. */
433
434                 env_to_conf_item = find_env_to_conf(key);
435                 if (!env_to_conf_item) {
436                         free(key);
437                         continue;
438                 }
439
440                 if (!handle_conf_setting(
441                       conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate,
442                       "environment")) {
443                         *errmsg = format("%s: %s", key, errmsg2);
444                         free(errmsg2);
445                         free(key);
446                         return false;
447                 }
448
449                 free(key);
450         }
451
452         return true;
453 }
454
455 bool
456 conf_set_value_in_file(const char *path, const char *key, const char *value,
457                        char **errmsg)
458 {
459         FILE *infile, *outfile;
460         char *outpath;
461         char buf[10000];
462         bool found;
463         const struct conf_item *item;
464
465         item = find_conf(key);
466         if (!item) {
467                 *errmsg = format("unknown configuration option \"%s\"", key);
468                 return false;
469         }
470
471         infile = fopen(path, "r");
472         if (!infile) {
473                 *errmsg = format("%s: %s", path, strerror(errno));
474                 return false;
475         }
476
477         outpath = format("%s.tmp", path);
478         outfile = create_tmp_file(&outpath, "w");
479         if (!outfile) {
480                 *errmsg = format("%s: %s", outpath, strerror(errno));
481                 free(outpath);
482                 fclose(infile);
483                 return false;
484         }
485
486         found = false;
487         while (fgets(buf, sizeof(buf), infile)) {
488                 char *errmsg2, *key2, *value2;
489                 bool ok;
490                 ok = parse_line(buf, &key2, &value2, &errmsg2);
491                 if (ok && key2 && str_eq(key2, key)) {
492                         found = true;
493                         fprintf(outfile, "%s = %s\n", key, value);
494                 } else {
495                         fputs(buf, outfile);
496                 }
497                 free(key2);
498                 free(value2);
499         }
500
501         if (!found) {
502                 fprintf(outfile, "%s = %s\n", key, value);
503         }
504
505         fclose(infile);
506         fclose(outfile);
507         if (x_rename(outpath, path) != 0) {
508                 *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno));
509                 return false;
510         }
511         free(outpath);
512
513         return true;
514 }
515
516 bool
517 conf_print_items(struct conf *conf,
518                  void (*printer)(const char *descr, const char *origin,
519                                  void *context),
520                  void *context)
521 {
522         char *s = x_strdup("");
523         char *s2;
524
525         reformat(&s, "base_dir = %s", conf->base_dir);
526         printer(s, conf->item_origins[find_conf("base_dir")->number], context);
527
528         reformat(&s, "cache_dir = %s", conf->cache_dir);
529         printer(s, conf->item_origins[find_conf("cache_dir")->number], context);
530
531         reformat(&s, "cache_dir_levels = %u", conf->cache_dir_levels);
532         printer(s, conf->item_origins[find_conf("cache_dir_levels")->number],
533                 context);
534
535         reformat(&s, "compiler = %s", conf->compiler);
536         printer(s, conf->item_origins[find_conf("compiler")->number], context);
537
538         reformat(&s, "compiler_check = %s", conf->compiler_check);
539         printer(s, conf->item_origins[find_conf("compiler_check")->number], context);
540
541         reformat(&s, "compression = %s", bool_to_string(conf->compression));
542         printer(s, conf->item_origins[find_conf("compression")->number], context);
543
544         reformat(&s, "compression_level = %u", conf->compression_level);
545         printer(s, conf->item_origins[find_conf("compression_level")->number],
546                 context);
547
548         reformat(&s, "cpp_extension = %s", conf->cpp_extension);
549         printer(s, conf->item_origins[find_conf("cpp_extension")->number], context);
550
551         reformat(&s, "direct_mode = %s", bool_to_string(conf->direct_mode));
552         printer(s, conf->item_origins[find_conf("direct_mode")->number], context);
553
554         reformat(&s, "disable = %s", bool_to_string(conf->disable));
555         printer(s, conf->item_origins[find_conf("disable")->number], context);
556
557         reformat(&s, "extra_files_to_hash = %s", conf->extra_files_to_hash);
558         printer(s, conf->item_origins[find_conf("extra_files_to_hash")->number],
559                 context);
560
561         reformat(&s, "hard_link = %s", bool_to_string(conf->hard_link));
562         printer(s, conf->item_origins[find_conf("hard_link")->number], context);
563
564         reformat(&s, "hash_dir = %s", bool_to_string(conf->hash_dir));
565         printer(s, conf->item_origins[find_conf("hash_dir")->number], context);
566
567         reformat(&s, "log_file = %s", conf->log_file);
568         printer(s, conf->item_origins[find_conf("log_file")->number], context);
569
570         reformat(&s, "max_files = %u", conf->max_files);
571         printer(s, conf->item_origins[find_conf("max_files")->number], context);
572
573         s2 = format_parsable_size_with_suffix(conf->max_size);
574         reformat(&s, "max_size = %s", s2);
575         printer(s, conf->item_origins[find_conf("max_size")->number], context);
576         free(s2);
577
578         reformat(&s, "path = %s", conf->path);
579         printer(s, conf->item_origins[find_conf("path")->number], context);
580
581         reformat(&s, "prefix_command = %s", conf->prefix_command);
582         printer(s, conf->item_origins[find_conf("prefix_command")->number], context);
583
584         reformat(&s, "read_only = %s", bool_to_string(conf->read_only));
585         printer(s, conf->item_origins[find_conf("read_only")->number], context);
586
587         reformat(&s, "read_only_direct = %s", bool_to_string(conf->read_only_direct));
588         printer(s, conf->item_origins[find_conf("read_only_direct")->number],
589                 context);
590
591         reformat(&s, "recache = %s", bool_to_string(conf->recache));
592         printer(s, conf->item_origins[find_conf("recache")->number], context);
593
594         reformat(&s, "run_second_cpp = %s", bool_to_string(conf->run_second_cpp));
595         printer(s, conf->item_origins[find_conf("run_second_cpp")->number], context);
596
597         reformat(&s, "sloppiness = ");
598         if (conf->sloppiness & SLOPPY_FILE_MACRO) {
599                 reformat(&s, "%sfile_macro, ", s);
600         }
601         if (conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME) {
602                 reformat(&s, "%sinclude_file_mtime, ", s);
603         }
604         if (conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME) {
605                 reformat(&s, "%sinclude_file_ctime, ", s);
606         }
607         if (conf->sloppiness & SLOPPY_TIME_MACROS) {
608                 reformat(&s, "%stime_macros, ", s);
609         }
610         if (conf->sloppiness & SLOPPY_PCH_DEFINES) {
611                 reformat(&s, "%spch_defines, ", s);
612         }
613         if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
614                 reformat(&s, "%sfile_stat_matches, ", s);
615         }
616         if (conf->sloppiness) {
617                 /* Strip last ", ". */
618                 s[strlen(s) - 2] = '\0';
619         }
620         printer(s, conf->item_origins[find_conf("sloppiness")->number], context);
621
622         reformat(&s, "stats = %s", bool_to_string(conf->stats));
623         printer(s, conf->item_origins[find_conf("stats")->number], context);
624
625         reformat(&s, "temporary_dir = %s", conf->temporary_dir);
626         printer(s, conf->item_origins[find_conf("temporary_dir")->number], context);
627
628         if (conf->umask == UINT_MAX) {
629                 reformat(&s, "umask = ");
630         } else {
631                 reformat(&s, "umask = %03o", conf->umask);
632         }
633         printer(s, conf->item_origins[find_conf("umask")->number], context);
634
635         reformat(&s, "unify = %s", bool_to_string(conf->unify));
636         printer(s, conf->item_origins[find_conf("unify")->number], context);
637
638         free(s);
639         return true;
640 }