Browse Source

Factor out conf file writing

master
cancel 5 years ago
parent
commit
e77a9ee2c4
  1. 160
      sysmisc.c
  2. 18
      sysmisc.h
  3. 148
      tui_main.c

160
sysmisc.c

@ -385,3 +385,163 @@ bool ezconf_read_step(Ezconf_read *ezcr, char const *const *names,
return conf_read_match(&ezcr->file, names, nameslen, ezcr->buffer, return conf_read_match(&ezcr->file, names, nameslen, ezcr->buffer,
sizeof ezcr->buffer, &ezcr->index, &ezcr->value); 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;
}

18
sysmisc.h

@ -107,3 +107,21 @@ typedef struct {
void ezconf_read_start(Ezconf_read *ezcr); void ezconf_read_start(Ezconf_read *ezcr);
bool ezconf_read_step(Ezconf_read *ezcr, char const *const *names, bool ezconf_read_step(Ezconf_read *ezcr, char const *const *names,
Usz nameslen); 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);

148
tui_main.c

@ -2395,45 +2395,11 @@ Prefs_load_error prefs_load_from_conf_file(Prefs *p) {
return Prefs_load_ok; return Prefs_load_ok;
} }
static void put_conf_pair(FILE *file, char const *left, char const *right) { void save_prefs_with_error_message(Midi_mode const *midi_mode, int softmargin_y,
fputs(left, file); int softmargin_x,
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) { bool softmargins_touched_by_user) {
Conf_save save; Confopt_w wopts[Confoptslen];
Conf_save_start_error starterr = conf_save_start(&save); Usz i = 0;
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;
oso *midi_output_device_name = NULL; oso *midi_output_device_name = NULL;
switch (midi_mode->any.type) { switch (midi_mode->any.type) {
case Midi_mode_type_null: case Midi_mode_type_null:
@ -2450,104 +2416,34 @@ Prefs_save_error save_prefs_to_disk(Midi_mode const *midi_mode,
osowipe(&midi_output_device_name); osowipe(&midi_output_device_name);
break; 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; } break;
#endif #endif
} }
if (!save.origfile) if (softmargins_touched_by_user) {
goto done_reading_existing; wopts[i].name = confopts[Confopt_margins];
for (;;) { wopts[i].id = Confopt_margins;
char linebuff[1024]; i++;
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:
#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;
}
#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); Ezconf_write ez;
continue; ezconf_write_start(&ez, wopts, i);
case Conf_read_irrelevant: while (ezconf_write_step(&ez)) {
fputs(left, save.tempfile); switch (ez.optid) {
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;
}
}
done_reading_existing:
switch (midi_output_pref) {
case Midi_output_pref_none:
break;
#ifdef FEAT_PORTMIDI #ifdef FEAT_PORTMIDI
case Midi_output_pref_portmidi: case Confopt_portmidi_output_device:
put_conf_pair(save.tempfile, confopts[Confopt_portmidi_output_device], fputs(osoc(midi_output_device_name), ez.save.tempfile);
osoc(midi_output_device_name));
osowipe(&midi_output_device_name);
break; break;
#endif #endif
} case Confopt_margins:
if (needs_write_margins) { fprintf(ez.save.tempfile, "%dx%d", softmargin_x, softmargin_y);
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; break;
} }
cleanup:
if (need_cancel_save)
conf_save_cancel(&save);
osofree(midi_output_device_name);
return error;
} }
osofree(midi_output_device_name);
void save_prefs_with_error_message(Midi_mode const *midi_mode, int softmargin_y, if (ez.error) {
int softmargin_x, char const *msg = prefs_save_error_string(ez.error);
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);
qmsg_printf_push("Config Error", qmsg_printf_push("Config Error",
"Error when writing configuration file:\n%s", msg); "Error when writing configuration file:\n%s", msg);
} }

Loading…
Cancel
Save