+
+ fputs(buf, out);
+ fputc('\n', out);
+ }
+
+ if(ferror(in)) {
+ logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", filename, strerror(errno));
+ fclose(in);
+ fclose(out);
+ return false;
+ }
+
+ fclose(in);
+
+ fprintf(out, "%s = %s\n", key, value);
+
+ if(ferror(out)) {
+ logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", tmpname, strerror(errno));
+ fclose(out);
+ return false;
+ }
+
+ fclose(out);
+
+#ifdef HAVE_MINGW
+ // We cannot atomically replace files on Windows.
+ char bakname[PATH_MAX];
+ snprintf(bakname, PATH_MAX, "%s.bak", filename);
+ if(rename(tmpname, bakfile) || rename(bakfile, filename)) {
+ rename(bakfile, filename);
+#else
+ if(rename(tmpname, filename)) {
+#endif
+ logger(mesh, MESHLINK_ERROR, "Failed to update `%s': %s", filename, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool append_config_file(meshlink_handle_t *mesh, const char *name, const char *key, const char *value) {
+ char filename[PATH_MAX];
+ snprintf(filename,PATH_MAX, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+
+ FILE *fp = fopen(filename, "a");
+
+ if(!fp) {
+ logger(mesh, MESHLINK_ERROR, "Cannot open config file %s: %s", filename, strerror(errno));
+ return false;
+ }
+
+ // Check if we don't add a duplicate entry
+
+ char entry[MAX_STRING_SIZE];
+ snprintf(entry, sizeof entry, "%s = %s", key, value);
+
+ char buffer[MAX_STRING_SIZE];
+ bool found = false;
+
+ while(readline(fp, buffer, sizeof buffer)) {
+ if(!strcmp(buffer, entry)) {
+ found = true;