Skip to content
Permalink
Browse files

Add initial prefs/conf file saving

Uses XDG_CONFIG_HOME when available. This probably has multiple bugs,
and it also needs cleaning up.
  • Loading branch information
cancel committed Jan 12, 2020
1 parent 2d14276 commit c60f7a557cf24544a5d4048eb10bb8f5948ded9f
Showing with 278 additions and 5 deletions.
  1. +97 −0 sysmisc.c
  2. +25 −1 sysmisc.h
  3. +156 −4 tui_main.c
@@ -2,6 +2,8 @@
#include "gbuffer.h"
#include "oso.h"
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>

ORCA_FORCE_NO_INLINE
Cboard_error cboard_copy(Glyph const* gbuffer, Usz field_height,
@@ -210,3 +212,98 @@ FILE* conf_file_open_for_reading(void) {
osofree(path);
return file;
}

Conf_save_start_error conf_save_start(Conf_save* p) {
memset(p, 0, sizeof(Conf_save));
oso *dir = NULL, *canonpath = NULL, *temppath = NULL;
FILE *origfile = NULL, *tempfile = NULL;
Conf_save_start_error err;
if (try_get_conf_dir(&dir)) {
err = Conf_save_start_no_home;
goto cleanup;
}
if (!dir) {
err = Conf_save_start_alloc_failed;
goto cleanup;
}
osoputoso(&canonpath, dir);
osocat(&canonpath, conf_file_name);
if (!canonpath) {
err = Conf_save_start_alloc_failed;
goto cleanup;
}
osoputoso(&temppath, canonpath);
osocat(&temppath, ".tmp");
if (!temppath) {
err = Conf_save_start_alloc_failed;
goto cleanup;
}
// Remove old temp file if it exists. If it exists and we can't remove it,
// error.
if (unlink(osoc(temppath)) == -1 && errno != ENOENT) {
err = Conf_save_start_old_temp_file_stuck;
goto cleanup;
}
tempfile = fopen(osoc(temppath), "w");
if (!tempfile) {
// Try to create config dir, in case it doesn't exist. (XDG says we should
// do this, and use mode 0700.)
mkdir(osoc(dir), 0700);
tempfile = fopen(osoc(temppath), "w");
}
if (!tempfile) {
err = Conf_save_start_temp_file_open_failed;
goto cleanup;
}
// This may be left as NULL.
origfile = fopen(osoc(canonpath), "r");
// We did it, boys.
osofree(dir);
p->canonpath = canonpath;
p->temppath = temppath;
p->origfile = origfile;
p->tempfile = tempfile;
return Conf_save_start_ok;

cleanup:
osofree(dir);
osofree(canonpath);
osofree(temppath);
if (origfile)
fclose(origfile);
if (tempfile)
fclose(tempfile);
return err;
}

void conf_save_cancel(Conf_save* p) {
osofree(p->canonpath);
osofree(p->temppath);
if (p->origfile)
fclose(p->origfile);
if (p->tempfile)
fclose(p->tempfile);
memset(p, 0, sizeof(Conf_save));
}

Conf_save_commit_error conf_save_commit(Conf_save* p) {
Conf_save_commit_error err;
fclose(p->tempfile);
p->tempfile = NULL;
if (p->origfile) {
fclose(p->origfile);
p->origfile = NULL;
}
// This isn't really atomic. But if we want to close and move a file
// simultaneously, I think we have to use OS-specific facilities. So I guess
// this is the best we can do for now. I could be wrong, though. But I
// couldn't find any good information about it.
if (rename(osoc(p->temppath), osoc(p->canonpath)) == -1) {
err = Conf_save_commit_rename_failed;
goto cleanup;
}
err = Conf_save_commit_ok;
cleanup:
conf_save_cancel(p);
return err;
}
@@ -28,5 +28,29 @@ Conf_read_result conf_read_line(FILE* file, char* buf, Usz bufsize,
char** out_left, Usz* out_leftlen,
char** out_right, Usz* out_rightlen);


FILE* conf_file_open_for_reading(void);

typedef struct {
FILE *origfile, *tempfile;
struct oso *canonpath, *temppath;
} Conf_save;

typedef enum {
Conf_save_start_ok = 0,
Conf_save_start_alloc_failed,
Conf_save_start_no_home,
Conf_save_start_mkdir_failed,
Conf_save_start_old_temp_file_stuck,
Conf_save_start_temp_file_open_failed,
} Conf_save_start_error;

typedef enum {
Conf_save_commit_ok = 0,
Conf_save_commit_temp_fsync_failed,
Conf_save_commit_temp_close_failed,
Conf_save_commit_rename_failed,
} Conf_save_commit_error;

Conf_save_start_error conf_save_start(Conf_save* p);
void conf_save_cancel(Conf_save* p);
Conf_save_commit_error conf_save_commit(Conf_save* p);
@@ -819,6 +819,20 @@ bool portmidi_find_device_id_by_name(char const* name, Usz namelen,
}
return false;
}
bool portmidi_find_name_of_device_id(PmDeviceID id, PmError* out_pmerror,
oso** out_name) {
*out_pmerror = portmidi_init_if_necessary();
if (*out_pmerror)
return false;
int num = Pm_CountDevices();
if (id < 0 || id >= num)
return false;
PmDeviceInfo const* info = Pm_GetDeviceInfo(id);
if (!info || !info->output)
return false;
osoput(out_name, info->name);
return true;
}
#endif
void midi_mode_deinit(Midi_mode* mm) {
switch (mm->any.type) {
@@ -2347,22 +2361,24 @@ typedef enum {
Prefs_load_ok = 0,
} Prefs_load_error;

static char const* confkey_portmidi_output_device = "portmidi_output_device";

ORCA_FORCE_NO_INLINE
Prefs_load_error prefs_load_from_conf_file(Prefs* p) {
(void)p;
FILE* conffile = conf_file_open_for_reading();
if (!conffile) {
return Prefs_load_ok;
}
char linebuff[512];
char linebuff[1024];
char *left, *right;
Usz leftsz, rightsz;
for (;;) {
char *left, *right;
Usz leftsz, rightsz;
Conf_read_result res = conf_read_line(conffile, linebuff, sizeof linebuff,
&left, &leftsz, &right, &rightsz);
switch (res) {
case Conf_read_left_and_right: {
if (strcmp("portmidi_output_device", left) == 0) {
if (strcmp(confkey_portmidi_output_device, left) == 0) {
osoput(&p->portmidi_output_device, right);
}
continue;
@@ -2380,6 +2396,140 @@ Prefs_load_error prefs_load_from_conf_file(Prefs* p) {
return Prefs_load_ok;
}

typedef enum {
Prefs_save_ok = 0,
Prefs_save_start_failed,
Prefs_save_commit_failed,
Prefs_save_line_too_long,
Prefs_save_existing_read_error,
} Prefs_save_error;

Prefs_save_error save_prefs_to_disk(Midi_mode const* midi_mode) {
Conf_save save;
Conf_save_start_error starterr = conf_save_start(&save);
if (starterr)
return Prefs_save_start_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;
Prefs_save_error error;
oso* midi_output_device_name = NULL;
switch (midi_mode->any.type) {
case Midi_mode_type_null:
break;
case Midi_mode_type_osc_bidule:
// TODO
break;
#ifdef FEAT_PORTMIDI
case Midi_mode_type_portmidi: {
PmError pmerror;
if (!portmidi_find_name_of_device_id(midi_mode->portmidi.device_id,
&pmerror, &midi_output_device_name) ||
osolen(midi_output_device_name) < 1) {
osowipe(&midi_output_device_name);
break;
}
midi_output_pref = Midi_output_pref_portmidi;
} 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:
#ifdef FEAT_PORTMIDI
if (strcmp(confkey_portmidi_output_device, left) == 0) {
if (midi_output_pref != Midi_output_pref_portmidi)
continue;
midi_output_pref = Midi_output_pref_none;
fputs(confkey_portmidi_output_device, save.tempfile);
fputs(" = ", save.tempfile);
fputs(osoc(midi_output_device_name), save.tempfile);
fputs("\n", save.tempfile);
osowipe(&midi_output_device_name);
continue;
}
#endif
fputs(left, save.tempfile);
fputs(" = ", save.tempfile);
fputs(right, save.tempfile);
fputs("\n", save.tempfile);
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;
}
}
done_reading_existing:
switch (midi_output_pref) {
case Midi_output_pref_none:
break;
#ifdef FEAT_PORTMIDI
case Midi_output_pref_portmidi:
fputs(confkey_portmidi_output_device, save.tempfile);
fputs(" = ", save.tempfile);
fputs(osoc(midi_output_device_name), save.tempfile);
fputs("\n", save.tempfile);
osowipe(&midi_output_device_name);
break;
#endif
}
need_cancel_save = false;
Conf_save_commit_error comerr = conf_save_commit(&save);
if (comerr) {
error = Prefs_save_commit_failed;
goto cleanup;
}
error = Prefs_save_ok;
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) {
Prefs_save_error err = save_prefs_to_disk(midi_mode);
char const* msg = "Unknown";
switch (err) {
case Prefs_save_ok:
return;
case Prefs_save_start_failed:
msg = "Start failed";
break;
case Prefs_save_commit_failed:
msg = "Failed to commit save file";
break;
case Prefs_save_line_too_long:
msg = "Line in file is too long";
break;
case Prefs_save_existing_read_error:
msg = "Error when reading existing configuration file";
break;
}
qmsg_printf_push("Save Error", "Error when saving:\n%s", msg);
}

void print_loading_message(char const* s) {
Usz len = strlen(s);
if (len > INT_MAX)
@@ -3087,6 +3237,8 @@ int main(int argc, char** argv) {
qmsg_printf_push("PortMidi Error",
"Error setting PortMidi output device:\n%s",
Pm_GetErrorText(pme));
} else {
save_prefs_with_error_message(&midi_mode);
}
} break;
#endif

0 comments on commit c60f7a5

Please sign in to comment.
You can’t perform that action at this time.