Separated vcedit_state_new() and vcedit_open() again.
[ruby-vorbistagger.git] / ext / ext.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 <stdio.h>
21 #include <stdbool.h>
22 #include <errno.h>
23
24 #include "vcedit.h"
25 #include "comments.h"
26
27 typedef struct {
28         vcedit_state *state;
29         VALUE comments;
30 } RbVorbisTagger;
31
32 static VALUE c_close (VALUE self);
33
34 static VALUE cComments, eVTError;
35 static ID id_length;
36
37 static void
38 c_free (RbVorbisTagger *o)
39 {
40         /* just in case the user forgot to call #close himself */
41         if (o->state)
42                 vcedit_state_unref (o->state);
43
44         ruby_xfree (o);
45 }
46
47 static void
48 c_mark (RbVorbisTagger *o)
49 {
50         rb_gc_mark (o->comments);
51 }
52
53 static VALUE
54 c_alloc (VALUE klass)
55 {
56         RbVorbisTagger *o;
57
58         return Data_Make_Struct (klass, RbVorbisTagger, c_mark, c_free, o);
59 }
60
61 /*
62  * call-seq:
63  *  Ogg::Vorbis::Tagger.open(arg)                    -> object
64  *  Ogg::Vorbis::Tagger.open(arg) { |object| block } -> object
65  *
66  * If a block isn't specified, Ogg::Vorbis::Tagger.open is a synonym
67  * for Ogg::Vorbis::Tagger.new.
68  * If a block is given, it will be invoked with the * Ogg::Vorbis::Tagger
69  * object as a parameter.
70  */
71 static VALUE
72 c_open (VALUE klass, VALUE arg)
73 {
74         VALUE obj = rb_class_new_instance (1, &arg, klass);
75
76         if (rb_block_given_p ())
77                 return rb_ensure (rb_yield, obj, c_close, obj);
78         else
79                 return obj;
80 }
81
82 /*
83  * call-seq:
84  *  Ogg::Vorbis::Tagger.new(filename) -> object
85  *
86  * Returns a new Ogg::Vorbis::Tagger object for the specified file.
87  *
88  * FIXME: add optional mode argument (read-only or read-write)
89  */
90 static VALUE
91 c_init (VALUE self, VALUE filename)
92 {
93         RbVorbisTagger *o;
94         vorbis_comment *vc;
95         int i;
96
97         Data_Get_Struct (self, RbVorbisTagger, o);
98
99         StringValue (filename);
100
101         o->state = vcedit_state_new (StringValuePtr (filename));
102         if (!o->state)
103                 rb_raise (eVTError, "vcedit_new_state() failed - %s",
104                           vcedit_error (o->state));
105
106         if (vcedit_open (o->state) < 0)
107                 rb_raise (eVTError, "vcedit_open() failed - %s",
108                           vcedit_error (o->state));
109
110         vc = vcedit_comments (o->state);
111         if (!vc)
112                 rb_raise (eVTError, "vcedit_comments() failed - %s",
113                           vcedit_error (o->state));
114
115         /* check whether all comments are well-formed */
116         for (i = 0; i < vc->comments; i++) {
117                 char *ptr, *content = vc->user_comments[i];
118
119                 ptr = strchr (content, '=');
120                 if (!ptr || ptr == content)
121                         rb_raise (eVTError, "malformed comment - %s", content);
122         }
123
124         o->comments = rb_class_new_instance (0, NULL, cComments);
125
126         comments_init (o->comments, o->state);
127
128         return self;
129 }
130
131 /*
132  * call-seq:
133  *  object.close -> object
134  *
135  * Closes *object* and returns it.
136  */
137 static VALUE
138 c_close (VALUE self)
139 {
140         RbVorbisTagger *o;
141
142         Data_Get_Struct (self, RbVorbisTagger, o);
143
144         vcedit_state_unref (o->state);
145         o->state = NULL;
146
147         return self;
148 }
149
150 /*
151  * call-seq:
152  *  object.write -> integer
153  *
154  * Writes the comments from *object* back to the IO object and
155  * returns the numbers of comments written.
156  */
157 static VALUE
158 c_write (VALUE self)
159 {
160         RbVorbisTagger *o;
161
162         Data_Get_Struct (self, RbVorbisTagger, o);
163
164         comments_sync (o->comments, o->state);
165
166         if (vcedit_write (o->state) < 0)
167                 rb_raise (rb_eIOError, "write failed - %s",
168                           vcedit_error (o->state));
169
170         return rb_funcall (o->comments, id_length, 0);
171 }
172
173 /*
174  * call-seq:
175  *  object.comments -> comments
176  *
177  * Returns the comments collection of *object*, which is an instance of
178  * Ogg::Vorbis::Comments.
179  */
180 static VALUE
181 c_comments (VALUE self)
182 {
183         RbVorbisTagger *o;
184
185         Data_Get_Struct (self, RbVorbisTagger, o);
186
187         return o->comments;
188 }
189
190 EXT_API
191 void
192 Init_vorbistagger_ext (void)
193 {
194         VALUE mOgg, mVorbis, eOgg, eVorbis, cVT;
195
196         mOgg = rb_define_module ("Ogg");
197         mVorbis = rb_define_module_under (mOgg, "Vorbis");
198
199         eOgg = rb_define_class_under (mOgg, "OggError", rb_eStandardError);
200         eVorbis = rb_define_class_under (mVorbis, "VorbisError", eOgg);
201
202         cVT = rb_define_class_under (mVorbis, "Tagger", rb_cObject);
203
204         rb_define_alloc_func (cVT, c_alloc);
205
206         rb_define_singleton_method (cVT, "open", c_open, 1);
207         rb_define_method (cVT, "initialize", c_init, 1);
208         rb_define_method (cVT, "close", c_close, 0);
209         rb_define_method (cVT, "write", c_write, 0);
210         rb_define_method (cVT, "comments", c_comments, 0);
211
212         eVTError = rb_define_class_under (cVT, "TaggerError", eVorbis);
213
214         id_length = rb_intern ("length");
215
216         cComments = Init_Comments (mVorbis);
217 }