Use linked list for storing sources in MIDI merger.
[calfbox.git] / sfzparser.c
blobea7cdf21c0577836eb3e7afa71a6409935f0d65d
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, len;
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 void unexpected_char(struct sfz_parser_state *state, int ch)
43 g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unexpected character '%c' (%d)", ch, ch);
46 static gboolean handle_header(struct sfz_parser_state *state, int ch)
48 if (ch >= 'a' && ch <= 'z')
49 return TRUE;
50 if (ch == '>')
52 char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start);
53 gboolean result = state->client->token(state->client, token, state->error);
54 g_free(token);
55 state->handler = handle_char;
56 return result;
58 unexpected_char(state, ch);
59 return FALSE;
62 static void scan_for_value(struct sfz_parser_state *state)
64 state->value_start = state->pos;
65 while(state->pos < state->len)
67 if (state->pos < state->len + 2 && state->buf[state->pos] == '/' && state->buf[state->pos + 1] == '/')
69 state->value_end = state->pos;
70 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
71 state->value_end--;
72 state->pos += 2;
73 while (state->pos < state->len && state->buf[state->pos] != '\r' && state->buf[state->pos] != '\n')
74 state->pos++;
75 return;
77 int ch = state->buf[state->pos];
78 if (ch == 0 || ch == '\r' || ch == '\n' || ch == '<')
80 state->value_end = state->pos;
81 // remove spaces before next key
82 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
83 state->value_end--;
84 return;
86 if (ch == '=')
88 state->value_end = state->pos;
89 // remove next key
90 while(state->value_end > state->value_start && !isspace(state->buf[state->value_end - 1]))
91 state->value_end--;
92 // remove spaces before next key
93 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
94 state->value_end--;
95 state->pos = state->value_end;
96 return;
98 state->pos++;
100 state->value_end = state->pos;
101 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
102 state->value_end--;
105 static gboolean handle_key(struct sfz_parser_state *state, int ch)
107 if (isalpha(ch) || isdigit(ch) || ch == '_')
108 return TRUE;
109 if(ch == '=')
111 state->key_end = state->pos - 1;
112 scan_for_value(state);
114 gchar *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
115 gchar *value = g_strndup(state->buf + state->value_start, state->value_end - state->value_start);
116 gboolean result = state->client->key_value(state->client, key, value);
117 g_free(key);
118 g_free(value);
119 state->handler = handle_char;
120 return result;
122 unexpected_char(state, ch);
123 return FALSE;
126 static gboolean handle_char(struct sfz_parser_state *state, int ch)
128 if (isalpha(ch) || isdigit(ch))
130 state->key_start = state->pos - 1;
131 state->handler = handle_key;
132 return TRUE;
134 switch(ch)
136 case '_':
137 return TRUE;
139 case '\r':
140 case '\n':
141 case ' ':
142 case '\t':
143 case -1:
144 return TRUE;
145 case '<':
146 state->token_start = state->pos;
147 state->handler = handle_header;
148 return TRUE;
149 default:
150 unexpected_char(state, ch);
151 return FALSE;
155 gboolean load_sfz(const char *name, struct sfz_parser_client *c, GError **error)
157 g_clear_error(error);
158 FILE *f = fopen(name, "rb");
159 if (!f)
161 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s'", name);
162 return FALSE;
165 fseek(f, 0, 2);
166 int len = ftell(f);
167 fseek(f, 0, 0);
169 unsigned char *buf = malloc(len + 1);
170 buf[len] = '\0';
171 if (fread(buf, 1, len, f) != len)
173 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot read '%s'", name);
174 fclose(f);
175 return FALSE;
177 fclose(f);
178 gboolean result = load_sfz_from_string((char *)buf, len, c, error);
179 free(buf);
180 return result;
183 gboolean load_sfz_from_string(const char *buf, int len, struct sfz_parser_client *c, GError **error)
185 struct sfz_parser_state s;
186 s.filename = "<inline>";
187 s.buf = buf;
188 s.handler = handle_char;
189 s.pos = 0;
190 s.len = len;
191 s.token_start = 0;
192 s.client = c;
193 s.error = error;
194 if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
196 // UTF-8 BOM
197 s.pos += 3;
199 while(s.pos < len && s.handler != NULL)
201 if (s.pos < len + 2 && buf[s.pos] == '/' && buf[s.pos + 1] == '/')
203 s.pos += 2;
204 while (s.pos < len && buf[s.pos] != '\r' && buf[s.pos] != '\n')
205 s.pos++;
206 continue;
208 if (!(*s.handler)(&s, buf[s.pos++]))
209 return FALSE;
211 if (s.handler)
213 if (!(*s.handler)(&s, -1))
214 return FALSE;
217 return TRUE;
220 GQuark cbox_sfz_parser_error_quark(void)
222 return g_quark_from_string("cbox-sfz-parser-error-quark");