bool conf_set_value_in_file(const char *path, const char *key, const char *value, char **errmsg) { FILE *infile, *outfile; char *outpath; char buf[10000]; bool found; const struct conf_item *item; item = find_conf(key); if (!item) { *errmsg = format("unknown configuration option \"%s\"", key); return false; } infile = fopen(path, "r"); if (!infile) { *errmsg = format("%s: %s", path, strerror(errno)); return false; } outpath = format("%s.tmp.%s", path, tmp_string()); outfile = fopen(outpath, "w"); if (!outfile) { *errmsg = format("%s: %s", outpath, strerror(errno)); free(outpath); fclose(infile); return false; } found = false; while (fgets(buf, sizeof(buf), infile)) { char *errmsg2, *key2, *value2; bool ok; ok = parse_line(buf, &key2, &value2, &errmsg2); if (ok && key2 && str_eq(key2, key)) { found = true; fprintf(outfile, "%s = %s\n", key, value); } else { fputs(buf, outfile); } free(key2); free(value2); } if (!found) { fprintf(outfile, "%s = %s\n", key, value); } fclose(infile); fclose(outfile); if (x_rename(outpath, path) != 0) { *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno)); return false; } free(outpath); return true; }
/* write out a stats file */ void stats_write(const char *path, struct counters *counters) { size_t i; char *tmp_file; FILE *f; tmp_file = format("%s.tmp.%s", path, tmp_string()); f = fopen(tmp_file, "wb"); if (!f && errno == ENOENT) { if (create_parent_dirs(path) == 0) { f = fopen(tmp_file, "wb"); } } if (!f) { cc_log("Failed to open %s", tmp_file); goto end; } for (i = 0; i < counters->size; i++) { if (fprintf(f, "%u\n", counters->data[i]) < 0) { fatal("Failed to write to %s", tmp_file); } } fclose(f); x_rename(tmp_file, path); end: free(tmp_file); }
// Write out a stats file. void stats_write(const char *path, struct counters *counters) { char *tmp_file = format("%s.tmp", path); FILE *f = create_tmp_file(&tmp_file, "wb"); for (size_t i = 0; i < counters->size; i++) { if (fprintf(f, "%u\n", counters->data[i]) < 0) { fatal("Failed to write to %s", tmp_file); } } fclose(f); x_rename(tmp_file, path); free(tmp_file); }
// Put the object name into a manifest file given a set of included files. // Returns true on success, otherwise false. bool manifest_put(const char *manifest_path, struct file_hash *object_hash, struct hashtable *included_files) { int ret = 0; gzFile f2 = NULL; struct manifest *mf = NULL; char *tmp_file = NULL; // We don't bother to acquire a lock when writing the manifest to disk. A // race between two processes will only result in one lost entry, which is // not a big deal, and it's also very unlikely. int fd1 = open(manifest_path, O_RDONLY | O_BINARY); if (fd1 == -1) { // New file. mf = create_empty_manifest(); } else { gzFile f1 = gzdopen(fd1, "rb"); if (!f1) { cc_log("Failed to gzdopen manifest file"); close(fd1); goto out; } mf = read_manifest(f1); gzclose(f1); if (!mf) { cc_log("Failed to read manifest file; deleting it"); x_unlink(manifest_path); mf = create_empty_manifest(); } } if (mf->n_objects > MAX_MANIFEST_ENTRIES) { // Normally, there shouldn't be many object entries in the manifest since // new entries are added only if an include file has changed but not the // source file, and you typically change source files more often than // header files. However, it's certainly possible to imagine cases where // the manifest will grow large (for instance, a generated header file that // changes for every build), and this must be taken care of since // processing an ever growing manifest eventually will take too much time. // A good way of solving this would be to maintain the object entries in // LRU order and discarding the old ones. An easy way is to throw away all // entries when there are too many. Let's do that for now. cc_log("More than %u entries in manifest file; discarding", MAX_MANIFEST_ENTRIES); free_manifest(mf); mf = create_empty_manifest(); } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) { // Rarely, file_info entries can grow large in pathological cases where // many included files change, but the main file does not. This also puts // an upper bound on the number of file_info entries. cc_log("More than %u file_info entries in manifest file; discarding", MAX_MANIFEST_FILE_INFO_ENTRIES); free_manifest(mf); mf = create_empty_manifest(); } tmp_file = format("%s.tmp", manifest_path); int fd2 = create_tmp_fd(&tmp_file); f2 = gzdopen(fd2, "wb"); if (!f2) { cc_log("Failed to gzdopen %s", tmp_file); goto out; } add_object_entry(mf, object_hash, included_files); if (write_manifest(f2, mf)) { gzclose(f2); f2 = NULL; if (x_rename(tmp_file, manifest_path) == 0) { ret = 1; } else { cc_log("Failed to rename %s to %s", tmp_file, manifest_path); goto out; } } else { cc_log("Failed to write manifest file"); goto out; } out: if (mf) { free_manifest(mf); } if (tmp_file) { free(tmp_file); } if (f2) { gzclose(f2); } return ret; }
/* * Put the object name into a manifest file given a set of included files. * Returns true on success, otherwise false. */ bool manifest_put(const char *manifest_path, struct file_hash *object_hash, struct hashtable *included_files) { int ret = 0; int fd1; int fd2; gzFile f2 = NULL; struct manifest *mf = NULL; char *tmp_file = NULL; /* * We don't bother to acquire a lock when writing the manifest to disk. A * race between two processes will only result in one lost entry, which is * not a big deal, and it's also very unlikely. */ fd1 = open(manifest_path, O_RDONLY | O_BINARY); if (fd1 == -1) { /* New file. */ mf = create_empty_manifest(); } else { gzFile f1 = gzdopen(fd1, "rb"); if (!f1) { cc_log("Failed to gzdopen manifest file"); close(fd1); goto out; } mf = read_manifest(f1); gzclose(f1); if (!mf) { cc_log("Failed to read manifest file; deleting it"); x_unlink(manifest_path); mf = create_empty_manifest(); } } if (mf->n_objects > MAX_MANIFEST_ENTRIES) { /* * Normally, there shouldn't be many object entries in the manifest since * new entries are added only if an include file has changed but not the * source file, and you typically change source files more often than * header files. However, it's certainly possible to imagine cases where * the manifest will grow large (for instance, a generated header file that * changes for every build), and this must be taken care of since * processing an ever growing manifest eventually will take too much time. * A good way of solving this would be to maintain the object entries in * LRU order and discarding the old ones. An easy way is to throw away all * entries when there are too many. Let's do that for now. */ cc_log("More than %u entries in manifest file; discarding", MAX_MANIFEST_ENTRIES); free_manifest(mf); mf = create_empty_manifest(); } tmp_file = format("%s.tmp.%s", manifest_path, tmp_string()); fd2 = safe_create_wronly(tmp_file); if (fd2 == -1) { cc_log("Failed to open %s", tmp_file); goto out; } f2 = gzdopen(fd2, "wb"); if (!f2) { cc_log("Failed to gzdopen %s", tmp_file); goto out; } add_object_entry(mf, object_hash, included_files); if (write_manifest(f2, mf)) { gzclose(f2); f2 = NULL; if (x_rename(tmp_file, manifest_path) == 0) { ret = 1; } else { cc_log("Failed to rename %s to %s", tmp_file, manifest_path); goto out; } } else { cc_log("Failed to write manifest file"); goto out; } out: if (mf) { free_manifest(mf); } if (tmp_file) { free(tmp_file); } if (f2) { gzclose(f2); } return ret; }