1 /* This program is licensed under the GNU Library General Public License, version 2,
2  * a copy of which is included with this program (LICENCE.LGPL).
3  *
4  * (c) 2000-2001 Michael Smith <msmith@xiph.org>
5  *
6  *
7  * Comment editing backend, suitable for use by nice frontend interfaces.
8  *
9  * last modified: $Id$
10  */
11 
12 /* Handle muxed streams and the Vorbis renormalization without having
13  * to understand remuxing:
14  * Linked list of buffers (buffer_chain).  Start a link and whenever
15  * you encounter an unknown page from the current stream (ie we found
16  * its bos in the bos section) push it onto the current buffer.  Whenever
17  * you encounter the stream being renormalized create a new link in the
18  * chain.
19  * On writing, write the contents of the first link before every Vorbis
20  * page written, and move to the next link.  Assuming the Vorbis pages
21  * in match vorbis pages out, the order of pages from different logical
22  * streams will be unchanged.
23  * Special case: header. After writing the vorbis headers, and before
24  * starting renormalization, flush accumulated links (takes care of
25  * situations where number of secondary vorbis header pages changes due
26  * to remuxing.  Similarly flush links at the end of renormalization
27  * and before the start of the next chain is written.
28  *
29  */
30 
31 /* This file is taken from vorbis-tools SVN
32  * (http://svn.xiph.org/trunk/vorbis-tools/vorbiscomment) Rev. 16716 2009-11-22,
33  * modifications are marked with kid3. */
34 /* kid3 */
35 #include "oggflacconfig.h"
36 #ifdef HAVE_VORBIS
37 
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <ogg/ogg.h>
47 #include <vorbis/codec.h>
48 
49 #include "vcedit.h"
50 /* kid3 */
51 /*#include "vceditaux.h"*/
52 typedef struct vcedit_page_buffer {
53 	char *data;
54 	size_t data_len;
55 } vcedit_page_buffer;
56 
57 typedef struct vcedit_buffer_chain {
58 	struct vcedit_buffer_chain *next;
59 	struct vcedit_page_buffer buffer;
60 } vcedit_buffer_chain;
61 /* kid3 */
62 /*#include "i18n.h"*/
63 #define _(str) ((char*)(str))
64 
65 
66 #define CHUNKSIZE 4096
67 #define BUFFERCHUNK CHUNKSIZE
68 
69 /* Helper function, shouldn't need to call directly */
page_buffer_push(vcedit_buffer_chain * bufferlink,ogg_page * og)70 static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) {
71 	int result=0;
72 	char *tmp;
73 	vcedit_page_buffer *buffer;
74 
75 	buffer = &bufferlink->buffer;
76 	tmp = realloc(buffer->data,
77 		      buffer->data_len + og->header_len + og->body_len);
78 	if(tmp) {
79 		buffer->data = tmp;
80 		memcpy(buffer->data + buffer->data_len, og->header,
81 		       og->header_len);
82 		buffer->data_len += og->header_len;
83 		memcpy(buffer->data + buffer->data_len, og->body,
84 	       og->body_len);
85 		result = 1;
86 		buffer->data_len += og->body_len;
87 	} else {
88 		result = -1;
89 	}
90 
91 	return result;
92 }
93 
94 /* Write and free the first link using callbacks */
buffer_chain_writelink(vcedit_state * state,void * out)95 static int buffer_chain_writelink(vcedit_state *state, void *out) {
96 	int result = 0;
97 	vcedit_buffer_chain *tmpchain;
98 	vcedit_page_buffer *tmpbuffer;
99 
100 	tmpchain = state->sidebuf;
101 	tmpbuffer = &tmpchain->buffer;
102 	if(tmpbuffer->data_len)
103 	{
104 		if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) !=
105 		   (size_t) tmpbuffer->data_len)
106 			result = -1;
107 		else
108 			result = 1;
109 	}
110 
111 	free(tmpbuffer->data);
112 	state->sidebuf = tmpchain->next;
113 	free(tmpchain);
114 	return result;
115 }
116 
117 
buffer_chain_newlink(vcedit_state * state)118 static int buffer_chain_newlink(vcedit_state *state) {
119 	int result = 1;
120 	vcedit_buffer_chain *bufferlink;
121 
122 	if(!state->sidebuf) {
123 		state->sidebuf = malloc (sizeof *state->sidebuf);
124 		if(state->sidebuf) {
125 			bufferlink = state->sidebuf;
126 		} else {
127 			result = -1;
128 		}
129 	} else {
130 		bufferlink=state->sidebuf;
131 		while(bufferlink->next) {
132 			bufferlink = bufferlink->next;
133 		}
134 		bufferlink->next =  malloc (sizeof *bufferlink->next);
135 		if(bufferlink->next) {
136 			bufferlink = bufferlink->next;
137 		} else {
138 			result = -1;
139 		}
140 	}
141 
142 	if(result > 0 ) {
143 		bufferlink->next = 0;
144 		bufferlink->buffer.data = 0;
145 		bufferlink->buffer.data_len = 0;
146 	}
147 	else
148 		state->lasterror =
149 			_("Couldn't get enough memory for input buffering.");
150 
151   return result;
152 }
153 
154 
155 /* Push page onto the end of the buffer chain */
buffer_chain_push(vcedit_state * state,ogg_page * og)156 static int buffer_chain_push(vcedit_state *state, ogg_page *og) {
157 	/* If there is no sidebuffer yet we need to create one, otherwise
158 	 * traverse to the last buffer and push the new page onto it. */
159 	int result=1;
160 	vcedit_buffer_chain *bufferlink;
161 	if(!state->sidebuf) {
162 		result = buffer_chain_newlink(state);
163 	}
164 
165 	if(result > 0) {
166 		bufferlink = state->sidebuf;
167 		while(bufferlink->next) {
168 			bufferlink = bufferlink->next;
169 	}
170 		result = page_buffer_push(bufferlink, og);
171 	}
172 
173 	if(result < 0)
174 		state->lasterror =
175 			_("Couldn't get enough memory for input buffering.");
176 
177 	return result;
178 }
179 
180 
181 
vcedit_supported_stream(vcedit_state * state,ogg_page * og)182 static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) {
183 	ogg_stream_state os;
184 	vorbis_info vi;
185 	vorbis_comment vc;
186 	ogg_packet header;
187 	int result = 0;
188 
189 	ogg_stream_init(&os, ogg_page_serialno(og));
190 	vorbis_info_init(&vi);
191 	vorbis_comment_init(&vc);
192 
193 	if( !ogg_page_bos(og) )
194                 result = -1;
195 
196 	if(result >= 0 && ogg_stream_pagein(&os, og) < 0)
197 	{
198 		state->lasterror =
199 			_("Error reading first page of Ogg bitstream.");
200 		result = -1;
201 	}
202 
203 	if(result >= 0 && ogg_stream_packetout(&os, &header) != 1)
204 	{
205 		state->lasterror = _("Error reading initial header packet.");
206 		result = -1;
207 	}
208 
209 	if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0)
210 	{
211                 result = 1;
212 	} else {
213 		/* Not vorbis, may eventually become a chain of checks (Speex,
214 		 * Theora), but for the moment return 0, bos scan will push
215                  * the current page onto the buffer.
216 		 */
217 	}
218 
219 	ogg_stream_clear(&os);
220 	vorbis_info_clear(&vi);
221 	vorbis_comment_clear(&vc);
222         return result;
223 }
224 
225 
vcedit_contains_serial(vcedit_state * state,int serialno)226 static int vcedit_contains_serial (vcedit_state *state, int serialno) {
227 	int result = 0;
228 	size_t count;
229 	for( count=0; count < state->serials.streams_len; count++ ) {
230 		if ( *(state->serials.streams + count ) == serialno )
231 			result = 1;
232 	}
233 
234 	return result;
235 }
236 
237 
vcedit_add_serial(vcedit_state * state,long serial)238 static int vcedit_add_serial (vcedit_state *state, long serial) {
239         int result = 0;
240         long *tmp;
241 
242 
243 	if( vcedit_contains_serial(state, serial) )
244         {
245 		result = 1;
246 	} else {
247 		tmp   = realloc(state->serials.streams,
248 				(state->serials.streams_len + 1) * sizeof *tmp);
249 		if(tmp) {
250 			state->serials.streams = tmp;
251 			*(state->serials.streams +
252 			  state->serials.streams_len) = serial;
253 			state->serials.streams_len += 1;
254 			result = 1;
255 		} else {
256 			state->lasterror =
257 				_("Couldn't get enough memory to register new stream serial number.");
258 		        result = -1;
259 		}
260 	}
261 	return result;
262 }
263 
264 
265 /* For the benefit of the secondary header read only.  Quietly creates
266  * newlinks and pushes pages onto the buffer in the right way */
vcedit_target_pageout(vcedit_state * state,ogg_page * og)267 static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) {
268 	int result = 0;
269 	int pageout_result;
270 	pageout_result = ogg_sync_pageout(state->oy, og);
271 	if(pageout_result > 0)
272 	{
273 		if(state->serial == ogg_page_serialno(og))
274 			result = buffer_chain_newlink(state);
275 		else
276 			result = buffer_chain_push(state, og);
277 	} else if (pageout_result < 0) {
278 		/* Vorbis comment traditionally ignores the not-synced
279 		 * error from pageout, so give it a different code. */
280 		result = -2;
281 	}
282 	return result;
283 }
284 
285 
286 /* (I'm paranoid about memset(x,0,len) not giving null pointers */
vcedit_new_state(void)287 vcedit_state *vcedit_new_state(void) {
288 	vcedit_state *state = malloc(sizeof(vcedit_state));
289 	if(state) {
290 		memset(state, 0, sizeof(vcedit_state));
291 		state->sidebuf = 0;
292 		state->serials.streams = 0;
293 		state->serials.streams_len = 0;
294 	}
295 	return state;
296 }
297 
vcedit_error(vcedit_state * state)298 char *vcedit_error(vcedit_state *state) {
299 	return state->lasterror;
300 }
301 
vcedit_comments(vcedit_state * state)302 vorbis_comment *vcedit_comments(vcedit_state *state) {
303 	return state->vc;
304 }
305 
vcedit_clear_internals(vcedit_state * state)306 static void vcedit_clear_internals(vcedit_state *state) {
307     char *tmp;
308 	if(state->vc) {
309 		vorbis_comment_clear(state->vc);
310 		free(state->vc);
311 	}
312 	if(state->os) {
313 		ogg_stream_clear(state->os);
314 		free(state->os);
315 	}
316 	if(state->oy) {
317 		ogg_sync_clear(state->oy);
318 		free(state->oy);
319 	}
320 	if(state->serials.streams_len) {
321 		free(state->serials.streams);
322 		state->serials.streams_len = 0;
323 		state->serials.streams = 0;
324 	}
325 	while(state->sidebuf) {
326 		vcedit_buffer_chain *tmpbuffer;
327 		tmpbuffer = state->sidebuf;
328 		state->sidebuf = tmpbuffer->next;
329 		free(tmpbuffer->buffer.data);
330 		free(tmpbuffer);
331 	}
332 	if(state->vendor)
333 		free(state->vendor);
334     if(state->mainbuf)
335         free(state->mainbuf);
336     if(state->bookbuf)
337         free(state->bookbuf);
338     if(state->vi) {
339        	vorbis_info_clear(state->vi);
340         free(state->vi);
341     }
342 
343     tmp = state->lasterror;
344     memset(state, 0, sizeof(*state));
345     state->lasterror = tmp;
346 }
347 
vcedit_clear(vcedit_state * state)348 void vcedit_clear(vcedit_state *state)
349 {
350 	if(state)
351 	{
352 		vcedit_clear_internals(state);
353 		free(state);
354 	}
355 }
356 
357 /* Next two functions pulled straight from libvorbis, apart from one change
358  * - we don't want to overwrite the vendor string.
359  */
360 /* kid3: changed type from char* to const char* */
_v_writestring(oggpack_buffer * o,const char * s,int len)361 static void _v_writestring(oggpack_buffer *o, const char *s, int len)
362 {
363 	while(len--)
364 	{
365 		oggpack_write(o,*s++,8);
366 	}
367 }
368 
_commentheader_out(vorbis_comment * vc,char * vendor,ogg_packet * op)369 static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
370 {
371 	oggpack_buffer opb;
372 
373 	oggpack_writeinit(&opb);
374 
375 	/* preamble */
376 	oggpack_write(&opb,0x03,8);
377 	_v_writestring(&opb,"vorbis", 6);
378 
379 	/* vendor */
380 	oggpack_write(&opb,strlen(vendor),32);
381 	_v_writestring(&opb,vendor, strlen(vendor));
382 
383 	/* comments */
384 	oggpack_write(&opb,vc->comments,32);
385 	if(vc->comments){
386 		int i;
387 		for(i=0;i<vc->comments;i++){
388 			if(vc->user_comments[i]){
389 				oggpack_write(&opb,vc->comment_lengths[i],32);
390 				_v_writestring(&opb,vc->user_comments[i],
391                         vc->comment_lengths[i]);
392 			}else{
393 				oggpack_write(&opb,0,32);
394 			}
395 		}
396 	}
397 	oggpack_write(&opb,1,1);
398 
399 	op->packet = malloc(oggpack_bytes(&opb));
400 	memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
401 
402 	op->bytes=oggpack_bytes(&opb);
403 	op->b_o_s=0;
404 	op->e_o_s=0;
405 	op->granulepos=0;
406 
407 	oggpack_writeclear(&opb);
408 	return 0;
409 }
410 
_blocksize(vcedit_state * s,ogg_packet * p)411 static int _blocksize(vcedit_state *s, ogg_packet *p)
412 {
413 	int this = vorbis_packet_blocksize(s->vi, p);
414 	int ret = (this + s->prevW)/4;
415 
416 	if(!s->prevW)
417 	{
418 		s->prevW = this;
419 		return 0;
420 	}
421 
422 	s->prevW = this;
423 	return ret;
424 }
425 
_fetch_next_packet(vcedit_state * s,ogg_packet * p,ogg_page * page)426 static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
427 {
428 	int result;
429 	char *buffer;
430 	int bytes;
431 	int serialno;
432 
433 	result = ogg_stream_packetout(s->os, p);
434 
435 	if(result > 0)
436 		return 1;
437 	else {
438 		while(1) {
439 			if(s->eosin)
440 				return 0;
441 
442 			while(ogg_sync_pageout(s->oy, page) <= 0)
443 			{
444 				buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
445 				bytes = s->read(buffer,1, CHUNKSIZE, s->in);
446 				ogg_sync_wrote(s->oy, bytes);
447 				if(bytes == 0)
448 				return 0;
449 			}
450 
451 			serialno = ogg_page_serialno(page);
452 			if(ogg_page_serialno(page) != s->serial)
453 			{
454 				if(vcedit_contains_serial(s, serialno)) {
455 					result = buffer_chain_push(s, page);
456 					if(result < 0)
457 						return result;
458 				}
459 				else
460 				{
461 					s->eosin = 1;
462 					s->extrapage = 1;
463 					return 0;
464 				}
465 			}
466 			else
467 			{
468 			  ogg_stream_pagein(s->os, page);
469 			  result = buffer_chain_newlink(s);
470 			  if (result < 0)
471 				  return result;
472 
473 			  if(ogg_page_eos(page))
474 				s->eosin = 1;
475 			}
476 			result = ogg_stream_packetout(s->os, p);
477 			if(result > 0)
478 				return 1;
479 		}
480 		/* Here == trouble */
481 		return 0;
482 	}
483 }
484 
485 /* kid3 vcedit_open() removed */
486 
vcedit_open_callbacks(vcedit_state * state,void * in,vcedit_read_func read_func,vcedit_write_func write_func)487 int vcedit_open_callbacks(vcedit_state *state, void *in,
488 		vcedit_read_func read_func, vcedit_write_func write_func)
489 {
490 
491 	char *buffer;
492 	int bytes,i;
493 	int chunks = 0;
494 	int read_bos, test_supported, page_pending;
495 	int have_vorbis;
496 	size_t vendor_size;
497 	ogg_packet *header;
498 	ogg_packet	header_main;
499 	ogg_packet  header_comments;
500 	ogg_packet	header_codebooks;
501 	ogg_page    og;
502 
503 	/* kid3, to fix LLVM scan-build error "Assigned value is garbage or undefined"
504 	   in line "state->mainlen = header_main.bytes;" */
505 	memset(&header_main, 0, sizeof(header_main));
506 
507 	state->in = in;
508 	state->read = read_func;
509 	state->write = write_func;
510 
511 	state->oy = malloc(sizeof(ogg_sync_state));
512 	ogg_sync_init(state->oy);
513 
514     while(1)
515     {
516     	buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
517 	    bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
518 
519     	ogg_sync_wrote(state->oy, bytes);
520 
521         if(ogg_sync_pageout(state->oy, &og) == 1)
522             break;
523 
524         if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
525         {
526 		    if(bytes<CHUNKSIZE)
527 			    state->lasterror = _("Input truncated or empty.");
528     		else
529 	    		state->lasterror = _("Input is not an Ogg bitstream.");
530 	    goto err;
531 	}
532     }
533 
534     /* BOS loop, starting with a loaded ogg page. */
535     if(buffer_chain_newlink(state) < 0)
536 	    goto err;
537 
538     for( read_bos = 1, have_vorbis = 0 ; read_bos; )
539     {
540 	    test_supported = vcedit_supported_stream(state, &og);
541 	    if(test_supported < 0)
542 	    {
543 		    goto err;
544 	    }
545 	    else if (test_supported == 0 || have_vorbis )
546 	    {
547 		    if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
548 			    goto err;
549 		    if( buffer_chain_push(state, &og) < 0)
550 			    goto err;
551 	    }
552 	    else if (test_supported > 0)
553 	    {
554 		    if(buffer_chain_newlink(state) < 0)
555 			    goto err;
556 		    state->serial = ogg_page_serialno(&og);
557 		    if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
558 		       goto err;
559 
560 		    state->os = malloc(sizeof(ogg_stream_state));
561 		    ogg_stream_init(state->os, state->serial);
562 
563 		    state->vi = malloc(sizeof(vorbis_info));
564 		    vorbis_info_init(state->vi);
565 
566 		    state->vc = malloc(sizeof(vorbis_comment));
567 		    vorbis_comment_init(state->vc);
568 
569 		    if(ogg_stream_pagein(state->os, &og) < 0)
570 		    {
571 			    state->lasterror =
572 				    _("Error reading first page of Ogg bitstream.");
573 			    goto err;
574 		    }
575 
576 		    if(ogg_stream_packetout(state->os, &header_main) != 1)
577 		    {
578 			    state->lasterror =
579 				    _("Error reading initial header packet.");
580 			    goto err;
581 		    }
582 
583 		    if(vorbis_synthesis_headerin(state->vi, state->vc,
584 						 &header_main) < 0)
585 		    {
586 			    state->lasterror =
587 				    _("Ogg bitstream does not contain Vorbis data.");
588 			    goto err;
589 		    }
590 		    have_vorbis = 1;
591 	    }
592 	    while(1)
593 	    {
594 		    buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
595 		    bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
596 
597 		    if(bytes == 0)
598 		    {
599 			    state->lasterror =
600 				    _("EOF before recognised stream.");
601 			    goto err;
602 		    }
603 
604 		    ogg_sync_wrote(state->oy, bytes);
605 
606 		    if(ogg_sync_pageout(state->oy, &og) == 1)
607 			    break;
608 	    }
609 	    if(!ogg_page_bos(&og)) {
610 		    read_bos = 0;
611 		    page_pending = 1;
612 	    }
613     }
614 
615         if(!state->os) {
616 		state->lasterror = _("Ogg bitstream does not contain a supported data-type.");
617 		goto err;
618 	}
619 
620 	state->mainlen = header_main.bytes;
621 	state->mainbuf = malloc(state->mainlen);
622 	memcpy(state->mainbuf, header_main.packet, header_main.bytes);
623 
624 	if(ogg_page_serialno(&og) == state->serial)
625 	{
626 		if(buffer_chain_newlink(state) < 0)
627 			goto err;
628 	}
629 
630 	else
631 	{
632 		if(buffer_chain_push(state, &og) < 0)
633 			goto err;
634 		page_pending = 0;
635 	}
636 
637 	i = 0;
638 	header = &header_comments;
639 	while(i<2) {
640 		while(i<2) {
641 			int result;
642 			if(!page_pending)
643 				result = vcedit_target_pageout(state, &og);
644 			else
645 			{
646 				result = 1;
647 				page_pending = 0;
648 			}
649 			if(result == 0 || result == -2) break; /* Too little data so far */
650 			else if(result == -1) goto err;
651 			else if(result == 1)
652 			{
653 				ogg_stream_pagein(state->os, &og);
654 				while(i<2)
655 				{
656 					result = ogg_stream_packetout(state->os, header);
657 					if(result == 0) break;
658 					if(result == -1)
659 					{
660 						state->lasterror = _("Corrupt secondary header.");
661 						goto err;
662 					}
663 					vorbis_synthesis_headerin(state->vi, state->vc, header);
664 					if(i==1)
665 					{
666 						state->booklen = header->bytes;
667 						state->bookbuf = malloc(state->booklen);
668 						memcpy(state->bookbuf, header->packet,
669 								header->bytes);
670 					}
671 					i++;
672 					header = &header_codebooks;
673 				}
674 			}
675 		}
676 
677 		buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
678 		bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
679 		if(bytes == 0 && i < 2)
680 		{
681 			state->lasterror = _("EOF before end of Vorbis headers.");
682 			goto err;
683 		}
684 		ogg_sync_wrote(state->oy, bytes);
685 	}
686 
687 	/* Copy the vendor tag */
688 	/* kid3 */
689 	vendor_size = strlen(state->vc->vendor) +1;
690 	state->vendor = malloc(vendor_size);
691 	memcpy(state->vendor, state->vc->vendor, vendor_size);
692 
693 	/* Headers are done! */
694 	return 0;
695 
696 err:
697 	vcedit_clear_internals(state);
698 	return -1;
699 }
700 
vcedit_write(vcedit_state * state,void * out)701 int vcedit_write(vcedit_state *state, void *out)
702 {
703 	ogg_stream_state streamout;
704 	ogg_packet header_main;
705 	ogg_packet header_comments;
706 	ogg_packet header_codebooks;
707 
708 	ogg_page ogout, ogin;
709 	ogg_packet op;
710 	ogg_int64_t granpos = 0;
711 	int result;
712 	char *buffer;
713 	int bytes;
714 	int needflush=0, needout=0;
715 
716 	state->eosin = 0;
717 	state->extrapage = 0;
718 
719 	header_main.bytes = state->mainlen;
720 	header_main.packet = state->mainbuf;
721 	header_main.b_o_s = 1;
722 	header_main.e_o_s = 0;
723 	header_main.granulepos = 0;
724 
725 	header_codebooks.bytes = state->booklen;
726 	header_codebooks.packet = state->bookbuf;
727 	header_codebooks.b_o_s = 0;
728 	header_codebooks.e_o_s = 0;
729 	header_codebooks.granulepos = 0;
730 
731 	ogg_stream_init(&streamout, state->serial);
732 
733 	_commentheader_out(state->vc, state->vendor, &header_comments);
734 
735 	ogg_stream_packetin(&streamout, &header_main);
736 	ogg_stream_packetin(&streamout, &header_comments);
737 	ogg_stream_packetin(&streamout, &header_codebooks);
738 
739 	while((result = ogg_stream_flush(&streamout, &ogout)))
740 	{
741 		if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
742 			goto cleanup;
743 		if(state->write(ogout.header,1,ogout.header_len, out) !=
744 				(size_t) ogout.header_len)
745 			goto cleanup;
746 		if(state->write(ogout.body,1,ogout.body_len, out) !=
747 				(size_t) ogout.body_len)
748 			goto cleanup;
749 	}
750 
751 	while(state->sidebuf) {
752 	  if(buffer_chain_writelink(state, out) < 0)
753 	    goto cleanup;
754 	}
755 	if(buffer_chain_newlink(state) < 0)
756 		goto cleanup;
757 
758 	while(_fetch_next_packet(state, &op, &ogin))
759 	{
760 		int size;
761 		size = _blocksize(state, &op);
762 		granpos += size;
763 
764 		if(needflush)
765 		{
766 			if(ogg_stream_flush(&streamout, &ogout))
767 			{
768 				if(state->sidebuf &&
769 				   buffer_chain_writelink(state, out) < 0)
770 					goto cleanup;
771 				if(state->write(ogout.header,1,ogout.header_len,
772 							out) != (size_t) ogout.header_len)
773 					goto cleanup;
774 				if(state->write(ogout.body,1,ogout.body_len,
775 							out) != (size_t) ogout.body_len)
776 					goto cleanup;
777 			}
778 		}
779 		else if(needout)
780 		{
781 			if(ogg_stream_pageout(&streamout, &ogout))
782 			{
783 				if(state->sidebuf &&
784 				   buffer_chain_writelink(state, out) < 0)
785 					goto cleanup;
786 				if(state->write(ogout.header,1,ogout.header_len,
787 							out) != (size_t) ogout.header_len)
788 					goto cleanup;
789 				if(state->write(ogout.body,1,ogout.body_len,
790 							out) != (size_t) ogout.body_len)
791 					goto cleanup;
792 			}
793 		}
794 
795 		needflush=needout=0;
796 
797 		if(op.granulepos == -1)
798 		{
799 			op.granulepos = granpos;
800 			ogg_stream_packetin(&streamout, &op);
801 		}
802 		else /* granulepos is set, validly. Use it, and force a flush to
803 				account for shortened blocks (vcut) when appropriate */
804 		{
805 			if(granpos > op.granulepos)
806 			{
807 				granpos = op.granulepos;
808 				ogg_stream_packetin(&streamout, &op);
809 				needflush=1;
810 			}
811 			else
812 			{
813 				ogg_stream_packetin(&streamout, &op);
814 				needout=1;
815 			}
816 		}
817 	}
818 
819 	streamout.e_o_s = 1;
820 	while(ogg_stream_flush(&streamout, &ogout))
821 	{
822 		if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
823 			goto cleanup;
824 		if(state->write(ogout.header,1,ogout.header_len,
825 					out) != (size_t) ogout.header_len)
826 			goto cleanup;
827 		if(state->write(ogout.body,1,ogout.body_len,
828 					out) != (size_t) ogout.body_len)
829 			goto cleanup;
830 	}
831 
832 	if (state->extrapage)
833 	{
834 		/* This is the first page of a new chain, get rid of the
835 		 * sidebuffer */
836 		while(state->sidebuf)
837 			if(buffer_chain_writelink(state, out) < 0)
838 				goto cleanup;
839 		if(state->write(ogin.header,1,ogin.header_len,
840 		                out) != (size_t) ogin.header_len)
841 			goto cleanup;
842 		if (state->write(ogin.body,1,ogin.body_len, out) !=
843 				(size_t) ogin.body_len)
844 			goto cleanup;
845 	}
846 
847 	state->eosin=0; /* clear it, because not all paths to here do */
848 	while(!state->eosin) /* We reached eos, not eof */
849 	{
850 		/* We copy the rest of the stream (other logical streams)
851 		 * through, a page at a time. */
852 		while(1)
853 		{
854 			result = ogg_sync_pageout(state->oy, &ogout);
855 			if(result==0)
856                 break;
857 			if(result<0)
858 				state->lasterror = _("Corrupt or missing data, continuing...");
859 			else
860 			{
861 				/* Don't bother going through the rest, we can just
862 				 * write the page out now */
863 				if(state->write(ogout.header,1,ogout.header_len,
864 						out) != (size_t) ogout.header_len) {
865 					goto cleanup;
866                 }
867 				if(state->write(ogout.body,1,ogout.body_len, out) !=
868 						(size_t) ogout.body_len) {
869 					goto cleanup;
870                 }
871 			}
872 		}
873 		buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
874 		bytes = state->read(buffer,1, CHUNKSIZE, state->in);
875 		ogg_sync_wrote(state->oy, bytes);
876 		if(bytes == 0)
877 		{
878 			state->eosin = 1;
879 			break;
880 		}
881 	}
882 
883 
884 cleanup:
885 	ogg_stream_clear(&streamout);
886 
887     /* We don't ogg_packet_clear() this, because the memory was allocated in
888        _commentheader_out(), so we mirror that here */
889     _ogg_free(header_comments.packet);
890 
891 	free(state->mainbuf);
892 	free(state->bookbuf);
893     state->mainbuf = state->bookbuf = NULL;
894 
895 	if(!state->eosin)
896 	{
897 		state->lasterror =
898 			_("Error writing stream to output. "
899 			"Output stream may be corrupted or truncated.");
900 		return -1;
901 	}
902 
903 	return 0;
904 }
905 
906 /* kid3 */
907 #endif /* HAVE_VORBIS */
908