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