a2569866a62766062a292d9719f0233fd75a1185
[ruby-vorbistagger.git] / ext / comments.c
1 /*
2  * Copyright (C) 2006 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation, version 2.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16  * MA 02110-1301 USA
17  */
18
19 #include <ruby.h>
20 #include <stdbool.h>
21 #include <ctype.h>
22 #include <assert.h>
23
24 #include "vcedit.h"
25
26 static ID id_casecmp, id_replace, id_compare;
27
28 void
29 comments_init (VALUE self, vcedit_state *state)
30 {
31         VALUE items;
32         vorbis_comment *vc;
33         int i;
34
35         vc = vcedit_comments (state);
36
37         items = rb_ary_new2 (vc->comments);
38         rb_iv_set (self, "items", items);
39
40         for (i = 0; i < vc->comments; i++) {
41                 VALUE k, v, pair;
42                 char *ptr, *content = vc->user_comments[i];
43
44                 ptr = strchr (content, '=');
45                 assert (ptr);
46
47                 k = rb_str_new (content, ptr - content);
48                 OBJ_FREEZE (k);
49
50                 v = rb_str_new2 (ptr + 1);
51
52                 pair = rb_ary_new3 (2, k, v);
53                 OBJ_FREEZE (pair);
54
55                 rb_ary_store (items, i, pair);
56         }
57 }
58
59 void
60 comments_sync (VALUE self, vcedit_state *state)
61 {
62         vorbis_comment *vc;
63         struct RArray *items;
64         int i;
65
66         vc = vcedit_comments (state);
67
68         vorbis_comment_clear (vc);
69         vorbis_comment_init (vc);
70
71         items = RARRAY (rb_iv_get (self, "items"));
72
73         for (i = 0; i < items->len; i++) {
74                 struct RArray *pair = RARRAY (items->ptr[i]);
75
76                 vorbis_comment_add_tag (vc,
77                                         StringValuePtr (pair->ptr[0]),
78                                         StringValuePtr (pair->ptr[1]));
79         }
80 }
81
82 /*
83  * call-seq:
84  *  object.inspect -> string
85  *
86  * Returns the contents of *object* as a string.
87  */
88 static VALUE
89 c_inspect (VALUE self)
90 {
91         VALUE ret;
92         struct RArray *items;
93         int i;
94
95         items = RARRAY (rb_iv_get (self, "items"));
96
97         ret = rb_str_buf_new (128);
98         rb_str_buf_cat (ret, "{", 1);
99
100         for (i = 0; i < items->len; i++) {
101                 struct RArray *pair = RARRAY (items->ptr[i]);
102
103                 if (i)
104                         rb_str_buf_cat (ret, ", ", 2);
105
106                 rb_str_buf_append (ret, rb_inspect (pair->ptr[0]));
107                 rb_str_buf_cat (ret, "=>", 2);
108                 rb_str_buf_append (ret, rb_inspect (pair->ptr[1]));
109         }
110
111         rb_str_buf_cat (ret, "}", 1);
112
113         return ret;
114 }
115
116 /*
117  * call-seq:
118  *  object.clear -> object
119  *
120  * Removes all elements from *object* and returns it.
121  */
122 static VALUE
123 c_clear (VALUE self)
124 {
125         rb_ary_clear (rb_iv_get (self, "items"));
126
127         return self;
128 }
129
130 /*
131  * call-seq:
132  *  object.delete(key) -> string or nil
133  *
134  * If a tag with the specified key exists, that tag is deleted and
135  * the tag's value is returned. Otherwise, +nil+ is returned.
136  */
137 static VALUE
138 c_delete (VALUE self, VALUE key)
139 {
140         VALUE ret = Qnil;
141         struct RArray *items;
142         int i, pos = -1;
143
144         items = RARRAY (rb_iv_get (self, "items"));
145
146         for (i = 0; i < items->len; i++) {
147                 struct RArray *pair = RARRAY (items->ptr[i]);
148                 VALUE tmp;
149
150                 tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
151                 if (tmp == INT2FIX (0)) {
152                         ret = pair->ptr[1];
153                         pos = i;
154                         break;
155                 }
156         }
157
158         if (pos != -1)
159                 rb_ary_delete_at (rb_iv_get (self, "items"), pos);
160
161         return ret;
162 }
163
164 /*
165  * call-seq:
166  *  object.keys -> array
167  *
168  * Returns an array that contains the keys of the tags in *object*.
169  */
170 static VALUE
171 c_keys (VALUE self)
172 {
173         VALUE ret;
174         struct RArray *items;
175         int i;
176
177         items = RARRAY (rb_iv_get (self, "items"));
178         ret = rb_ary_new2 (items->len);
179
180         for (i = 0; i < items->len; i++) {
181                 struct RArray *pair = RARRAY (items->ptr[i]);
182
183                 rb_ary_store (ret, i, pair->ptr[0]);
184         }
185
186         return ret;
187 }
188
189 /*
190  * call-seq:
191  *  object.values -> array
192  *
193  * Returns an array that contains the values of the tags in *object*.
194  */
195 static VALUE
196 c_values (VALUE self)
197 {
198         VALUE ret;
199         struct RArray *items;
200         int i;
201
202         items = RARRAY (rb_iv_get (self, "items"));
203         ret = rb_ary_new2 (items->len);
204
205         for (i = 0; i < items->len; i++) {
206                 struct RArray *pair = RARRAY (items->ptr[i]);
207
208                 rb_ary_store (ret, i, pair->ptr[1]);
209         }
210
211         return ret;
212 }
213
214 /*
215  * call-seq:
216  *  object.length -> integer
217  *
218  * Returns the number of tags in *object*.
219  */
220 static VALUE
221 c_length (VALUE self)
222 {
223         struct RArray *items;
224
225         items = RARRAY (rb_iv_get (self, "items"));
226
227         return LONG2NUM (items->len);
228 }
229
230 /*
231  * call-seq:
232  *  object.empty? -> true or false
233  *
234  * Returns true if *object* is empty or false otherwise.
235  */
236 static VALUE
237 c_get_empty (VALUE self)
238 {
239         struct RArray *items;
240
241         items = RARRAY (rb_iv_get (self, "items"));
242
243         return items->len ? Qfalse : Qtrue;
244 }
245
246 /*
247  * call-seq:
248  *  object[key] -> string
249  *
250  * Returns the value of the tag with the key *key* or +nil+ if the
251  * tag cannot be found.
252  */
253 static VALUE
254 c_aref (VALUE self, VALUE key)
255 {
256         struct RArray *items;
257         int i;
258
259         items = RARRAY (rb_iv_get (self, "items"));
260
261         for (i = 0; i < items->len; i++) {
262                 struct RArray *pair = RARRAY (items->ptr[i]);
263                 VALUE tmp;
264
265                 tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
266                 if (tmp == INT2FIX (0))
267                         return pair->ptr[1];
268         }
269
270         return Qnil;
271 }
272
273 /*
274  * call-seq:
275  *  object[key] = string
276  *
277  * Sets the value of the tag with the key *key* to *string*.
278  */
279 static VALUE
280 c_aset (VALUE self, VALUE key, VALUE value)
281 {
282         VALUE tmp;
283         struct RArray *items;
284         int i;
285
286         items = RARRAY (rb_iv_get (self, "items"));
287
288         for (i = 0; i < items->len; i++) {
289                 struct RArray *pair = RARRAY (items->ptr[i]);
290                 VALUE tmp;
291
292                 tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
293                 if (tmp == INT2FIX (0)) {
294                         rb_funcall (pair->ptr[1], id_replace, 1, value);
295                         return pair->ptr[1];
296                 }
297         }
298
299         tmp = rb_ary_new3 (2, rb_str_dup_frozen (key), value);
300         OBJ_FREEZE (tmp);
301
302         rb_ary_push (rb_iv_get (self, "items"), tmp);
303
304         return value;
305 }
306
307 /*
308  * call-seq:
309  *  object.has_key?(key) -> true or false
310  *
311  * Returns true if a tag exists with the specified key or false
312  * otherwise.
313  */
314 static VALUE
315 c_has_key (VALUE self, VALUE key)
316 {
317         struct RArray *items;
318         int i;
319
320         items = RARRAY (rb_iv_get (self, "items"));
321
322         for (i = 0; i < items->len; i++) {
323                 struct RArray *pair = RARRAY (items->ptr[i]);
324                 VALUE tmp;
325
326                 tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
327                 if (tmp == INT2FIX (0))
328                         return Qtrue;
329         }
330
331         return Qfalse;
332 }
333
334 /*
335  * call-seq:
336  *  object <=> other -> -1, 0 or 1
337  *
338  * Compares *object* to *other* and returns -1, 0 or 1 if
339  * *object* is less than, equal or greater than *other*.
340  */
341 static VALUE
342 c_compare (VALUE self, VALUE other)
343 {
344         struct RArray *a, *b;
345         int i, j;
346
347         if (rb_obj_is_kind_of (other, CLASS_OF (self)) != Qtrue)
348                 rb_raise (rb_eArgError, "invalid argument");
349
350         a = RARRAY (rb_iv_get (self, "items"));
351         b = RARRAY (rb_iv_get (other, "items"));
352
353         if (a->len < b->len)
354                 return -1;
355
356         if (b->len < a->len)
357                 return 1;
358
359         for (i = 0; i < a->len; i++) {
360                 struct RArray *aa = RARRAY (a->ptr[i]);
361                 struct RArray *bb = RARRAY (b->ptr[i]);
362
363                 for (j = 0; j < 2; j++) {
364                         VALUE tmp;
365
366                         tmp = rb_funcall (aa->ptr[j], id_compare, 1, bb->ptr[j]);
367                         if (FIX2INT (tmp) != 0)
368                                 return tmp;
369                 }
370         }
371
372         return INT2FIX (0);
373 }
374
375 /*
376  * call-seq:
377  *  object.each { |key, value| block } -> object
378  *
379  * Calls _block_ once for each tag in *object*, passing the key and
380  * value of the tag.
381  * Returns *object*.
382  */
383 static VALUE
384 c_each (VALUE self)
385 {
386         struct RArray *items;
387         int i;
388
389         items = RARRAY (rb_iv_get (self, "items"));
390
391         for (i = 0; i < items->len; i++) {
392                 struct RArray *pair = RARRAY (items->ptr[i]);
393
394                 rb_yield_values (2, pair->ptr[0], pair->ptr[1]);
395         }
396
397         return self;
398 }
399
400 /*
401  * call-seq:
402  *  object.each_key { |key| block } -> object
403  *
404  * Calls _block_ once for each tag in *object*, passing the key
405  * of the tag.
406  * Returns *object*.
407  */
408 static VALUE
409 c_each_key (VALUE self)
410 {
411         struct RArray *items;
412         int i;
413
414         items = RARRAY (rb_iv_get (self, "items"));
415
416         for (i = 0; i < items->len; i++) {
417                 struct RArray *pair = RARRAY (items->ptr[i]);
418
419                 rb_yield (pair->ptr[0]);
420         }
421
422         return self;
423 }
424
425 /*
426  * call-seq:
427  *  object.each_value { |value| block } -> object
428  *
429  * Calls _block_ once for each tag in *object*, passing the value
430  * of the tag. Returns *object*.
431  */
432 static VALUE
433 c_each_value (VALUE self)
434 {
435         struct RArray *items;
436         int i;
437
438         items = RARRAY (rb_iv_get (self, "items"));
439
440         for (i = 0; i < items->len; i++) {
441                 struct RArray *pair = RARRAY (items->ptr[i]);
442
443                 rb_yield (pair->ptr[1]);
444         }
445
446         return self;
447 }
448
449 VALUE
450 Init_Comments (VALUE mVorbis)
451 {
452         VALUE c;
453
454         c = rb_define_class_under (mVorbis, "Comments", rb_cObject);
455
456         rb_define_method (c, "inspect", c_inspect, 0);
457         rb_define_method (c, "clear", c_clear, 0);
458         rb_define_method (c, "delete", c_delete, 1);
459         rb_define_method (c, "length", c_length, 0);
460         rb_define_method (c, "has_key?", c_has_key, 1);
461         rb_define_method (c, "[]", c_aref, 1);
462         rb_define_method (c, "[]=", c_aset, 2);
463         rb_define_method (c, "empty?", c_get_empty, 0);
464         rb_define_method (c, "keys", c_keys, 0);
465         rb_define_method (c, "values", c_values, 0);
466
467         rb_include_module (c, rb_mComparable);
468         rb_define_method (c, "<=>", c_compare, 1);
469
470         rb_include_module (c, rb_mEnumerable);
471         rb_define_method (c, "each", c_each, 0);
472         rb_define_method (c, "each_key", c_each_key, 0);
473         rb_define_method (c, "each_value", c_each_value, 0);
474
475         rb_define_alias (c, "size", "length");
476         rb_define_alias (c, "each_pair", "each");
477         rb_define_alias (c, "key?", "has_key?");
478         rb_define_alias (c, "include?", "has_key?");
479         rb_define_alias (c, "member?", "has_key?");
480
481         id_casecmp = rb_intern ("casecmp");
482         id_replace = rb_intern ("replace");
483         id_compare = rb_intern ("<=>");
484
485         return c;
486 }