Fix broken bounds check.
[calfbox.git] / sfzparser.c
blobb57c3d822ce4ebc801e5a50a0fd453e2a697227c
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 "tarfile.h"
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 struct sfz_parser_state
29 struct sfz_parser_client *client;
30 gboolean (*handler)(struct sfz_parser_state *state, int ch);
31 const char *filename;
32 const char *buf;
33 int pos, len;
34 int token_start;
35 int key_start, key_end;
36 int value_start, value_end;
37 GError **error;
40 static gboolean handle_char(struct sfz_parser_state *state, int ch);
42 static void unexpected_char(struct sfz_parser_state *state, int ch)
44 g_set_error(state->error, CBOX_SFZPARSER_ERROR, CBOX_SFZ_PARSER_ERROR_INVALID_CHAR, "Unexpected character '%c' (%d)", ch, ch);
47 static gboolean handle_header(struct sfz_parser_state *state, int ch)
49 if (ch >= 'a' && ch <= 'z')
50 return TRUE;
51 if (ch == '>')
53 char *token = g_strndup(state->buf + state->token_start, state->pos - 1 - state->token_start);
54 gboolean result = state->client->token(state->client, token, state->error);
55 g_free(token);
56 state->handler = handle_char;
57 return result;
59 unexpected_char(state, ch);
60 return FALSE;
63 static void scan_for_value(struct sfz_parser_state *state)
65 state->value_start = state->pos;
66 while(state->pos < state->len)
68 if (state->pos < state->len + 2 && state->buf[state->pos] == '/' && state->buf[state->pos + 1] == '/')
70 state->value_end = state->pos;
71 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
72 state->value_end--;
73 state->pos += 2;
74 while (state->pos < state->len && state->buf[state->pos] != '\r' && state->buf[state->pos] != '\n')
75 state->pos++;
76 return;
78 int ch = state->buf[state->pos];
79 if (ch == 0 || ch == '\r' || ch == '\n' || ch == '<')
81 state->value_end = state->pos;
82 // remove spaces before next key
83 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
84 state->value_end--;
85 return;
87 if (ch == '=')
89 state->value_end = state->pos;
90 // remove next key
91 while(state->value_end > state->value_start && !isspace(state->buf[state->value_end - 1]))
92 state->value_end--;
93 // remove spaces before next key
94 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
95 state->value_end--;
96 state->pos = state->value_end;
97 return;
99 state->pos++;
101 state->value_end = state->pos;
102 while(state->value_end > state->value_start && isspace(state->buf[state->value_end - 1]))
103 state->value_end--;
106 static gboolean handle_key(struct sfz_parser_state *state, int ch)
108 if (isalpha(ch) || isdigit(ch) || ch == '_')
109 return TRUE;
110 if(ch == '=')
112 state->key_end = state->pos - 1;
113 scan_for_value(state);
115 gchar *key = g_strndup(state->buf + state->key_start, state->key_end - state->key_start);
116 gchar *value = g_strndup(state->buf + state->value_start, state->value_end - state->value_start);
117 gboolean result = state->client->key_value(state->client, key, value);
118 g_free(key);
119 g_free(value);
120 state->handler = handle_char;
121 return result;
123 unexpected_char(state, ch);
124 return FALSE;
127 static gboolean handle_char(struct sfz_parser_state *state, int ch)
129 if (isalpha(ch) || isdigit(ch))
131 state->key_start = state->pos - 1;
132 state->handler = handle_key;
133 return TRUE;
135 switch(ch)
137 case '_':
138 return TRUE;
140 case '\r':
141 case '\n':
142 case ' ':
143 case '\t':
144 case -1:
145 return TRUE;
146 case '<':
147 state->token_start = state->pos;
148 state->handler = handle_header;
149 return TRUE;
150 default:
151 unexpected_char(state, ch);
152 return FALSE;
156 gboolean load_sfz(const char *name, struct cbox_tarfile *tarfile, struct sfz_parser_client *c, GError **error)
158 g_clear_error(error);
159 FILE *f;
160 int len = -1;
161 if (tarfile)
163 struct cbox_taritem *item = cbox_tarfile_get_item_by_name(tarfile, name, TRUE);
164 if (!item)
166 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (2), "Cannot find '%s' in the tarfile", name);
167 return FALSE;
169 int fd = cbox_tarfile_openitem(tarfile, item);
170 if (fd < 0)
172 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s' in the tarfile", name);
173 return FALSE;
175 f = fdopen(fd, "rb");
176 len = item->size;
178 else
179 f = fopen(name, "rb");
181 if (!f)
183 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "Cannot open '%s'", name);
184 return FALSE;
187 if (len == -1)
189 fseek(f, 0, SEEK_END);
190 len = ftell(f);
191 fseek(f, 0, SEEK_SET);
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.len = len;
216 s.token_start = 0;
217 s.client = c;
218 s.error = error;
219 if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
221 // UTF-8 BOM
222 s.pos += 3;
224 while(s.pos < len && s.handler != NULL)
226 if (s.pos < len + 2 && buf[s.pos] == '/' && buf[s.pos + 1] == '/')
228 s.pos += 2;
229 while (s.pos < len && buf[s.pos] != '\r' && buf[s.pos] != '\n')
230 s.pos++;
231 continue;
233 if (!(*s.handler)(&s, buf[s.pos++]))
234 return FALSE;
236 if (s.handler)
238 if (!(*s.handler)(&s, -1))
239 return FALSE;
242 return TRUE;
245 GQuark cbox_sfz_parser_error_quark(void)
247 return g_quark_from_string("cbox-sfz-parser-error-quark");