vorbis_comment 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/extconf.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+ find_library('ogg', 'ogg_sync_init')
3
+ find_library('vorbis', 'vorbis_comment_init')
4
+ create_makefile("vorbis_comment_ext")
data/test/blank.ogg ADDED
Binary file
data/test/corrupt.ogg ADDED
Binary file
data/test/lt8k.ogg ADDED
Binary file
Binary file
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'vorbis_comment'
4
+ require 'fileutils'
5
+ require 'test/unit'
6
+
7
+ begin
8
+ VorbisComment.new(File.join(File.dirname(__FILE__), 'lt8k.ogg')).fields
9
+ rescue VorbisComment::InvalidDataError
10
+ puts('The linked version of libvorbis has problems with files < 8K in size')
11
+ end
12
+
13
+ class VorbisCommentTest < Test::Unit::TestCase
14
+ def vc(filename)
15
+ VorbisComment.new(File.join(File.dirname(__FILE__), filename))
16
+ end
17
+
18
+ def work_with_copy(filename, &block)
19
+ new_name = File.join(File.dirname(__FILE__), "copy-#{filename}")
20
+ FileUtils.cp(File.join(File.dirname(__FILE__), filename), new_name)
21
+ begin
22
+ result = yield new_name
23
+ ensure
24
+ File.delete(new_name)
25
+ end
26
+ result
27
+ end
28
+
29
+ def test_initialize
30
+ assert_equal File.join(File.dirname(__FILE__), 'title.ogg'), vc('title.ogg').filename
31
+ # Nonexistent files don't raise an error until they are accessed for the fields
32
+ assert_equal 'nonexistant.ogg', VorbisComment.new('nonexistant.ogg').filename
33
+ # Corrupt files don't raise an error until they are parsed for the fields
34
+ assert_equal File.join(File.dirname(__FILE__), 'corrupt.ogg'), vc('corrupt.ogg').filename
35
+ end
36
+
37
+ def test_exists?
38
+ assert_equal true, vc('title.ogg').exists?
39
+ assert_equal true, vc('manyfields.ogg').exists?
40
+ # Blank tags still exist
41
+ assert_equal true, vc('blank.ogg').exists?
42
+ # Corrupt tags are considered not to exist
43
+ assert_equal false, vc('corrupt.ogg').exists?
44
+ # Files that aren't ogg files will be treated similarly
45
+ assert_equal false, vc('test_vorbis_comment.rb').exists?
46
+ # But nonexistant files raise an OpenError
47
+ assert_raises(VorbisComment::OpenError){vc('nonexistant.ogg').exists?}
48
+ end
49
+
50
+ def test_fields
51
+ assert_equal Hash[], vc('blank.ogg').fields
52
+ assert_equal Hash['title'=>['Silence']], vc('title.ogg').fields
53
+ assert_equal Hash[{"ARTIST"=>["Test Artist 1", "Test Artist 2"], "TrackNumber"=>["1"], "Album"=>["Test Album"], "Title"=>["Test Title"], "Date"=>["1999-12-31"], "comment"=>[""]}], vc('manyfields.ogg').fields
54
+ assert_raises(VorbisComment::InvalidDataError){vc('corrupt.ogg').fields}
55
+ assert_raises(VorbisComment::OpenError){vc('nonexistant.ogg').fields}
56
+ end
57
+
58
+ def test_remove!
59
+ assert_equal Hash[], work_with_copy('blank.ogg'){|filename| VorbisComment.new(filename).remove!}
60
+ assert_equal Hash[], work_with_copy('title.ogg'){|filename| VorbisComment.new(filename).remove!}
61
+ assert_equal Hash[], work_with_copy('manyfields.ogg'){|filename| VorbisComment.new(filename).remove!}
62
+ assert_raises(VorbisComment::InvalidDataError){work_with_copy('corrupt.ogg'){|filename| VorbisComment.new(filename).remove!}}
63
+ assert_raises(VorbisComment::OpenError){VorbisComment.new('nonexistant.ogg').remove!}
64
+ end
65
+
66
+ def test_pretty_print
67
+ assert_equal "", vc('blank.ogg').pretty_print
68
+ assert_equal "title: Silence", vc('title.ogg').pretty_print
69
+ assert_equal "ARTIST: Test Artist 1, Test Artist 2\nAlbum: Test Album\nDate: 1999-12-31\nTitle: Test Title\nTrackNumber: 1\ncomment: ", vc('manyfields.ogg').pretty_print
70
+ assert_equal "CORRUPT TAG!", vc('corrupt.ogg').pretty_print
71
+ assert_equal "CORRUPT TAG!", vc('test_vorbis_comment.rb').pretty_print
72
+ end
73
+
74
+ def test_update
75
+ f = {'Blah'=>['Blah']}
76
+ assert_raises(VorbisComment::InvalidDataError){work_with_copy('corrupt.ogg'){|filename| VorbisComment.new(filename).update{|fields| fields.merge!(f)}}}
77
+ assert_raises(VorbisComment::OpenError){VorbisComment.new('nonexistant.ogg').update{|fields| fields.merge!(f)}}
78
+
79
+ g = {'x'=>'y', 'y'=>:z, :z=>[:A], :zz=>[['A'], [[[:b]], 'c']]}
80
+ h = {'x'=>['y'], 'y'=>['z'], 'z'=>['A'], 'zz'=>['A', 'bc']}
81
+
82
+ %w'blank.ogg title.ogg manyfields.ogg'.each do |fname|
83
+ work_with_copy(fname) do |filename|
84
+ v = VorbisComment.new(filename)
85
+ fx = v.fields.merge(f)
86
+ assert_equal fx, v.update{|fields| fields.merge!(f)}
87
+ assert_equal fx, v.fields
88
+ v = VorbisComment.new(filename)
89
+ assert_equal fx, v.fields
90
+ gx = v.fields.merge(g)
91
+ assert_equal gx, v.update{|fields| fields.merge!(g)}
92
+ v = VorbisComment.new(filename)
93
+ hx = v.fields.merge(h)
94
+ assert_equal hx, VorbisComment.new(filename).fields
95
+ end
96
+ end
97
+ end
98
+
99
+ def test_bad_keys_and_values
100
+ work_with_copy('blank.ogg') do |filename|
101
+ v = VorbisComment.new(filename)
102
+ # Test for bad keys
103
+ (("\x00".."\x1f").to_a + ['='] + ("\x7e".."\xff").to_a).each do |c|
104
+ assert_raises(VorbisComment::InvalidCommentError){v.update{|fields| fields[c] = 'Blah'}}
105
+ end
106
+ # Test for bad vales (Invalid UTF8)
107
+ assert_raises(VorbisComment::InvalidCommentError){v.update{|fields| fields['Blah'] = "\x81\x81"}}
108
+ end
109
+ end
110
+
111
+ def test_add_to_fields_and_normalize_fields
112
+ work_with_copy('blank.ogg') do |filename|
113
+ v = VorbisComment.new(filename)
114
+ assert_equal Hash[], v.fields
115
+ assert_equal [], v.send(:normalize_fields)
116
+ v.send(:add_to_fields, 'A', 'b')
117
+ assert_equal CICPHash['A'=>['b']], v.fields
118
+ assert_equal [['A', 'b']], v.send(:normalize_fields)
119
+ v.send(:add_to_fields, :a, ['C'])
120
+ assert_equal CICPHash['A'=>['b',['C']]], v.fields
121
+ assert_equal [['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
122
+ v.send(:add_to_fields, 12, 42)
123
+ assert_equal CICPHash['A'=>['b',['C']], 12=>[42]], v.fields
124
+ assert_equal [['12', '42'], ['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
125
+ v.update{}
126
+ assert_equal CICPHash['A'=>['C','b'], '12'=>['42']], VorbisComment.new(filename).fields
127
+ assert_equal [['12', '42'], ['A', 'C'], ['A', 'b']], v.send(:normalize_fields)
128
+ end
129
+ end
130
+ end
data/test/title.ogg ADDED
Binary file
data/vcedit.c ADDED
@@ -0,0 +1,583 @@
1
+ /*
2
+ * Copyright (C) 2000-2001 Michael Smith (msmith at xiph org)
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 <stdio.h>
20
+ #include <stdbool.h>
21
+ #include <stdlib.h>
22
+ #include <string.h>
23
+ #include <errno.h>
24
+ #include <limits.h>
25
+ #include <unistd.h>
26
+ #include <sys/types.h>
27
+ #include <sys/stat.h>
28
+ #include <ogg/ogg.h>
29
+ #include <vorbis/codec.h>
30
+ #include <assert.h>
31
+
32
+ #include "vcedit.h"
33
+
34
+ #define CHUNKSIZE 4096
35
+
36
+ struct vcedit_state_St {
37
+ int refcount;
38
+
39
+ ogg_sync_state oy;
40
+ ogg_stream_state os;
41
+
42
+ vorbis_comment vc;
43
+ vorbis_info vi;
44
+
45
+ FILE *in;
46
+ mode_t file_mode;
47
+
48
+ bool opened;
49
+ long serial;
50
+
51
+ ogg_packet packet_main;
52
+ ogg_packet packet_code_books;
53
+
54
+ char *vendor;
55
+ int prevW;
56
+
57
+ bool extra_page;
58
+ bool eos;
59
+
60
+ char filename[0];
61
+ };
62
+
63
+ static void
64
+ ogg_packet_init (ogg_packet *p, unsigned char *buf, long len)
65
+ {
66
+ p->packet = buf;
67
+ p->bytes = len;
68
+
69
+ p->b_o_s = p->e_o_s = 0;
70
+ p->granulepos = p->packetno = 0;
71
+ }
72
+
73
+ static bool
74
+ ogg_packet_dup_data (ogg_packet *p)
75
+ {
76
+ unsigned char *tmp;
77
+
78
+ tmp = malloc (p->bytes);
79
+ if (!tmp)
80
+ return false;
81
+
82
+ memcpy (tmp, p->packet, p->bytes);
83
+ p->packet = tmp;
84
+
85
+ return true;
86
+ }
87
+
88
+ static void
89
+ vcedit_state_free (vcedit_state *s)
90
+ {
91
+ free (s->vendor);
92
+
93
+ if (s->in) {
94
+ fclose (s->in);
95
+ s->in = NULL;
96
+ }
97
+
98
+ free (s);
99
+ }
100
+
101
+ static bool
102
+ vcedit_state_init (vcedit_state *s, const char *filename)
103
+ {
104
+ s->refcount = 1;
105
+
106
+ ogg_packet_init (&s->packet_main, NULL, 0);
107
+ ogg_packet_init (&s->packet_code_books, NULL, 0);
108
+
109
+ strcpy (s->filename, filename);
110
+
111
+ return true;
112
+ }
113
+
114
+ vcedit_state *
115
+ vcedit_state_new (const char *filename)
116
+ {
117
+ vcedit_state *s;
118
+ size_t len;
119
+
120
+ len = strlen (filename);
121
+ if (len > PATH_MAX)
122
+ return NULL;
123
+
124
+ s = malloc (sizeof (vcedit_state) + len + 1);
125
+ if (!s)
126
+ return NULL;
127
+
128
+ memset (s, 0, sizeof (vcedit_state));
129
+
130
+ if (!vcedit_state_init (s, filename)) {
131
+ vcedit_state_free (s);
132
+ return NULL;
133
+ }
134
+
135
+ return s;
136
+ }
137
+
138
+ vorbis_comment *
139
+ vcedit_comments (vcedit_state *s)
140
+ {
141
+ return s->opened ? &s->vc : NULL;
142
+ }
143
+
144
+ static void
145
+ vcedit_clear_internals (vcedit_state *s)
146
+ {
147
+ ogg_stream_clear (&s->os);
148
+ ogg_sync_clear (&s->oy);
149
+
150
+ vorbis_info_clear (&s->vi);
151
+ vorbis_comment_clear (&s->vc);
152
+
153
+ free (s->vendor);
154
+ s->vendor = NULL;
155
+
156
+ ogg_packet_clear (&s->packet_main);
157
+ ogg_packet_clear (&s->packet_code_books);
158
+
159
+ s->serial = 0;
160
+ s->opened = false;
161
+ }
162
+
163
+ void
164
+ vcedit_state_ref (vcedit_state *s)
165
+ {
166
+ s->refcount++;
167
+ }
168
+
169
+ void
170
+ vcedit_state_unref (vcedit_state *s)
171
+ {
172
+ if (--s->refcount)
173
+ return;
174
+
175
+ if (s->opened)
176
+ vcedit_clear_internals (s);
177
+
178
+ vcedit_state_free (s);
179
+ }
180
+
181
+ /* Next two functions pulled straight from libvorbis, apart from one change
182
+ * - we don't want to overwrite the vendor string.
183
+ */
184
+ static void
185
+ _v_writestring (oggpack_buffer *o, const char *s, int len)
186
+ {
187
+ while (len--)
188
+ oggpack_write (o, *s++, 8);
189
+ }
190
+
191
+ static bool
192
+ write_comments (vcedit_state *s, ogg_packet *packet)
193
+ {
194
+ oggpack_buffer opb;
195
+ size_t len;
196
+ int i;
197
+
198
+ ogg_packet_init (packet, NULL, 0);
199
+
200
+ oggpack_writeinit (&opb);
201
+
202
+ /* preamble */
203
+ oggpack_write (&opb, 0x03, 8);
204
+ _v_writestring (&opb, "vorbis", 6);
205
+
206
+ /* vendor */
207
+ len = strlen (s->vendor);
208
+ oggpack_write (&opb, len, 32);
209
+ _v_writestring (&opb, s->vendor, len);
210
+
211
+ /* comments */
212
+ oggpack_write (&opb, s->vc.comments, 32);
213
+
214
+ for (i = 0; i < s->vc.comments; i++)
215
+ if (!s->vc.user_comments[i])
216
+ oggpack_write (&opb, 0, 32);
217
+ else {
218
+ oggpack_write (&opb, s->vc.comment_lengths[i], 32);
219
+ _v_writestring (&opb, s->vc.user_comments[i],
220
+ s->vc.comment_lengths[i]);
221
+ }
222
+
223
+ oggpack_write (&opb, 1, 1);
224
+
225
+ packet->bytes = oggpack_bytes (&opb);
226
+
227
+ packet->packet = _ogg_malloc (packet->bytes);
228
+ if (!packet->packet)
229
+ return false;
230
+
231
+ memcpy (packet->packet, opb.buffer, packet->bytes);
232
+
233
+ oggpack_writeclear (&opb);
234
+
235
+ return true;
236
+ }
237
+
238
+ static int
239
+ _blocksize (vcedit_state *s, ogg_packet *p)
240
+ {
241
+ int this, ret = 0;
242
+
243
+ this = vorbis_packet_blocksize (&s->vi, p);
244
+
245
+ if (s->prevW)
246
+ ret = (this + s->prevW) / 4;
247
+
248
+ s->prevW = this;
249
+
250
+ return ret;
251
+ }
252
+
253
+ static int
254
+ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
255
+ {
256
+ char *buffer;
257
+ int result, bytes;
258
+
259
+ result = ogg_stream_packetout (&s->os, p);
260
+ if (result == 1)
261
+ return 1;
262
+
263
+ if (s->eos)
264
+ return 0;
265
+
266
+ while (ogg_sync_pageout (&s->oy, page) != 1) {
267
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
268
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
269
+ ogg_sync_wrote (&s->oy, bytes);
270
+
271
+ if (!bytes && (feof (s->in) || ferror (s->in)))
272
+ return 0;
273
+ }
274
+
275
+ if (ogg_page_eos (page))
276
+ s->eos = true;
277
+ else if (ogg_page_serialno (page) != s->serial) {
278
+ s->eos = true;
279
+ s->extra_page = true;
280
+
281
+ return 0;
282
+ }
283
+
284
+ ogg_stream_pagein (&s->os, page);
285
+
286
+ return _fetch_next_packet (s, p, page);
287
+ }
288
+
289
+ vcedit_error
290
+ vcedit_open (vcedit_state *s)
291
+ {
292
+ vcedit_error ret;
293
+ ogg_packet packet_comments, *header = &packet_comments;
294
+ ogg_page page;
295
+ struct stat st;
296
+ char *buffer;
297
+ size_t bytes, total = 0;
298
+ int i = 0;
299
+
300
+ s->in = fopen (s->filename, "rb");
301
+ if (!s->in)
302
+ return VCEDIT_ERR_OPEN;
303
+
304
+ s->file_mode = stat (s->filename, &st) ? 0664 : st.st_mode;
305
+
306
+ ogg_sync_init (&s->oy);
307
+
308
+ do {
309
+ /* Bail if we don't find data in the first 40 kB */
310
+ if (feof (s->in) || ferror (s->in) || total >= (CHUNKSIZE * 10)) {
311
+ ogg_sync_clear (&s->oy);
312
+
313
+ return VCEDIT_ERR_INVAL;
314
+ }
315
+
316
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
317
+
318
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
319
+ total += bytes;
320
+
321
+ ogg_sync_wrote (&s->oy, bytes);
322
+ } while (ogg_sync_pageout (&s->oy, &page) != 1);
323
+
324
+ s->serial = ogg_page_serialno (&page);
325
+
326
+ ogg_stream_init (&s->os, s->serial);
327
+ vorbis_info_init (&s->vi);
328
+ vorbis_comment_init (&s->vc);
329
+
330
+ if (ogg_stream_pagein (&s->os, &page) < 0) {
331
+ ret = VCEDIT_ERR_INVAL;
332
+ goto err;
333
+ }
334
+
335
+ if (ogg_stream_packetout (&s->os, &s->packet_main) != 1) {
336
+ ret = VCEDIT_ERR_INVAL;
337
+ goto err;
338
+ }
339
+
340
+ if (!ogg_packet_dup_data (&s->packet_main)) {
341
+ s->packet_main.packet = NULL;
342
+ ret = VCEDIT_ERR_INVAL;
343
+ goto err;
344
+ }
345
+
346
+ if (vorbis_synthesis_headerin (&s->vi, &s->vc, &s->packet_main) < 0) {
347
+ ret = VCEDIT_ERR_INVAL;
348
+ goto err;
349
+ }
350
+
351
+ ogg_packet_init (&packet_comments, NULL, 0);
352
+
353
+ while (i < 2) {
354
+ if (feof (s->in) || ferror (s->in)) {
355
+ ret = VCEDIT_ERR_INVAL;
356
+ goto err;
357
+ }
358
+
359
+ while (i < 2) {
360
+ int result;
361
+
362
+ result = ogg_sync_pageout (&s->oy, &page);
363
+ if (!result)
364
+ break; /* Too little data so far */
365
+
366
+ if (result != 1)
367
+ continue;
368
+
369
+ ogg_stream_pagein (&s->os, &page);
370
+
371
+ while (i < 2) {
372
+ result = ogg_stream_packetout (&s->os, header);
373
+ if (!result)
374
+ break;
375
+
376
+ if (result != 1) {
377
+ ret = VCEDIT_ERR_INVAL;
378
+ goto err;
379
+ }
380
+
381
+ if (i++ == 1 && !ogg_packet_dup_data (header)) {
382
+ header->packet = NULL;
383
+ ret = VCEDIT_ERR_INVAL;
384
+ goto err;
385
+ }
386
+
387
+ vorbis_synthesis_headerin (&s->vi, &s->vc, header);
388
+
389
+ header = &s->packet_code_books;
390
+ }
391
+ }
392
+
393
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
394
+
395
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
396
+ ogg_sync_wrote (&s->oy, bytes);
397
+ }
398
+
399
+ /* Copy the vendor tag */
400
+ s->vendor = strdup (s->vc.vendor);
401
+
402
+ /* Headers are done! */
403
+ s->opened = true;
404
+
405
+ return VCEDIT_ERR_SUCCESS;
406
+
407
+ err:
408
+ vcedit_clear_internals (s);
409
+
410
+ return ret;
411
+ }
412
+
413
+ static bool
414
+ write_data (const void *buf, size_t size, size_t nmemb, FILE *stream)
415
+ {
416
+ while (nmemb > 0) {
417
+ size_t w;
418
+
419
+ w = fwrite (buf, size, nmemb, stream);
420
+ if (!w && ferror (stream))
421
+ return false;
422
+
423
+ nmemb -= w;
424
+ buf += size * w;
425
+ }
426
+
427
+ return true;
428
+ }
429
+
430
+ static bool
431
+ write_page (FILE *f, ogg_page *p)
432
+ {
433
+ return write_data (p->header, 1, p->header_len, f) &&
434
+ write_data (p->body, 1, p->body_len, f);
435
+ }
436
+
437
+ vcedit_error
438
+ vcedit_write (vcedit_state *s)
439
+ {
440
+ ogg_stream_state stream;
441
+ ogg_packet packet;
442
+ ogg_page page_out, page_in;
443
+ ogg_int64_t granpos = 0;
444
+ FILE *out;
445
+ char *buffer, tmpfile[PATH_MAX];
446
+ bool success = false, need_flush = false, need_out = false;
447
+ int fd, result, bytes;
448
+
449
+ if (!s->opened)
450
+ return VCEDIT_ERR_INVAL;
451
+
452
+ strcpy (tmpfile, s->filename);
453
+ strcat (tmpfile, ".XXXXXX");
454
+
455
+ fd = mkstemp (tmpfile);
456
+ if (fd == -1)
457
+ return VCEDIT_ERR_TMPFILE;
458
+
459
+ out = fdopen (fd, "wb");
460
+ if (!out) {
461
+ unlink (tmpfile);
462
+ close (fd);
463
+
464
+ return VCEDIT_ERR_TMPFILE;
465
+ }
466
+
467
+ s->prevW = 0;
468
+ s->extra_page = s->eos = false;
469
+
470
+ ogg_stream_init (&stream, s->serial);
471
+
472
+ /* write "main" packet */
473
+ s->packet_main.b_o_s = 1;
474
+ ogg_stream_packetin (&stream, &s->packet_main);
475
+ s->packet_main.b_o_s = 0;
476
+
477
+ /* prepare and write comments */
478
+ if (!write_comments (s, &packet)) {
479
+ ogg_stream_clear (&stream);
480
+ unlink (tmpfile);
481
+ fclose (out);
482
+
483
+ return VCEDIT_ERR_INVAL;
484
+ }
485
+
486
+ ogg_stream_packetin (&stream, &packet);
487
+ ogg_packet_clear (&packet);
488
+
489
+ /* write codebooks */
490
+ ogg_stream_packetin (&stream, &s->packet_code_books);
491
+
492
+ while (ogg_stream_flush (&stream, &page_out))
493
+ if (!write_page (out, &page_out))
494
+ goto cleanup;
495
+
496
+ while (_fetch_next_packet (s, &packet, &page_in)) {
497
+ bool write = false;
498
+ int size;
499
+
500
+ size = _blocksize (s, &packet);
501
+ granpos += size;
502
+
503
+ if (need_flush)
504
+ write = ogg_stream_flush (&stream, &page_out);
505
+ else if (need_out)
506
+ write = ogg_stream_pageout (&stream, &page_out);
507
+
508
+ if (write && !write_page (out, &page_out))
509
+ goto cleanup;
510
+
511
+ need_flush = need_out = false;
512
+
513
+ if (packet.granulepos == -1) {
514
+ packet.granulepos = granpos;
515
+ ogg_stream_packetin (&stream, &packet);
516
+ } else {
517
+ /* granulepos is set, validly. Use it, and force a flush to
518
+ * account for shortened blocks (vcut) when appropriate
519
+ */
520
+ if (granpos > packet.granulepos) {
521
+ granpos = packet.granulepos;
522
+ ogg_stream_packetin (&stream, &packet);
523
+ need_flush = true;
524
+ } else {
525
+ ogg_stream_packetin (&stream, &packet);
526
+ need_out = true;
527
+ }
528
+ }
529
+ }
530
+
531
+ stream.e_o_s = 1;
532
+
533
+ while (ogg_stream_flush (&stream, &page_out))
534
+ if (!write_page (out, &page_out))
535
+ goto cleanup;
536
+
537
+ if (s->extra_page && !write_page (out, &page_in))
538
+ goto cleanup;
539
+
540
+ /* clear it, because not all paths to here do */
541
+ s->eos = false;
542
+
543
+ do {
544
+ /* We copy the rest of the stream (other logical streams)
545
+ * through, a page at a time.
546
+ */
547
+ while ((result = ogg_sync_pageout (&s->oy, &page_out)))
548
+ if (result == 1 && !write_page (out, &page_out))
549
+ goto cleanup;
550
+
551
+ buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
552
+ bytes = fread (buffer, 1, CHUNKSIZE, s->in);
553
+ ogg_sync_wrote (&s->oy, bytes);
554
+
555
+ if (ferror (s->in))
556
+ goto cleanup;
557
+ } while (bytes || !feof (s->in));
558
+
559
+ s->eos = success = true;
560
+
561
+ cleanup:
562
+ fclose (s->in);
563
+
564
+ if (!success) {
565
+ unlink (tmpfile);
566
+ fclose (out);
567
+ } else {
568
+ fclose (out);
569
+ unlink (s->filename);
570
+ rename (tmpfile, s->filename);
571
+ chmod (s->filename, s->file_mode);
572
+ }
573
+
574
+ ogg_stream_clear (&stream);
575
+
576
+ if (!s->eos)
577
+ return VCEDIT_ERR_INVAL;
578
+
579
+ vcedit_clear_internals (s);
580
+
581
+ return (vcedit_open (s) == VCEDIT_ERR_SUCCESS) ?
582
+ VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN;
583
+ }