add dbus command to allow checking of wether hardware is currently exported
[a2jmidid.git] / conf.c
blob8e4a7f87564e399164499e06e161c921da1f080b
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * ALSA SEQ < - > JACK MIDI bridge
5 * Copyright (c) 2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <sys/stat.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <time.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <expat.h>
30 #include "conf.h"
31 #include "log.h"
32 #include "paths.h"
34 #define A2J_CONF_HEADER_TEXT \
35 "a2jmidid settings.\n" \
36 "You probably don't want to edit this because\n" \
37 "it will be overwritten next time a2jmidid saves.\n"
39 bool
40 a2j_settings_write_string(int fd, const char * string)
42 size_t len;
44 len = strlen(string);
46 if (write(fd, string, len) != len)
48 a2j_error("write() failed to write config file.");
49 return false;
52 return true;
55 bool
56 a2j_settings_write_option(
57 int fd,
58 const char * name,
59 const char * content)
61 if (!a2j_settings_write_string(fd, " "))
63 return false;
66 if (!a2j_settings_write_string(fd, "<option name=\""))
68 return false;
71 if (!a2j_settings_write_string(fd, name))
73 return false;
76 if (!a2j_settings_write_string(fd, "\">"))
78 return false;
81 if (!a2j_settings_write_string(fd, content))
83 return false;
86 if (!a2j_settings_write_string(fd, "</option>\n"))
88 return false;
91 return true;
94 void
95 a2j_conf_save()
97 int fd;
98 bool ret;
99 time_t timestamp;
100 char timestamp_str[26];
102 time(&timestamp);
103 ctime_r(&timestamp, timestamp_str);
104 timestamp_str[24] = 0;
106 a2j_info("Saving settings to \"%s\" ...", g_a2j_conf_path);
108 fd = open(g_a2j_conf_path, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
109 if (fd == -1)
111 a2j_error("open() failed to open conf filename. error is %d (%s)", errno, strerror(errno));
112 goto exit;
115 if (!a2j_settings_write_string(fd, "<?xml version=\"1.0\"?>\n"))
117 goto exit_close;
120 if (!a2j_settings_write_string(fd, "<!--\n"))
122 goto exit_close;
125 if (!a2j_settings_write_string(fd, A2J_CONF_HEADER_TEXT))
127 goto exit_close;
130 if (!a2j_settings_write_string(fd, "-->\n"))
132 goto exit_close;
135 if (!a2j_settings_write_string(fd, "<!-- "))
137 goto exit_close;
140 if (!a2j_settings_write_string(fd, timestamp_str))
142 goto exit_close;
145 if (!a2j_settings_write_string(fd, " -->\n"))
147 goto exit_close;
150 if (!a2j_settings_write_string(fd, "<a2jmidid>\n"))
152 goto exit_close;
155 if (!a2j_settings_write_option(fd, "jack_server_name", g_a2j_jack_server_name))
157 goto exit_close;
160 if (!a2j_settings_write_option(fd, "export_hw_ports", g_a2j_export_hw_ports ? "true" : "false"))
162 goto exit_close;
165 if (!a2j_settings_write_string(fd, "</a2jmidid>\n"))
167 goto exit_close;
170 ret = true;
172 exit_close:
173 close(fd);
175 exit:
176 return;
179 #define PARSE_CONTEXT_ROOT 0
180 #define PARSE_CONTEXT_A2J 1
181 #define PARSE_CONTEXT_OPTION 2
183 #define MAX_STACK_DEPTH 10
184 #define MAX_OPTION_LENGTH 100
186 struct parse_context
188 XML_Bool error;
189 unsigned int element[MAX_STACK_DEPTH];
190 signed int depth;
191 char option[MAX_OPTION_LENGTH+1];
192 int option_used;
193 char * name;
196 void
197 a2j_conf_set_bool_option(
198 const char * value_str,
199 bool * value_ptr)
201 if (strcmp(value_str, "true") == 0)
203 *value_ptr = true;
205 else if (strcmp(value_str, "false") == 0)
207 *value_ptr = false;
209 else
211 a2j_error("ignoring unknown bool value \"%s\"", value_str);
215 void
216 a2j_conf_set_string_option(
217 const char * input,
218 char ** value)
220 char * dup;
222 dup = strdup(input);
223 if (dup == NULL)
225 a2j_error("Out of memory");
226 return;
229 *value = dup;
232 void
233 a2j_conf_set_option(
234 const char * option_name,
235 const char * option_value)
237 a2j_info("setting option \"%s\" to value \"%s\"", option_name, option_value);
239 if (strcmp(option_name, "jack_server_name") == 0)
241 a2j_conf_set_string_option(option_value, &g_a2j_jack_server_name);
243 else if (strcmp(option_name, "export_hw_ports") == 0)
245 a2j_conf_set_bool_option(option_value, &g_a2j_export_hw_ports);
247 else
249 a2j_error(
250 "Unknown parameter \"%s\"",
251 option_name);
252 return;
256 #define context_ptr ((struct parse_context *)data)
258 void
259 a2j_conf_settings_callback_chrdata(void *data, const XML_Char *s, int len)
261 if (context_ptr->error)
263 return;
266 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_OPTION)
268 if (context_ptr->option_used + len >= MAX_OPTION_LENGTH)
270 a2j_error("xml parse max char data length reached");
271 context_ptr->error = XML_TRUE;
272 return;
275 memcpy(context_ptr->option + context_ptr->option_used, s, len);
276 context_ptr->option_used += len;
280 void
281 a2j_conf_settings_callback_elstart(void *data, const char *el, const char **attr)
283 if (context_ptr->error)
285 return;
288 if (context_ptr->depth + 1 >= MAX_STACK_DEPTH)
290 a2j_error("xml parse max stack depth reached");
291 context_ptr->error = XML_TRUE;
292 return;
295 if (strcmp(el, "a2jmidid") == 0)
297 //a2j_info("<jack>");
298 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_A2J;
299 return;
302 if (strcmp(el, "option") == 0)
304 //a2j_info("<option>");
305 if ((attr[0] == NULL || attr[2] != NULL) || strcmp(attr[0], "name") != 0)
307 a2j_error("<option> XML element must contain exactly one attribute, named \"name\"");
308 context_ptr->error = XML_TRUE;
309 return;
312 context_ptr->name = strdup(attr[1]);
313 if (context_ptr->name == NULL)
315 a2j_error("strdup() failed");
316 context_ptr->error = XML_TRUE;
317 return;
320 context_ptr->element[++context_ptr->depth] = PARSE_CONTEXT_OPTION;
321 context_ptr->option_used = 0;
322 return;
325 a2j_error("unknown element \"%s\"", el);
326 context_ptr->error = XML_TRUE;
329 void
330 a2j_conf_settings_callback_elend(void *data, const char *el)
332 if (context_ptr->error)
334 return;
337 //a2j_info("element end (depth = %d, element = %u)", context_ptr->depth, context_ptr->element[context_ptr->depth]);
339 if (context_ptr->element[context_ptr->depth] == PARSE_CONTEXT_OPTION)
341 context_ptr->option[context_ptr->option_used] = 0;
343 if (context_ptr->depth == 1 &&
344 context_ptr->element[0] == PARSE_CONTEXT_A2J)
346 a2j_conf_set_option(context_ptr->name, context_ptr->option);
350 context_ptr->depth--;
352 if (context_ptr->name != NULL)
354 free(context_ptr->name);
355 context_ptr->name = NULL;
359 #undef context_ptr
361 void
362 a2j_conf_load()
364 XML_Parser parser;
365 int bytes_read;
366 void *buffer;
367 struct stat st;
368 int fd;
369 enum XML_Status xmls;
370 struct parse_context context;
372 a2j_info("Loading settings from \"%s\" using %s ...", g_a2j_conf_path, XML_ExpatVersion());
374 if (stat(g_a2j_conf_path, &st) != 0)
376 if (errno == ENOENT)
378 a2j_info("No conf file found, using defaults...");
379 return;
382 a2j_error("failed to stat \"%s\", error is %d (%s)", g_a2j_conf_path, errno, strerror(errno));
385 fd = open(g_a2j_conf_path, O_RDONLY);
386 if (fd == -1)
388 a2j_error("open() failed to open conf filename.");
389 goto exit;
392 parser = XML_ParserCreate(NULL);
393 if (parser == NULL)
395 a2j_error("XML_ParserCreate() failed to create parser object.");
396 goto exit_close_file;
399 //a2j_info("conf file size is %llu bytes", (unsigned long long)st.st_size);
401 /* we are expecting that conf file has small enough size to fit in memory */
403 buffer = XML_GetBuffer(parser, st.st_size);
404 if (buffer == NULL)
406 a2j_error("XML_GetBuffer() failed.");
407 goto exit_free_parser;
410 bytes_read = read(fd, buffer, st.st_size);
411 if (bytes_read != st.st_size)
413 a2j_error("read() returned unexpected result.");
414 goto exit_free_parser;
417 context.error = XML_FALSE;
418 context.depth = -1;
419 context.name = NULL;
421 XML_SetElementHandler(parser, a2j_conf_settings_callback_elstart, a2j_conf_settings_callback_elend);
422 XML_SetCharacterDataHandler(parser, a2j_conf_settings_callback_chrdata);
423 XML_SetUserData(parser, &context);
425 xmls = XML_ParseBuffer(parser, bytes_read, XML_TRUE);
426 if (xmls == XML_STATUS_ERROR)
428 a2j_error("XML_ParseBuffer() failed.");
429 goto exit_free_parser;
432 exit_free_parser:
433 XML_ParserFree(parser);
435 exit_close_file:
436 close(fd);
438 exit:
439 return;