Commit c2207cf0 authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

Merge branch 'aggraef/purr-data-testing'

parents 586f5fcc a5a80f1f
......@@ -248,67 +248,122 @@ static void sys_donesavepreferences( void)
// prefs file that is currently the one to save to
static char current_prefs[FILENAME_MAX] = "org.puredata.pd-l2ork";
static void sys_initloadpreferences( void)
static char *sys_prefbuf;
static int sys_getpreference(const char *key, char *value, int size)
// Maximum number of bytes to be read on each iteration. Note that the buffer
// is resized in increments of BUFSZ until the entire prefs data has been
// read. For best performance, BUFSZ should be a sizeable fraction of the
// expected preference data size. The size 4096 matches PD's internal GUI
// socket size and thus should normally be enough to read the entire prefs
// data in one go.
#define BUFSZ 4096
// AG: We have to go to some lengths here since 'defaults read' doesn't
// properly deal with UTF-8 characters in the prefs data. 'defaults export'
// does the trick, however, so we use that to read the entire prefs data at
// once from a pipe, using plutil to convert the resulting data to JSON format
// which can then be translated to Pd's Unix preferences file format using
// sed. The result is stored in a character buffer for efficient access. From
// there we can retrieve the individual keys in the same fashion as on Unix. A
// welcome side effect is that loading the prefs is *much* faster now than
// with the previous method which invoked 'defaults read' on each individual
// key.
// XXXTODO: In principle, this approach should also work in reverse to import
// the data into the defaults storage in one go. Presumably this should also
// be much faster than the current implementation which invokes the shell to
// run 'defaults write' for each individual key.
static void sys_initloadpreferences(void)
char cmdbuf[256];
int nread = 0, nleft = size;
char default_prefs[FILENAME_MAX]; // default prefs embedded in the package
char embedded_prefs[FILENAME_MAX]; // overrides others for standalone app
char embedded_prefs_file[FILENAME_MAX];
char user_prefs_file[FILENAME_MAX];
char *homedir = getenv("HOME");
struct stat statbuf;
/* the 'defaults' command expects the filename without .plist at the end */
snprintf(default_prefs, FILENAME_MAX, "%s/../org.puredata.pd-l2ork.default",
snprintf(embedded_prefs, FILENAME_MAX, "%s/../org.puredata.pd-l2ork",
snprintf(embedded_prefs_file, FILENAME_MAX, "%s.plist", embedded_prefs);
snprintf(user_prefs_file, FILENAME_MAX,
"%s/Library/Preferences/org.puredata.pd-l2ork.plist", homedir);
if (stat(embedded_prefs_file, &statbuf) == 0)
snprintf(cmdbuf, FILENAME_MAX + 20,
"defaults read '%s' %s 2> /dev/null\n", embedded_prefs, key);
strncpy(current_prefs, embedded_prefs, FILENAME_MAX);
else if (stat(user_prefs_file, &statbuf) == 0)
snprintf(cmdbuf, FILENAME_MAX + 20,
"defaults read org.puredata.pd-l2ork %s 2> /dev/null\n", key);
strcpy(current_prefs, "org.puredata.pd-l2ork");
char cmdbuf[MAXPDSTRING], *buf;
FILE *fp;
size_t sz, n = 0;
int res;
// This looks complicated, but is rather straightforward. The individual
// stages of the pipe are:
// 1. defaults export: grab our defaults in XML format
// 2. plutil -convert json -r -o - -: convert to JSON
// 3. sed: a few edits remove the extra JSON bits (curly brances, string
// quotes, unwanted whitespace and character escapes)
snprintf(cmdbuf, MAXPDSTRING, "defaults export %s - | plutil -convert json -r -o - - | sed -E -e 's/[{}]//g' -e 's/^ *\"(([^\"]|\\\\.)*)\" *: *\"(([^\"]|\\\\.)*)\".*/\\1: \\3/' -e 's/\\\\(.)/\\1/g'", current_prefs);
// open the pipe
fp = popen(cmdbuf, "r");
if (!fp) {
// if opening the pipe failed for some reason, bail out now
if (sys_verbose)
error("%s: %s", current_prefs, strerror(errno));
snprintf(cmdbuf, FILENAME_MAX + 20,
"defaults read '%s' %s 2> /dev/null\n", default_prefs, key);
strcpy(current_prefs, "org.puredata.pd-l2ork");
// Initialize the buffer. Note that we have to reserve one extra byte for
// the terminating NUL character. The buf variable always points to the
// current chunk of memory to be written into.
sys_prefbuf = buf = malloc((sz = BUFSZ)+1);
while (buf && (n = fread(buf, 1, BUFSZ, fp)) > 0) {
char *newbuf;
size_t oldsz = sz;
// terminating NUL byte, to be safe
buf[n] = 0;
// if the byte count is short, then all data has been read; bail out
if (n < BUFSZ) break;
// more data may follow, enlarge the buffer in BUFSZ increments
sz += BUFSZ;
if ((newbuf = realloc(sys_prefbuf, sz+1))) {
// memory allocation succeeded, prepare the new buffer for the next read
sys_prefbuf = newbuf;
// adjust the current buffer pointer
buf = newbuf + oldsz;
} else {
// memory allocation failed, bail out
buf = NULL;
FILE *fp = popen(cmdbuf, "r");
while (nread < size)
int newread = fread(value+nread, 1, size-nread, fp);
if (newread <= 0)
nread += newread;
// close the pipe
res = pclose(fp);
if (res)
post("%s: pclose returned exit status %d", current_prefs, WEXITSTATUS(res));
// check for memory allocation errors
if (!buf) {
error("couldn't allocate memory for preferences buffer");
if (nread < 1)
// When we come here, n is the length of the last chunk we read into buf.
// Add the terminating NUL byte there.
buf[n] = 0;
if (sys_verbose)
post("success reading preferences from: %s", current_prefs);
//post("%s: read %d bytes of preferences data", current_prefs, strlen(sys_prefbuf));
static int sys_getpreference(const char *key, char *value, int size)
char searchfor[80], *where, *whereend;
if (!sys_prefbuf)
return (0);
if (nread >= size)
nread = size-1;
value[nread] = 0;
if (value[nread-1] == '\n') /* remove newline character at end */
value[nread-1] = 0;
sprintf(searchfor, "\n%s:", key);
where = strstr(sys_prefbuf, searchfor);
if (!where)
return (0);
where += strlen(searchfor);
while (*where == ' ' || *where == '\t')
for (whereend = where; *whereend && *whereend != '\n'; whereend++)
if (*whereend == '\n')
if (whereend > where + size - 1)
whereend = where + size - 1;
strncpy(value, where, whereend+1-where);
value[whereend+1-where] = 0;
return (1);
static void sys_doneloadpreferences( void)
if (sys_prefbuf)
sys_prefbuf = NULL;
static void sys_initsavepreferences( void)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment