Fix more warnings. Use -Wall flag to enable those warnings to show up in first place.
[calfbox.git] / sfzparser.c
blob831044cf93b8644e658f53d80d051310ff2e2887
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "sfzparser.h"
20 #include <ctype.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
26 struct sfz_parser_state
28 struct sfz_parser_client *client;
29 gboolean (*handler)(struct sfz_parser_state *state, int ch);
30 const char *filename;
31 const char *buf;
32 int pos;
33 int token_start;
34 int key_start, key_end;
35 int value_start, value_end;
36 GError **error;
39 static gboolean handle_char(struct sfz_parser_state *state, int ch);
41 static gboolean handle_2ndslash(struct sfz_parser_state *state, int ch)
43 if (ch == '\r' || ch == '\n')
45 state->handler = handle_char;
46 state->token_start = state->pos;
48 // otherwise, consume
49 return TRUE;
52 static void unexpected_char(struct sfz_parser_state *state, int ch)
54 g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unexpected character '%c' (%d)", ch, ch);
57 static gboolean handle_postslash(struct sfz_parser_state *state, int ch)
59 if (ch == '/')
61 state->handler = handle_2ndslash;
62 return TRUE;
64 unexpected_char(state, ch);
65 return FALSE;
68 static gboolean handle_header(struct sfz_parser_state *state, int ch)
70 if (ch >= 'a' && ch <= 'z')
71 return TRUE;
72 if (ch == '>')
74 if (!strncmp(state->buf + state->token_start, "region", state->pos - 1 - state->token_start))
76 state->client->region(state->client);
78 else
79 if (!strncmp(state->buf + state->token_start, "group", state->pos - 1 - state->token_start))
81 state->client->group(state->client);
83 else
85 gchar *tmp = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start);
86 g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_HEADER, "Unexpected header <%s>", tmp);
87 g_free(tmp);
88 return FALSE;
90 state->handler = handle_char;
91 return TRUE;
93 unexpected_char(state, ch);
94 return FALSE;
97 static void scan_for_value(struct sfz_parser_state *state)
99 state->value_start = state->pos;
100 while(1)
102 int ch = state->buf[state->pos];
103 if (ch == 0 || ch == '\r' || ch == '\n' || ch == '<')
105 state->value_end = state->pos;
106 // remove spaces before next key
107 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
108 state->value_end--;
109 return;
111 if (ch == '=')
113 state->value_end = state->pos;
114 // remove next key
115 while(state->value_end > state->value_start && !isspace(state->buf[state->value_end - 1]))
116 state->value_end--;
117 // remove spaces before next key
118 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
119 state->value_end--;
120 state->pos = state->value_end;
121 return;
123 state->pos++;
127 static gboolean handle_key(struct sfz_parser_state *state, int ch)
129 if (isalpha(ch) || isdigit(ch) || ch == '_')
130 return TRUE;
131 if(ch == '=')
133 state->key_end = state->pos - 1;
134 scan_for_value(state);
136 gchar *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
137 gchar *value = g_strndup(state->buf + state->value_start, state->value_end - state->value_start);
138 gboolean result = state->client->key_value(state->client, key, value);
139 g_free(key);
140 g_free(value);
141 state->handler = handle_char;
142 return result;
144 unexpected_char(state, ch);
145 return FALSE;
148 static gboolean handle_char(struct sfz_parser_state *state, int ch)
150 if (isalpha(ch) || isdigit(ch))
152 state->key_start = state->pos - 1;
153 state->handler = handle_key;
154 return TRUE;
156 switch(ch)
158 case '_':
159 return TRUE;
161 case '\r':
162 case '\n':
163 case ' ':
164 case '\t':
165 case -1:
166 return TRUE;
167 case '/':
168 state->handler = handle_postslash;
169 return TRUE;
170 case '<':
171 state->token_start = state->pos;
172 state->handler = handle_header;
173 return TRUE;
174 default:
175 unexpected_char(state, ch);
176 return FALSE;
180 gboolean load_sfz(const char *name, struct sfz_parser_client *c, GError **error)
182 g_clear_error(error);
183 FILE *f = fopen(name, "rb");
184 if (!f)
186 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s'", name);
187 return FALSE;
190 fseek(f, 0, 2);
191 int len = ftell(f);
192 fseek(f, 0, 0);
194 unsigned char *buf = malloc(len + 1);
195 buf[len] = '\0';
196 if (fread(buf, 1, len, f) != len)
198 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot read '%s'", name);
199 fclose(f);
200 return FALSE;
202 fclose(f);
203 gboolean result = load_sfz_from_string((char *)buf, len, c, error);
204 free(buf);
205 return result;
208 gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error)
210 struct sfz_parser_state s;
211 s.filename = "<inline>";
212 s.buf = buf;
213 s.handler = handle_char;
214 s.pos = 0;
215 s.token_start = 0;
216 s.client = c;
217 s.error = error;
218 if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
220 // UTF-8 BOM
221 s.pos += 3;
223 while(s.pos < len && s.handler != NULL)
225 if (!(*s.handler)(&s, buf[s.pos++]))
226 return FALSE;
228 if (s.handler)
230 if (!(*s.handler)(&s, -1))
231 return FALSE;
234 return TRUE;
237 GQuark cbox_sfz_parser_error_quark(void)
239 return g_quark_from_string("cbox-sfz-parser-error-quark");