]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
config: Track and print configuration item origins
authorJoel Rosdahl <joel@rosdahl.net>
Fri, 29 Jul 2011 18:58:08 +0000 (20:58 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Fri, 29 Jul 2011 19:01:19 +0000 (21:01 +0200)
ccache.c
conf.c
conf.h
confitems.gperf
confitems_lookup.c
dev.mk.in
envtoconfitems_lookup.c
test/test_conf.c

index 920a7d97a7928a71834fbfce375bcd59a8f60210..072dbf3aab05f592669dfec9ca2d90c41739d623 100644 (file)
--- a/ccache.c
+++ b/ccache.c
@@ -77,6 +77,9 @@ struct conf *conf = NULL;
 /* Where to write configuration changes. */
 char *primary_config_path = NULL;
 
+/* Secondary, read-only configuration file (if any). */
+char *secondary_config_path = NULL;
+
 /* current working directory taken from $PWD, or getcwd() if $PWD is bad */
 char *current_working_dir = NULL;
 
@@ -1832,16 +1835,13 @@ initialize(void)
        if (p) {
                primary_config_path = x_strdup(p);
        } else {
-               char *sysconf_config_path;
-
-               sysconf_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR));
-               if (!conf_read(conf, sysconf_config_path, &errmsg)) {
-                       if (stat(sysconf_config_path, &st) == 0) {
+               secondary_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR));
+               if (!conf_read(conf, secondary_config_path, &errmsg)) {
+                       if (stat(secondary_config_path, &st) == 0) {
                                fatal("%s", errmsg);
                        }
                        /* Missing config file in SYSCONFDIR is OK. */
                }
-               free(sysconf_config_path);
 
                if ((p = getenv("CCACHE_DIR"))) {
                        free(conf->cache_dir);
@@ -1885,6 +1885,7 @@ cc_reset(void)
 {
        conf_free(conf); conf = NULL;
        free(primary_config_path); primary_config_path = NULL;
+       free(secondary_config_path); secondary_config_path = NULL;
        free(current_working_dir); current_working_dir = NULL;
        free(input_file); input_file = NULL;
        free(output_obj); output_obj = NULL;
@@ -2083,12 +2084,12 @@ ccache(int argc, char *argv[])
 }
 
 static void
-conf_printer(const char *s, void *context)
+conf_printer(const char *descr, const char *origin, void *context)
 {
        if (context == NULL) {
-               cc_log("%s\n", s);
+               cc_log("(%s) %s", origin, descr);
        } else {
-               fprintf(context, "%s\n", s);
+               fprintf(context, "(%s) %s\n", origin, descr);
        }
 }
 
diff --git a/conf.c b/conf.c
index 10e29768b2030c24baba965b204dbd0b63678401..3747b46f43ec1c189aefd4cfd53a37cb547fd445 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -24,6 +24,7 @@ typedef bool (*conf_item_verifier)(void *value, char **errmsg);
 
 struct conf_item {
        const char *name;
+       size_t number;
        conf_item_parser parser;
        size_t offset;
        conf_item_verifier verifier;
@@ -202,7 +203,8 @@ find_env_to_conf(const char *name)
 
 static bool
 handle_conf_setting(struct conf *conf, const char *key, const char *value,
-                    char **errmsg, bool from_env_variable, bool negate_boolean)
+                    char **errmsg, bool from_env_variable, bool negate_boolean,
+                    const char *origin)
 {
        const struct conf_item *item;
 
@@ -219,7 +221,7 @@ handle_conf_setting(struct conf *conf, const char *key, const char *value,
                 */
                bool *value = (bool *)((void *)conf + item->offset);
                *value = !negate_boolean;
-               return true;
+               goto out;
        }
 
        if (!item->parser(value, (void *)conf + item->offset, errmsg)) {
@@ -229,6 +231,8 @@ handle_conf_setting(struct conf *conf, const char *key, const char *value,
                return false;
        }
 
+out:
+       conf->item_origins[item->number] = origin;
        return true;
 }
 
@@ -283,6 +287,7 @@ parse_line(const char *line, char **key, char **value, char **errmsg)
 struct conf *
 conf_create(void)
 {
+       size_t i;
        struct conf *conf = x_malloc(sizeof(*conf));
        conf->base_dir = x_strdup("");
        conf->cache_dir = format("%s/.ccache", get_home_directory());
@@ -310,6 +315,10 @@ conf_create(void)
        conf->temporary_dir = x_strdup("");
        conf->umask = UINT_MAX; /* default: don't set umask */
        conf->unify = false;
+       conf->item_origins = x_malloc(CONFITEMS_TOTAL_KEYWORDS * sizeof(char*));
+       for (i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) {
+               conf->item_origins[i] = "default";
+       }
        return conf;
 }
 
@@ -329,9 +338,11 @@ conf_free(struct conf *conf)
        free(conf->path);
        free(conf->prefix_command);
        free(conf->temporary_dir);
+       free(conf->item_origins);
        free(conf);
 }
 
+/* Note: The path pointer is stored in conf, so path must outlive conf. */
 bool
 conf_read(struct conf *conf, const char *path, char **errmsg)
 {
@@ -356,7 +367,7 @@ conf_read(struct conf *conf, const char *path, char **errmsg)
                ++line_number;
                ok = parse_line(buf, &key, &value, &errmsg2);
                if (ok && key) { /* key == NULL if comment or blank line */
-                       ok = handle_conf_setting(conf, key, value, &errmsg2, false, false);
+                       ok = handle_conf_setting(conf, key, value, &errmsg2, false, false, path);
                }
                free(key);
                free(value);
@@ -415,7 +426,8 @@ conf_update_from_environment(struct conf *conf, char **errmsg)
                }
 
                if (!handle_conf_setting(
-                           conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate)) {
+                           conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate,
+                           "environment")) {
                        *errmsg = format("%s: %s", key, errmsg2);
                        free(errmsg2);
                        free(key);
@@ -489,77 +501,79 @@ conf_set_value_in_file(const char *path, const char *key, const char *value,
        return true;
 }
 
-bool conf_print_items(struct conf *conf,
-                      void(*printer)(const char *s, void *context),
-                      void *context)
+bool
+conf_print_items(struct conf *conf,
+                 void (*printer)(const char *descr, const char *origin,
+                                 void *context),
+                 void *context)
 {
        char *s = x_strdup("");
        char *s2;
 
        reformat(&s, "base_dir = %s", conf->base_dir);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("base_dir")->number], context);
 
        reformat(&s, "cache_dir = %s", conf->cache_dir);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("cache_dir")->number], context);
 
        reformat(&s, "cache_dir_levels = %u", conf->cache_dir_levels);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("cache_dir_levels")->number], context);
 
        reformat(&s, "compiler = %s", conf->compiler);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("compiler")->number], context);
 
        reformat(&s, "compiler_check = %s", conf->compiler_check);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("compiler_check")->number], context);
 
        reformat(&s, "compression = %s", conf->compression ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("compression")->number], context);
 
        reformat(&s, "cpp_extension = %s", conf->cpp_extension);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("cpp_extension")->number], context);
 
        reformat(&s, "detect_shebang = %s", conf->detect_shebang ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("detect_shebang")->number], context);
 
        reformat(&s, "direct_mode = %s", conf->direct_mode ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("direct_mode")->number], context);
 
        reformat(&s, "disable = %s", conf->disable ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("disable")->number], context);
 
        reformat(&s, "extra_files_to_hash = %s", conf->extra_files_to_hash);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("extra_files_to_hash")->number], context);
 
        reformat(&s, "hard_link = %s", conf->hard_link ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("hard_link")->number], context);
 
        reformat(&s, "hash_dir = %s", conf->hash_dir ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("hash_dir")->number], context);
 
        reformat(&s, "log_file = %s", conf->log_file);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("log_file")->number], context);
 
        reformat(&s, "max_files = %u", conf->max_files);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("max_files")->number], context);
 
        s2 = format_parsable_size_with_suffix(conf->max_size);
        reformat(&s, "max_size = %s", s2);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("max_size")->number], context);
        free(s2);
 
        reformat(&s, "path = %s", conf->path);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("path")->number], context);
 
        reformat(&s, "prefix_command = %s", conf->prefix_command);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("prefix_command")->number], context);
 
        reformat(&s, "read_only = %s", conf->read_only ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("read_only")->number], context);
 
        reformat(&s, "recache = %s", conf->recache ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("recache")->number], context);
 
        reformat(&s, "run_second_cpp = %s", conf->run_second_cpp ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("run_second_cpp")->number], context);
 
        reformat(&s, "sloppiness = ");
        if (conf->sloppiness & SLOPPY_FILE_MACRO) {
@@ -575,23 +589,23 @@ bool conf_print_items(struct conf *conf,
                /* Strip last ", ". */
                s[strlen(s) - 2] = '\0';
        }
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("sloppiness")->number], context);
 
        reformat(&s, "stats = %s", conf->stats ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("stats")->number], context);
 
        reformat(&s, "temporary_dir = %s", conf->temporary_dir);
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("temporary_dir")->number], context);
 
        if (conf->umask == UINT_MAX) {
-               printer("umask = ", context);
+               reformat(&s, "umask = ");
        } else {
                reformat(&s, "umask = %03o", conf->umask);
-               printer(s, context);
        }
+       printer(s, conf->item_origins[find_conf("umask")->number], context);
 
        reformat(&s, "unify = %s", conf->unify ? "true" : "false");
-       printer(s, context);
+       printer(s, conf->item_origins[find_conf("unify")->number], context);
 
        free(s);
        return true;
diff --git a/conf.h b/conf.h
index d6fef70e95d0d42f6bf4ccf5859660014a68c531..d4e9587c458965de0986b49d1e140cddc3c615c9 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -30,6 +30,8 @@ struct conf {
        char *temporary_dir;
        unsigned umask;
        bool unify;
+
+       const char **item_origins;
 };
 
 struct conf *conf_create(void);
@@ -39,7 +41,8 @@ bool conf_update_from_environment(struct conf *conf, char **errmsg);
 bool conf_set_value_in_file(const char *path, const char *key,
                             const char *value, char **errmsg);
 bool conf_print_items(struct conf *conf,
-                      void(*printer)(const char *s, void *context),
+                      void (*printer)(const char *descr, const char *origin,
+                                      void *context),
                       void *context);
 
 #endif
index 353ff653004a282805c2790e339712c84ea7f511..fe67b84c441fc105ef29134ee6c84391a702a9da 100644 (file)
@@ -4,32 +4,32 @@
 %readonly-tables
 %define hash-function-name confitems_hash
 %define lookup-function-name confitems_get
-%define initializer-suffix ,NULL,0,NULL
+%define initializer-suffix ,0,NULL,0,NULL
 struct conf_item;
 %%
-base_dir, ITEM_V(base_dir, env_string, absolute_path)
-cache_dir, ITEM(cache_dir, env_string)
-cache_dir_levels, ITEM_V(cache_dir_levels, unsigned, dir_levels)
-compiler, ITEM(compiler, string)
-compiler_check, ITEM(compiler_check, string)
-compression, ITEM(compression, bool)
-cpp_extension, ITEM(cpp_extension, string)
-detect_shebang, ITEM(detect_shebang, bool)
-direct_mode, ITEM(direct_mode, bool)
-disable, ITEM(disable, bool)
-extra_files_to_hash, ITEM(extra_files_to_hash, env_string)
-hard_link, ITEM(hard_link, bool)
-hash_dir, ITEM(hash_dir, bool)
-log_file, ITEM(log_file, env_string)
-max_files, ITEM(max_files, unsigned)
-max_size, ITEM(max_size, size)
-path, ITEM(path, env_string)
-prefix_command, ITEM(prefix_command, env_string)
-read_only, ITEM(read_only, bool)
-recache, ITEM(recache, bool)
-run_second_cpp, ITEM(run_second_cpp, bool)
-sloppiness, ITEM(sloppiness, sloppiness)
-stats, ITEM(stats, bool)
-temporary_dir, ITEM(temporary_dir, env_string)
-umask, ITEM(umask, umask)
-unify, ITEM(unify, bool)
+base_dir,             0, ITEM_V(base_dir, env_string, absolute_path)
+cache_dir,            1, ITEM(cache_dir, env_string)
+cache_dir_levels,     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)
+compiler,             3, ITEM(compiler, string)
+compiler_check,       4, ITEM(compiler_check, string)
+compression,          5, ITEM(compression, bool)
+cpp_extension,        6, ITEM(cpp_extension, string)
+detect_shebang,       7, ITEM(detect_shebang, bool)
+direct_mode,          8, ITEM(direct_mode, bool)
+disable,              9, ITEM(disable, bool)
+extra_files_to_hash, 10, ITEM(extra_files_to_hash, env_string)
+hard_link,           11, ITEM(hard_link, bool)
+hash_dir,            12, ITEM(hash_dir, bool)
+log_file,            13, ITEM(log_file, env_string)
+max_files,           14, ITEM(max_files, unsigned)
+max_size,            15, ITEM(max_size, size)
+path,                16, ITEM(path, env_string)
+prefix_command,      17, ITEM(prefix_command, env_string)
+read_only,           18, ITEM(read_only, bool)
+recache,             19, ITEM(recache, bool)
+run_second_cpp,      20, ITEM(run_second_cpp, bool)
+sloppiness,          21, ITEM(sloppiness, sloppiness)
+stats,               22, ITEM(stats, bool)
+temporary_dir,       23, ITEM(temporary_dir, env_string)
+umask,               24, ITEM(umask, umask)
+unify,               25, ITEM(unify, bool)
index 9635e592e216b320acbcd32e95607fba4d1737d8..1fc88e035180ec6aad05ce2b5d974cd32c58f14d 100644 (file)
@@ -95,69 +95,69 @@ confitems_get (register const char *str, register unsigned int len)
 
   static const struct conf_item wordlist[] =
     {
-      {"",NULL,0,NULL}, {"",NULL,0,NULL}, {"",NULL,0,NULL},
-      {"",NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 26 "confitems.gperf"
-      {"path", ITEM(path, env_string)},
+      {"path",                16, ITEM(path, env_string)},
 #line 32 "confitems.gperf"
-      {"stats", ITEM(stats, bool)},
-      {"",NULL,0,NULL}, {"",NULL,0,NULL},
+      {"stats",               22, ITEM(stats, bool)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 13 "confitems.gperf"
-      {"compiler", ITEM(compiler, string)},
+      {"compiler",             3, ITEM(compiler, string)},
 #line 11 "confitems.gperf"
-      {"cache_dir", ITEM(cache_dir, env_string)},
+      {"cache_dir",            1, ITEM(cache_dir, env_string)},
 #line 35 "confitems.gperf"
-      {"unify", ITEM(unify, bool)},
+      {"unify",               25, ITEM(unify, bool)},
 #line 15 "confitems.gperf"
-      {"compression", ITEM(compression, bool)},
-      {"",NULL,0,NULL},
+      {"compression",          5, ITEM(compression, bool)},
+      {"",0,NULL,0,NULL},
 #line 16 "confitems.gperf"
-      {"cpp_extension", ITEM(cpp_extension, string)},
+      {"cpp_extension",        6, ITEM(cpp_extension, string)},
 #line 14 "confitems.gperf"
-      {"compiler_check", ITEM(compiler_check, string)},
-      {"",NULL,0,NULL},
+      {"compiler_check",       4, ITEM(compiler_check, string)},
+      {"",0,NULL,0,NULL},
 #line 12 "confitems.gperf"
-      {"cache_dir_levels", ITEM_V(cache_dir_levels, unsigned, dir_levels)},
+      {"cache_dir_levels",     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)},
 #line 19 "confitems.gperf"
-      {"disable", ITEM(disable, bool)},
+      {"disable",              9, ITEM(disable, bool)},
 #line 25 "confitems.gperf"
-      {"max_size", ITEM(max_size, size)},
+      {"max_size",            15, ITEM(max_size, size)},
 #line 24 "confitems.gperf"
-      {"max_files", ITEM(max_files, unsigned)},
+      {"max_files",           14, ITEM(max_files, unsigned)},
 #line 34 "confitems.gperf"
-      {"umask", ITEM(umask, umask)},
+      {"umask",               24, ITEM(umask, umask)},
 #line 18 "confitems.gperf"
-      {"direct_mode", ITEM(direct_mode, bool)},
-      {"",NULL,0,NULL},
+      {"direct_mode",          8, ITEM(direct_mode, bool)},
+      {"",0,NULL,0,NULL},
 #line 23 "confitems.gperf"
-      {"log_file", ITEM(log_file, env_string)},
+      {"log_file",            13, ITEM(log_file, env_string)},
 #line 27 "confitems.gperf"
-      {"prefix_command", ITEM(prefix_command, env_string)},
+      {"prefix_command",      17, ITEM(prefix_command, env_string)},
 #line 31 "confitems.gperf"
-      {"sloppiness", ITEM(sloppiness, sloppiness)},
+      {"sloppiness",          21, ITEM(sloppiness, sloppiness)},
 #line 22 "confitems.gperf"
-      {"hash_dir", ITEM(hash_dir, bool)},
+      {"hash_dir",            12, ITEM(hash_dir, bool)},
 #line 21 "confitems.gperf"
-      {"hard_link", ITEM(hard_link, bool)},
+      {"hard_link",           11, ITEM(hard_link, bool)},
 #line 33 "confitems.gperf"
-      {"temporary_dir", ITEM(temporary_dir, env_string)},
+      {"temporary_dir",       23, ITEM(temporary_dir, env_string)},
 #line 30 "confitems.gperf"
-      {"run_second_cpp", ITEM(run_second_cpp, bool)},
-      {"",NULL,0,NULL}, {"",NULL,0,NULL},
+      {"run_second_cpp",      20, ITEM(run_second_cpp, bool)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 29 "confitems.gperf"
-      {"recache", ITEM(recache, bool)},
+      {"recache",             19, ITEM(recache, bool)},
 #line 10 "confitems.gperf"
-      {"base_dir", ITEM_V(base_dir, env_string, absolute_path)},
+      {"base_dir",             0, ITEM_V(base_dir, env_string, absolute_path)},
 #line 28 "confitems.gperf"
-      {"read_only", ITEM(read_only, bool)},
-      {"",NULL,0,NULL}, {"",NULL,0,NULL}, {"",NULL,0,NULL},
-      {"",NULL,0,NULL},
+      {"read_only",           18, ITEM(read_only, bool)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 17 "confitems.gperf"
-      {"detect_shebang", ITEM(detect_shebang, bool)},
-      {"",NULL,0,NULL}, {"",NULL,0,NULL}, {"",NULL,0,NULL},
-      {"",NULL,0,NULL},
+      {"detect_shebang",       7, ITEM(detect_shebang, bool)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 20 "confitems.gperf"
-      {"extra_files_to_hash", ITEM(extra_files_to_hash, env_string)}
+      {"extra_files_to_hash", 10, ITEM(extra_files_to_hash, env_string)}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -174,3 +174,4 @@ confitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
+static const size_t CONFITEMS_TOTAL_KEYWORDS = 26;
index cd4874b28f0db1e82a3363725c0d7f3990109e4c..fd103ca783e81853ed57282a7b172eefe09a516f 100644 (file)
--- a/dev.mk.in
+++ b/dev.mk.in
@@ -35,9 +35,9 @@ files_to_distclean += .deps version.c dev.mk
 
 source_dist_files = \
     main.c $(base_sources) $(test_sources) $(headers) zlib/*.c zlib/*.h \
-    confitems_lookup.c envtoconfitems_lookup.c \
     config.h.in configure install-sh Makefile.in test.sh GPL-3.0.txt \
     AUTHORS.txt INSTALL.txt LICENSE.txt MANUAL.txt NEWS.txt README.txt
+built_dist_files += confitems_lookup.c envtoconfitems_lookup.c
 dist_files = \
     $(addprefix $(srcdir)/, $(source_dist_files)) \
     $(built_dist_files)
@@ -49,6 +49,7 @@ version.o: version.c
 
 %_lookup.c: %.gperf
        $(GPERF) $< >$@
+       echo "static const size_t `echo $* | tr a-z A-Z`_TOTAL_KEYWORDS = `sed -nr 's/.*TOTAL_KEYWORDS = ([0-9]+).*/\1/p' $@`;" >>$@
 
 .PHONY: dist
 dist: $(dist_archives)
index 5121f8ef8e0a936ed01106a3a286e98124b59359..57962ea8ec2de5f3e9bc8ed3f1d8f71c301f6230 100644 (file)
@@ -174,3 +174,4 @@ envtoconfitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
+static const size_t ENVTOCONFITEMS_TOTAL_KEYWORDS = 26;
index cf6a61877ace7cfefe845973ac15ce066a9e609c..692ffc9a1057316b416e87282b5e3f395c4c1c7a 100644 (file)
 #include "test/framework.h"
 #include "test/util.h"
 
-static char *received_conf_items[100];
+#define N_CONFIG_ITEMS 26
+static struct {
+       char *descr;
+       const char *origin;
+} received_conf_items[N_CONFIG_ITEMS];
 static size_t n_received_conf_items = 0;
 
 static void
-conf_item_receiver(const char *s, void *context)
+conf_item_receiver(const char *descr, const char *origin, void *context)
 {
        (void)context;
-       received_conf_items[n_received_conf_items] = x_strdup(s);
+       received_conf_items[n_received_conf_items].descr = x_strdup(descr);
+       received_conf_items[n_received_conf_items].origin = origin;
        ++n_received_conf_items;
 }
 
@@ -36,7 +41,7 @@ free_received_conf_items(void)
 {
        while (n_received_conf_items > 0) {
                --n_received_conf_items;
-               free(received_conf_items[n_received_conf_items]);
+               free(received_conf_items[n_received_conf_items].descr);
        }
 }
 
@@ -335,6 +340,7 @@ TEST(conf_set_existing_value)
 
 TEST(conf_print_items)
 {
+       size_t i;
        struct conf conf = {
                "bd",
                "cd",
@@ -361,40 +367,53 @@ TEST(conf_print_items)
                false,
                "td",
                022,
-               true
+               true,
+               NULL
        };
        size_t n = 0;
 
+       conf.item_origins = x_malloc(N_CONFIG_ITEMS * sizeof(char *));
+       for (i = 0; i < N_CONFIG_ITEMS; ++i) {
+               conf.item_origins[i] = format("origin%zu", i);
+       }
+
        conf_print_items(&conf, conf_item_receiver, NULL);
        CHECK_INT_EQ(26, n_received_conf_items);
-       CHECK_STR_EQ("base_dir = bd", received_conf_items[n++]);
-       CHECK_STR_EQ("cache_dir = cd", received_conf_items[n++]);
-       CHECK_STR_EQ("cache_dir_levels = 7", received_conf_items[n++]);
-       CHECK_STR_EQ("compiler = c", received_conf_items[n++]);
-       CHECK_STR_EQ("compiler_check = cc", received_conf_items[n++]);
-       CHECK_STR_EQ("compression = true", received_conf_items[n++]);
-       CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++]);
-       CHECK_STR_EQ("detect_shebang = true", received_conf_items[n++]);
-       CHECK_STR_EQ("direct_mode = false", received_conf_items[n++]);
-       CHECK_STR_EQ("disable = true", received_conf_items[n++]);
-       CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++]);
-       CHECK_STR_EQ("hard_link = true", received_conf_items[n++]);
-       CHECK_STR_EQ("hash_dir = true", received_conf_items[n++]);
-       CHECK_STR_EQ("log_file = lf", received_conf_items[n++]);
-       CHECK_STR_EQ("max_files = 4711", received_conf_items[n++]);
-       CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++]);
-       CHECK_STR_EQ("path = p", received_conf_items[n++]);
-       CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++]);
-       CHECK_STR_EQ("read_only = true", received_conf_items[n++]);
-       CHECK_STR_EQ("recache = true", received_conf_items[n++]);
-       CHECK_STR_EQ("run_second_cpp = true", received_conf_items[n++]);
+       CHECK_STR_EQ("base_dir = bd", received_conf_items[n++].descr);
+       CHECK_STR_EQ("cache_dir = cd", received_conf_items[n++].descr);
+       CHECK_STR_EQ("cache_dir_levels = 7", received_conf_items[n++].descr);
+       CHECK_STR_EQ("compiler = c", received_conf_items[n++].descr);
+       CHECK_STR_EQ("compiler_check = cc", received_conf_items[n++].descr);
+       CHECK_STR_EQ("compression = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++].descr);
+       CHECK_STR_EQ("detect_shebang = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("direct_mode = false", received_conf_items[n++].descr);
+       CHECK_STR_EQ("disable = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr);
+       CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("hash_dir = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr);
+       CHECK_STR_EQ("max_files = 4711", received_conf_items[n++].descr);
+       CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++].descr);
+       CHECK_STR_EQ("path = p", received_conf_items[n++].descr);
+       CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr);
+       CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("recache = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("run_second_cpp = true", received_conf_items[n++].descr);
        CHECK_STR_EQ("sloppiness = file_macro, include_file_mtime, time_macros",
-                    received_conf_items[n++]);
-       CHECK_STR_EQ("stats = false", received_conf_items[n++]);
-       CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++]);
-       CHECK_STR_EQ("umask = 022", received_conf_items[n++]);
-       CHECK_STR_EQ("unify = true", received_conf_items[n++]);
+                    received_conf_items[n++].descr);
+       CHECK_STR_EQ("stats = false", received_conf_items[n++].descr);
+       CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr);
+       CHECK_STR_EQ("umask = 022", received_conf_items[n++].descr);
+       CHECK_STR_EQ("unify = true", received_conf_items[n++].descr);
+
+       for (i = 0; i < N_CONFIG_ITEMS; ++i) {
+               char *expected = format("origin%zu", i);
+               CHECK_STR_EQ(expected, received_conf_items[i].origin);
+       }
+
        free_received_conf_items();
+       free(conf.item_origins);
 }
 
 TEST_SUITE_END