ref: e77a9ee2c4394d395f89ffb007a9a83a6b049dbb
parent: bc01e227b5831df0d4f88f4c11ebc2b6968d2535
author: cancel <cancel@cancel.fm>
date: Wed Jan 15 12:45:11 EST 2020
Factor out conf file writing
--- a/sysmisc.c
+++ b/sysmisc.c
@@ -385,3 +385,163 @@
return conf_read_match(&ezcr->file, names, nameslen, ezcr->buffer,
sizeof ezcr->buffer, &ezcr->index, &ezcr->value);
}
+
+enum {
+ Confwflag_add_newline = 1 << 0,
+};
+
+void ezconf_write_start(Ezconf_write *ezcw, Confopt_w *opts, size_t optscount) {
+ for (size_t i = 0; i < optscount; i++)
+ opts[i].written = 0;
+ *ezcw = (Ezconf_write){0};
+ ezcw->opts = opts;
+ ezcw->optscount = optscount;
+ Prefs_save_error error = Prefs_save_unknown_error;
+ switch (conf_save_start(&ezcw->save)) {
+ case Conf_save_start_ok:
+ error = Prefs_save_ok;
+ break;
+ case Conf_save_start_alloc_failed:
+ error = Prefs_save_oom;
+ break;
+ case Conf_save_start_no_home:
+ error = Prefs_save_no_home;
+ break;
+ case Conf_save_start_mkdir_failed:
+ error = Prefs_save_mkdir_failed;
+ break;
+ case Conf_save_start_conf_dir_not_dir:
+ error = Prefs_save_conf_dir_not_dir;
+ break;
+ case Conf_save_start_old_temp_file_stuck:
+ error = Prefs_save_old_temp_file_stuck;
+ break;
+ case Conf_save_start_temp_file_perm_denied:
+ error = Prefs_save_temp_file_perm_denied;
+ break;
+ case Conf_save_start_temp_file_open_failed:
+ error = Prefs_save_temp_open_failed;
+ break;
+ }
+ ezcw->error = error;
+}
+
+bool ezconf_write_step(Ezconf_write *ezcw) {
+ U32 stateflags = ezcw->stateflags;
+ FILE *origfile = ezcw->save.origfile, *tempfile = ezcw->save.tempfile;
+ Confopt_w *opts = ezcw->opts, *chosen = NULL;
+ size_t optscount = ezcw->optscount;
+ if (ezcw->error || !tempfile) // Already errored or finished ok
+ return false;
+ // If we set a flag to write a closing newline the last time we were called,
+ // write it now.
+ if (stateflags & Confwflag_add_newline) {
+ fputs("\n", tempfile);
+ stateflags &= ~(U32)Confwflag_add_newline;
+ }
+ if (!optscount)
+ goto commit;
+ if (!origfile)
+ goto write_leftovers;
+ for (;;) { // Scan through file looking for known keys in key=value lines
+ char linebuff[1024];
+ char *left, *right;
+ Usz leftsz, rightsz;
+ Conf_read_result res = conf_read_line(origfile, linebuff, sizeof linebuff,
+ &left, &leftsz, &right, &rightsz);
+ switch (res) {
+ case Conf_read_left_and_right: {
+ for (size_t i = 0; i < optscount; i++) {
+ char const *name = opts[i].name;
+ if (!name)
+ continue;
+ if (strcmp(name, left) != 0)
+ continue;
+ // If we already wrote this one, comment out the line instead, and move
+ // on to the next line.
+ if (opts[i].written) {
+ fputs("# ", tempfile);
+ goto write_landr;
+ }
+ chosen = opts + i;
+ goto return_for_writing;
+ }
+ write_landr:
+ fputs(left, tempfile);
+ fputs(" = ", tempfile);
+ fputs(right, tempfile);
+ fputs("\n", tempfile);
+ continue;
+ }
+ case Conf_read_irrelevant:
+ fputs(left, tempfile);
+ fputs("\n", tempfile);
+ continue;
+ case Conf_read_eof:
+ goto end_original;
+ case Conf_read_buffer_too_small:
+ ezcw->error = Prefs_save_line_too_long;
+ goto cancel;
+ case Conf_read_io_error:
+ ezcw->error = Prefs_save_existing_read_error;
+ goto cancel;
+ }
+ }
+end_original: // Don't need original file anymore
+ fclose(origfile);
+ ezcw->save.origfile = origfile = NULL;
+write_leftovers: // Write out any guys that weren't in original file.
+ for (;;) { // Find the first guy that wasn't already written.
+ if (!optscount)
+ goto commit;
+ chosen = opts;
+ // Drop the guy from the front of the list. This is to reduce super-linear
+ // complexity growth as the number of conf key-value pairs are increased.
+ // (Otherwise, we iterate the full set of guys on each call during the
+ // "write the leftovers" phase.)
+ opts++;
+ optscount--;
+ if (!chosen->written)
+ break;
+ }
+ // Once control has reached here, we're going to return true to the caller.
+ // Which means we expect to be called at least one more time. So update the
+ // pointers stored in the persistent state, so that we don't have to scan
+ // through as much of this list next time. (This might even end up finishing
+ // it off, making it empty.)
+ ezcw->opts = opts;
+ ezcw->optscount = optscount;
+return_for_writing:
+ chosen->written = true;
+ fputs(chosen->name, tempfile);
+ fputs(" = ", tempfile);
+ ezcw->optid = chosen->id;
+ stateflags |= (U32)Confwflag_add_newline;
+ ezcw->stateflags = stateflags;
+ return true;
+cancel:
+ conf_save_cancel(&ezcw->save);
+ // ^- Sets tempfile to null, which we use as a guard at the top of this
+ // function.
+ ezcw->stateflags = 0;
+ return false;
+commit:;
+ Prefs_save_error error = Prefs_save_unknown_error;
+ switch (conf_save_commit(&ezcw->save)) {
+ case Conf_save_commit_ok:
+ error = Prefs_save_ok;
+ break;
+ case Conf_save_commit_temp_fsync_failed:
+ error = Prefs_save_temp_fsync_failed;
+ break;
+ case Conf_save_commit_temp_close_failed:
+ error = Prefs_save_temp_close_failed;
+ break;
+ case Conf_save_commit_rename_failed:
+ error = Prefs_save_rename_failed;
+ break;
+ }
+ ezcw->error = error;
+ ezcw->stateflags = 0;
+ return false;
+}
--- a/sysmisc.h
+++ b/sysmisc.h
@@ -107,3 +107,21 @@
void ezconf_read_start(Ezconf_read *ezcr);
bool ezconf_read_step(Ezconf_read *ezcr, char const *const *names,
Usz nameslen);
+
+typedef struct {
+ char const *name;
+ intptr_t id;
+ U8 written : 1;
+} Confopt_w;
+
+typedef struct {
+ Conf_save save;
+ Confopt_w *opts;
+ size_t optscount;
+ intptr_t optid;
+ Prefs_save_error error;
+ U32 stateflags;
+} Ezconf_write;
+
+void ezconf_write_start(Ezconf_write *ezcw, Confopt_w *opts, size_t optscount);
+bool ezconf_write_step(Ezconf_write *ezcw);
--- a/tui_main.c
+++ b/tui_main.c
@@ -2395,45 +2395,11 @@
return Prefs_load_ok;
}
-static void put_conf_pair(FILE *file, char const *left, char const *right) {
- fputs(left, file);
- fputs(" = ", file);
- fputs(right, file);
- fputs("\n", file);
-}
-
-Prefs_save_error save_prefs_to_disk(Midi_mode const *midi_mode,
- int softmargin_y, int softmargin_x,
- bool softmargins_touched_by_user) {
- Conf_save save;
- Conf_save_start_error starterr = conf_save_start(&save);
- switch (starterr) {
- case Conf_save_start_ok:
- break;
- case Conf_save_start_alloc_failed:
- return Prefs_save_oom;
- case Conf_save_start_no_home:
- return Prefs_save_no_home;
- case Conf_save_start_mkdir_failed:
- return Prefs_save_mkdir_failed;
- case Conf_save_start_conf_dir_not_dir:
- return Prefs_save_conf_dir_not_dir;
- case Conf_save_start_old_temp_file_stuck:
- return Prefs_save_old_temp_file_stuck;
- case Conf_save_start_temp_file_perm_denied:
- return Prefs_save_temp_file_perm_denied;
- case Conf_save_start_temp_file_open_failed:
- return Prefs_save_temp_open_failed;
- }
- bool need_cancel_save = true;
- enum Midi_output_pref {
- Midi_output_pref_none = 0,
-#ifdef FEAT_PORTMIDI
- Midi_output_pref_portmidi,
-#endif
- } midi_output_pref = Midi_output_pref_none;
- bool needs_write_margins = softmargins_touched_by_user;
- Prefs_save_error error;
+void save_prefs_with_error_message(Midi_mode const *midi_mode, int softmargin_y,
+ int softmargin_x,
+ bool softmargins_touched_by_user) {
+ Confopt_w wopts[Confoptslen];
+ Usz i = 0;
oso *midi_output_device_name = NULL;
switch (midi_mode->any.type) {
case Midi_mode_type_null:
@@ -2450,104 +2416,34 @@
osowipe(&midi_output_device_name);
break;
}
- midi_output_pref = Midi_output_pref_portmidi;
+ wopts[i].name = confopts[Confopt_portmidi_output_device];
+ wopts[i].id = Confopt_portmidi_output_device;
+ i++;
} break;
#endif
}
- if (!save.origfile)
- goto done_reading_existing;
- for (;;) {
- char linebuff[1024];
- char *left, *right;
- Usz leftsz, rightsz;
- Conf_read_result res =
- conf_read_line(save.origfile, linebuff, sizeof linebuff, &left, &leftsz,
- &right, &rightsz);
- switch (res) {
- case Conf_read_left_and_right:
+ if (softmargins_touched_by_user) {
+ wopts[i].name = confopts[Confopt_margins];
+ wopts[i].id = Confopt_margins;
+ i++;
+ }
+ Ezconf_write ez;
+ ezconf_write_start(&ez, wopts, i);
+ while (ezconf_write_step(&ez)) {
+ switch (ez.optid) {
#ifdef FEAT_PORTMIDI
- if (strcmp(confopts[Confopt_portmidi_output_device], left) == 0) {
- if (midi_output_pref != Midi_output_pref_portmidi)
- continue;
- midi_output_pref = Midi_output_pref_none;
- put_conf_pair(save.tempfile, confopts[Confopt_portmidi_output_device],
- osoc(midi_output_device_name));
- osowipe(&midi_output_device_name);
- continue;
- }
+ case Confopt_portmidi_output_device:
+ fputs(osoc(midi_output_device_name), ez.save.tempfile);
+ break;
#endif
- if (strcmp(confopts[Confopt_margins], left) == 0) {
- if (!needs_write_margins)
- continue;
- needs_write_margins = false;
- fprintf(save.tempfile, "%s = %dx%d\n", confopts[Confopt_margins],
- softmargin_x, softmargin_y);
- continue;
- }
- put_conf_pair(save.tempfile, left, right);
- continue;
- case Conf_read_irrelevant:
- fputs(left, save.tempfile);
- fputs("\n", save.tempfile);
- continue;
- case Conf_read_eof:
- goto done_reading_existing;
- case Conf_read_buffer_too_small:
- error = Prefs_save_line_too_long;
- goto cleanup;
- case Conf_read_io_error:
- error = Prefs_save_existing_read_error;
- goto cleanup;
+ case Confopt_margins:
+ fprintf(ez.save.tempfile, "%dx%d", softmargin_x, softmargin_y);
+ break;
}
}
-done_reading_existing:
- switch (midi_output_pref) {
- case Midi_output_pref_none:
- break;
-#ifdef FEAT_PORTMIDI
- case Midi_output_pref_portmidi:
- put_conf_pair(save.tempfile, confopts[Confopt_portmidi_output_device],
- osoc(midi_output_device_name));
- osowipe(&midi_output_device_name);
- break;
-#endif
- }
- if (needs_write_margins) {
- needs_write_margins = false;
- fprintf(save.tempfile, "%s = %dx%d\n", confopts[Confopt_margins],
- softmargin_x, softmargin_y); // TODO redundant
- }
- need_cancel_save = false;
- Conf_save_commit_error comerr = conf_save_commit(&save);
- error = Prefs_save_unknown_error;
- switch (comerr) {
- case Conf_save_commit_ok:
- error = Prefs_save_ok;
- break;
- case Conf_save_commit_temp_fsync_failed:
- error = Prefs_save_temp_fsync_failed;
- break;
- case Conf_save_commit_temp_close_failed:
- error = Prefs_save_temp_close_failed;
- break;
- case Conf_save_commit_rename_failed:
- error = Prefs_save_rename_failed;
- break;
- }
-cleanup:
- if (need_cancel_save)
- conf_save_cancel(&save);
osofree(midi_output_device_name);
- return error;
-}
-
-void save_prefs_with_error_message(Midi_mode const *midi_mode, int softmargin_y,
- int softmargin_x,
- bool softmargins_touched_by_user) {
- Prefs_save_error err = save_prefs_to_disk(
- midi_mode, softmargin_y, softmargin_x, softmargins_touched_by_user);
- if (err) {
- char const *msg = prefs_save_error_string(err);
+ if (ez.error) {
+ char const *msg = prefs_save_error_string(ez.error);
qmsg_printf_push("Config Error",
"Error when writing configuration file:\n%s", msg);
}