shithub: orca

Download patch

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);
   }