File indexing completed on 2024-05-12 04:55:42

0001 /* This program is licensed under the GNU Library General Public License, version 2,
0002  * a copy of which is included with this program (LICENCE.LGPL).
0003  *
0004  * (c) 2000-2001 Michael Smith <msmith@xiph.org>
0005  *
0006  *
0007  * Comment editing backend, suitable for use by nice frontend interfaces.
0008  *
0009  * last modified: $Id$
0010  */
0011 
0012 /* Handle muxed streams and the Vorbis renormalization without having
0013  * to understand remuxing:
0014  * Linked list of buffers (buffer_chain).  Start a link and whenever
0015  * you encounter an unknown page from the current stream (ie we found
0016  * its bos in the bos section) push it onto the current buffer.  Whenever
0017  * you encounter the stream being renormalized create a new link in the
0018  * chain.
0019  * On writing, write the contents of the first link before every Vorbis
0020  * page written, and move to the next link.  Assuming the Vorbis pages
0021  * in match vorbis pages out, the order of pages from different logical
0022  * streams will be unchanged.
0023  * Special case: header. After writing the vorbis headers, and before
0024  * starting renormalization, flush accumulated links (takes care of
0025  * situations where number of secondary vorbis header pages changes due
0026  * to remuxing.  Similarly flush links at the end of renormalization
0027  * and before the start of the next chain is written.
0028  *
0029  */
0030 
0031 /* This file is taken from vorbis-tools SVN
0032  * (http://svn.xiph.org/trunk/vorbis-tools/vorbiscomment) Rev. 16716 2009-11-22,
0033  * modifications are marked with kid3. */
0034 /* kid3 */
0035 #include "oggflacconfig.h"
0036 #ifdef HAVE_VORBIS
0037 
0038 #ifdef HAVE_CONFIG_H
0039 #include <config.h>
0040 #endif
0041 
0042 #include <stdio.h>
0043 #include <stdlib.h>
0044 #include <string.h>
0045 #include <errno.h>
0046 #include <ogg/ogg.h>
0047 #include <vorbis/codec.h>
0048 
0049 #include "vcedit.h"
0050 /* kid3 */
0051 /*#include "vceditaux.h"*/
0052 typedef struct vcedit_page_buffer {
0053     char *data;
0054     size_t data_len;
0055 } vcedit_page_buffer;
0056     
0057 typedef struct vcedit_buffer_chain {
0058     struct vcedit_buffer_chain *next;
0059     struct vcedit_page_buffer buffer;
0060 } vcedit_buffer_chain;
0061 /* kid3 */
0062 /*#include "i18n.h"*/
0063 #define _(str) ((char*)(str))
0064 
0065 
0066 #define CHUNKSIZE 4096
0067 #define BUFFERCHUNK CHUNKSIZE
0068 
0069 /* Helper function, shouldn't need to call directly */
0070 static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) {
0071     int result=0;
0072     char *tmp;
0073     vcedit_page_buffer *buffer;
0074     
0075     buffer = &bufferlink->buffer;
0076     tmp = realloc(buffer->data,
0077               buffer->data_len + og->header_len + og->body_len);
0078     if(tmp) {
0079         buffer->data = tmp;
0080         memcpy(buffer->data + buffer->data_len, og->header,
0081                og->header_len);
0082         buffer->data_len += og->header_len;
0083         memcpy(buffer->data + buffer->data_len, og->body,
0084            og->body_len);
0085         result = 1;
0086         buffer->data_len += og->body_len;
0087     } else {
0088         result = -1;
0089     }
0090     
0091     return result;
0092 }
0093 
0094 /* Write and free the first link using callbacks */
0095 static int buffer_chain_writelink(vcedit_state *state, void *out) {
0096     int result = 0;
0097     vcedit_buffer_chain *tmpchain;
0098     vcedit_page_buffer *tmpbuffer;
0099 
0100     tmpchain = state->sidebuf;
0101     tmpbuffer = &tmpchain->buffer;
0102     if(tmpbuffer->data_len)
0103     {
0104         if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) !=
0105            (size_t) tmpbuffer->data_len)
0106             result = -1;
0107         else 
0108             result = 1;
0109     }
0110 
0111     free(tmpbuffer->data);
0112     state->sidebuf = tmpchain->next;
0113     free(tmpchain);
0114     return result;
0115 }
0116 
0117 
0118 static int buffer_chain_newlink(vcedit_state *state) {
0119     int result = 1;
0120     vcedit_buffer_chain *bufferlink;
0121     
0122     if(!state->sidebuf) {
0123         state->sidebuf = malloc (sizeof *state->sidebuf);
0124         if(state->sidebuf) {
0125             bufferlink = state->sidebuf;
0126         } else {
0127             result = -1;
0128         }
0129     } else {
0130         bufferlink=state->sidebuf;
0131         while(bufferlink->next) {
0132             bufferlink = bufferlink->next;
0133         }
0134         bufferlink->next =  malloc (sizeof *bufferlink->next);
0135         if(bufferlink->next) {
0136             bufferlink = bufferlink->next;
0137         } else {
0138             result = -1;
0139         }
0140     }
0141 
0142     if(result > 0 ) {
0143         bufferlink->next = 0;
0144         bufferlink->buffer.data = 0;
0145         bufferlink->buffer.data_len = 0;
0146     }
0147     else 
0148         state->lasterror =
0149             _("Couldn't get enough memory for input buffering.");
0150 
0151   return result;
0152 }
0153 
0154 
0155 /* Push page onto the end of the buffer chain */
0156 static int buffer_chain_push(vcedit_state *state, ogg_page *og) {
0157     /* If there is no sidebuffer yet we need to create one, otherwise
0158      * traverse to the last buffer and push the new page onto it. */
0159     int result=1;
0160     vcedit_buffer_chain *bufferlink;
0161     if(!state->sidebuf) {
0162         result = buffer_chain_newlink(state);
0163     }
0164     
0165     if(result > 0) {
0166         bufferlink = state->sidebuf;
0167         while(bufferlink->next) {
0168             bufferlink = bufferlink->next;
0169     }
0170         result = page_buffer_push(bufferlink, og);
0171     }
0172 
0173     if(result < 0)
0174         state->lasterror =
0175             _("Couldn't get enough memory for input buffering.");
0176 
0177     return result;
0178 }
0179 
0180 
0181 
0182 static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) {
0183     ogg_stream_state os;
0184     vorbis_info vi;
0185     vorbis_comment vc;
0186     ogg_packet header;
0187     int result = 0;
0188     
0189     ogg_stream_init(&os, ogg_page_serialno(og));
0190     vorbis_info_init(&vi);
0191     vorbis_comment_init(&vc);
0192     
0193     if( !ogg_page_bos(og) )
0194                 result = -1;
0195 
0196     if(result >= 0 && ogg_stream_pagein(&os, og) < 0)
0197     {
0198         state->lasterror =
0199             _("Error reading first page of Ogg bitstream.");
0200         result = -1;
0201     }
0202 
0203     if(result >= 0 && ogg_stream_packetout(&os, &header) != 1)
0204     {
0205         state->lasterror = _("Error reading initial header packet.");
0206         result = -1;
0207     }
0208 
0209     if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0)
0210     {
0211                 result = 1;
0212     } else {
0213         /* Not vorbis, may eventually become a chain of checks (Speex,
0214          * Theora), but for the moment return 0, bos scan will push
0215                  * the current page onto the buffer.
0216          */
0217     }
0218 
0219     ogg_stream_clear(&os);
0220     vorbis_info_clear(&vi);
0221     vorbis_comment_clear(&vc);
0222         return result;
0223 }
0224 
0225 
0226 static int vcedit_contains_serial (vcedit_state *state, int serialno) {
0227     int result = 0;
0228     size_t count;
0229     for( count=0; count < state->serials.streams_len; count++ ) {
0230         if ( *(state->serials.streams + count ) == serialno )
0231             result = 1;
0232     }
0233     
0234     return result;
0235 }
0236 
0237 
0238 static int vcedit_add_serial (vcedit_state *state, long serial) {
0239         int result = 0;
0240         long *tmp;
0241     
0242     
0243     if( vcedit_contains_serial(state, serial) )
0244         {
0245         result = 1;
0246     } else {
0247         tmp   = realloc(state->serials.streams,
0248                 (state->serials.streams_len + 1) * sizeof *tmp);
0249         if(tmp) {
0250             state->serials.streams = tmp;
0251             *(state->serials.streams +
0252               state->serials.streams_len) = serial;
0253             state->serials.streams_len += 1;
0254             result = 1;
0255         } else {
0256             state->lasterror =
0257                 _("Couldn't get enough memory to register new stream serial number.");
0258                 result = -1;
0259         }
0260     } 
0261     return result;
0262 }
0263 
0264 
0265 /* For the benefit of the secondary header read only.  Quietly creates
0266  * newlinks and pushes pages onto the buffer in the right way */
0267 static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) {
0268     int result = 0;
0269     int pageout_result;
0270     pageout_result = ogg_sync_pageout(state->oy, og);
0271     if(pageout_result > 0)
0272     {
0273         if(state->serial == ogg_page_serialno(og))
0274             result = buffer_chain_newlink(state);
0275         else
0276             result = buffer_chain_push(state, og);
0277     } else if (pageout_result < 0) {
0278         /* Vorbis comment traditionally ignores the not-synced
0279          * error from pageout, so give it a different code. */
0280         result = -2;
0281     }
0282     return result;
0283 }
0284 
0285 
0286 /* (I'm paranoid about memset(x,0,len) not giving null pointers */
0287 vcedit_state *vcedit_new_state(void) {
0288     vcedit_state *state = malloc(sizeof(vcedit_state));
0289     if(state) {
0290         memset(state, 0, sizeof(vcedit_state));
0291         state->sidebuf = 0;
0292         state->serials.streams = 0;
0293         state->serials.streams_len = 0;
0294     }
0295     return state;
0296 }
0297 
0298 char *vcedit_error(vcedit_state *state) {
0299     return state->lasterror;
0300 }
0301 
0302 vorbis_comment *vcedit_comments(vcedit_state *state) {
0303     return state->vc;
0304 }
0305 
0306 static void vcedit_clear_internals(vcedit_state *state) {
0307     char *tmp;
0308     if(state->vc) {
0309         vorbis_comment_clear(state->vc);
0310         free(state->vc);
0311     }
0312     if(state->os) {
0313         ogg_stream_clear(state->os);
0314         free(state->os);
0315     }
0316     if(state->oy) {
0317         ogg_sync_clear(state->oy);
0318         free(state->oy);
0319     }
0320     if(state->serials.streams_len) {
0321         free(state->serials.streams);
0322         state->serials.streams_len = 0;
0323         state->serials.streams = 0;
0324     }
0325     while(state->sidebuf) {
0326         vcedit_buffer_chain *tmpbuffer;
0327         tmpbuffer = state->sidebuf;
0328         state->sidebuf = tmpbuffer->next;
0329         free(tmpbuffer->buffer.data);
0330         free(tmpbuffer);
0331     }
0332     if(state->vendor)
0333         free(state->vendor);
0334     if(state->mainbuf)
0335         free(state->mainbuf);
0336     if(state->bookbuf)
0337         free(state->bookbuf);
0338     if(state->vi) {
0339         vorbis_info_clear(state->vi);
0340         free(state->vi);
0341     }
0342 
0343     tmp = state->lasterror;
0344     memset(state, 0, sizeof(*state));
0345     state->lasterror = tmp;
0346 }
0347 
0348 void vcedit_clear(vcedit_state *state)
0349 {
0350     if(state)
0351     {
0352         vcedit_clear_internals(state);
0353         free(state);
0354     }
0355 }
0356 
0357 /* Next two functions pulled straight from libvorbis, apart from one change
0358  * - we don't want to overwrite the vendor string.
0359  */
0360 /* kid3: changed type from char* to const char* */
0361 static void _v_writestring(oggpack_buffer *o, const char *s, int len)
0362 {
0363     while(len--)
0364     {
0365         oggpack_write(o,*s++,8);
0366     }
0367 }
0368 
0369 static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
0370 {
0371     oggpack_buffer opb;
0372 
0373     oggpack_writeinit(&opb);
0374 
0375     /* preamble */  
0376     oggpack_write(&opb,0x03,8);
0377     _v_writestring(&opb,"vorbis", 6);
0378 
0379     /* vendor */
0380     oggpack_write(&opb,strlen(vendor),32);
0381     _v_writestring(&opb,vendor, strlen(vendor));
0382 
0383     /* comments */
0384     oggpack_write(&opb,vc->comments,32);
0385     if(vc->comments){
0386         int i;
0387         for(i=0;i<vc->comments;i++){
0388             if(vc->user_comments[i]){
0389                 oggpack_write(&opb,vc->comment_lengths[i],32);
0390                 _v_writestring(&opb,vc->user_comments[i], 
0391                         vc->comment_lengths[i]);
0392             }else{
0393                 oggpack_write(&opb,0,32);
0394             }
0395         }
0396     }
0397     oggpack_write(&opb,1,1);
0398 
0399     op->packet = malloc(oggpack_bytes(&opb));
0400     memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
0401 
0402     op->bytes=oggpack_bytes(&opb);
0403     op->b_o_s=0;
0404     op->e_o_s=0;
0405     op->granulepos=0;
0406 
0407     oggpack_writeclear(&opb);
0408     return 0;
0409 }
0410 
0411 static int _blocksize(vcedit_state *s, ogg_packet *p)
0412 {
0413     int this = vorbis_packet_blocksize(s->vi, p);
0414     int ret = (this + s->prevW)/4;
0415 
0416     if(!s->prevW)
0417     {
0418         s->prevW = this;
0419         return 0;
0420     }
0421 
0422     s->prevW = this;
0423     return ret;
0424 }
0425 
0426 static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
0427 {
0428     int result;
0429     char *buffer;
0430     int bytes;
0431     int serialno;
0432 
0433     result = ogg_stream_packetout(s->os, p);
0434 
0435     if(result > 0)
0436         return 1;
0437     else {
0438         while(1) {
0439             if(s->eosin)
0440                 return 0;
0441 
0442             while(ogg_sync_pageout(s->oy, page) <= 0)
0443             {
0444                 buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
0445                 bytes = s->read(buffer,1, CHUNKSIZE, s->in);
0446                 ogg_sync_wrote(s->oy, bytes);
0447                 if(bytes == 0)
0448                 return 0;
0449             }
0450 
0451             serialno = ogg_page_serialno(page);
0452             if(ogg_page_serialno(page) != s->serial)
0453             {
0454                 if(vcedit_contains_serial(s, serialno)) {
0455                     result = buffer_chain_push(s, page);
0456                     if(result < 0)
0457                         return result;
0458                 }
0459                 else
0460                 {
0461                     s->eosin = 1;
0462                     s->extrapage = 1;
0463                     return 0;
0464                 }
0465             } 
0466             else
0467             {
0468               ogg_stream_pagein(s->os, page);
0469               result = buffer_chain_newlink(s);
0470               if (result < 0)
0471                   return result;
0472 
0473               if(ogg_page_eos(page))
0474                 s->eosin = 1;
0475             }
0476             result = ogg_stream_packetout(s->os, p);
0477             if(result > 0)
0478                 return 1;
0479         }
0480         /* Here == trouble */
0481         return 0;
0482     }
0483 }
0484 
0485 /* kid3 vcedit_open() removed */
0486 
0487 int vcedit_open_callbacks(vcedit_state *state, void *in,
0488         vcedit_read_func read_func, vcedit_write_func write_func)
0489 {
0490 
0491     char *buffer;
0492     int bytes,i;
0493     int chunks = 0;
0494     int read_bos, test_supported, page_pending;
0495     int have_vorbis;
0496     size_t vendor_size;
0497     ogg_packet *header;
0498     ogg_packet  header_main;
0499     ogg_packet  header_comments;
0500     ogg_packet  header_codebooks;
0501     ogg_page    og;
0502 
0503     /* kid3, to fix LLVM scan-build error "Assigned value is garbage or undefined"
0504        in line "state->mainlen = header_main.bytes;" */
0505     memset(&header_main, 0, sizeof(header_main));
0506 
0507     state->in = in;
0508     state->read = read_func;
0509     state->write = write_func;
0510 
0511     state->oy = malloc(sizeof(ogg_sync_state));
0512     ogg_sync_init(state->oy);
0513 
0514     while(1)
0515     {
0516         buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
0517         bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
0518 
0519         ogg_sync_wrote(state->oy, bytes);
0520 
0521         if(ogg_sync_pageout(state->oy, &og) == 1)
0522             break;
0523 
0524         if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
0525         {
0526             if(bytes<CHUNKSIZE)
0527                 state->lasterror = _("Input truncated or empty.");
0528             else
0529                 state->lasterror = _("Input is not an Ogg bitstream.");
0530         goto err;
0531     }
0532     }
0533 
0534     /* BOS loop, starting with a loaded ogg page. */
0535     if(buffer_chain_newlink(state) < 0)
0536         goto err;
0537 
0538     for( read_bos = 1, have_vorbis = 0 ; read_bos; )
0539     {
0540         test_supported = vcedit_supported_stream(state, &og);
0541         if(test_supported < 0)
0542         {
0543             goto err;
0544         }
0545         else if (test_supported == 0 || have_vorbis )
0546         {
0547             if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
0548                 goto err;
0549             if( buffer_chain_push(state, &og) < 0)
0550                 goto err;
0551         }
0552         else if (test_supported > 0)
0553         {
0554             if(buffer_chain_newlink(state) < 0)
0555                 goto err;
0556             state->serial = ogg_page_serialno(&og);
0557             if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
0558                goto err;
0559  
0560             state->os = malloc(sizeof(ogg_stream_state));
0561             ogg_stream_init(state->os, state->serial);
0562 
0563             state->vi = malloc(sizeof(vorbis_info));
0564             vorbis_info_init(state->vi);
0565 
0566             state->vc = malloc(sizeof(vorbis_comment));
0567             vorbis_comment_init(state->vc);
0568 
0569             if(ogg_stream_pagein(state->os, &og) < 0)
0570             {
0571                 state->lasterror = 
0572                     _("Error reading first page of Ogg bitstream.");
0573                 goto err;
0574             }
0575 
0576             if(ogg_stream_packetout(state->os, &header_main) != 1)
0577             {
0578                 state->lasterror =
0579                     _("Error reading initial header packet.");
0580                 goto err;
0581             }
0582 
0583             if(vorbis_synthesis_headerin(state->vi, state->vc,
0584                          &header_main) < 0)
0585             {
0586                 state->lasterror =
0587                     _("Ogg bitstream does not contain Vorbis data.");
0588                 goto err;
0589             }
0590             have_vorbis = 1;
0591         }
0592         while(1)
0593         {
0594             buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
0595             bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
0596 
0597             if(bytes == 0)
0598             {
0599                 state->lasterror =
0600                     _("EOF before recognised stream.");
0601                 goto err;
0602             }
0603 
0604             ogg_sync_wrote(state->oy, bytes);
0605 
0606             if(ogg_sync_pageout(state->oy, &og) == 1)
0607                 break;
0608         }
0609         if(!ogg_page_bos(&og)) {
0610             read_bos = 0;
0611             page_pending = 1;
0612         }
0613     }
0614 
0615         if(!state->os) {
0616         state->lasterror = _("Ogg bitstream does not contain a supported data-type.");
0617         goto err;
0618     }
0619 
0620     state->mainlen = header_main.bytes;
0621     state->mainbuf = malloc(state->mainlen);
0622     memcpy(state->mainbuf, header_main.packet, header_main.bytes);
0623 
0624     if(ogg_page_serialno(&og) == state->serial)
0625     {
0626         if(buffer_chain_newlink(state) < 0)
0627             goto err;
0628     }
0629 
0630     else
0631     {
0632         if(buffer_chain_push(state, &og) < 0)
0633             goto err;
0634         page_pending = 0;
0635     }
0636 
0637     i = 0;
0638     header = &header_comments;
0639     while(i<2) {
0640         while(i<2) {
0641             int result;
0642             if(!page_pending)
0643                 result = vcedit_target_pageout(state, &og);
0644             else
0645             {
0646                 result = 1;
0647                 page_pending = 0;
0648             }
0649             if(result == 0 || result == -2) break; /* Too little data so far */
0650             else if(result == -1) goto err;
0651             else if(result == 1)
0652             {
0653                 ogg_stream_pagein(state->os, &og);
0654                 while(i<2)
0655                 {
0656                     result = ogg_stream_packetout(state->os, header);
0657                     if(result == 0) break;
0658                     if(result == -1)
0659                     {
0660                         state->lasterror = _("Corrupt secondary header.");
0661                         goto err;
0662                     }
0663                     vorbis_synthesis_headerin(state->vi, state->vc, header);
0664                     if(i==1)
0665                     {
0666                         state->booklen = header->bytes;
0667                         state->bookbuf = malloc(state->booklen);
0668                         memcpy(state->bookbuf, header->packet, 
0669                                 header->bytes);
0670                     }
0671                     i++;
0672                     header = &header_codebooks;
0673                 }
0674             }
0675         }
0676 
0677         buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
0678         bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
0679         if(bytes == 0 && i < 2)
0680         {
0681             state->lasterror = _("EOF before end of Vorbis headers.");
0682             goto err;
0683         }
0684         ogg_sync_wrote(state->oy, bytes);
0685     }
0686 
0687     /* Copy the vendor tag */
0688     /* kid3 */
0689     vendor_size = strlen(state->vc->vendor) +1;
0690     state->vendor = malloc(vendor_size);
0691     memcpy(state->vendor, state->vc->vendor, vendor_size);
0692 
0693     /* Headers are done! */
0694     return 0;
0695 
0696 err:
0697     vcedit_clear_internals(state);
0698     return -1;
0699 }
0700 
0701 int vcedit_write(vcedit_state *state, void *out)
0702 {
0703     ogg_stream_state streamout;
0704     ogg_packet header_main;
0705     ogg_packet header_comments;
0706     ogg_packet header_codebooks;
0707 
0708     ogg_page ogout, ogin;
0709     ogg_packet op;
0710     ogg_int64_t granpos = 0;
0711     int result;
0712     char *buffer;
0713     int bytes;
0714     int needflush=0, needout=0;
0715 
0716     state->eosin = 0;
0717     state->extrapage = 0;
0718 
0719     header_main.bytes = state->mainlen;
0720     header_main.packet = state->mainbuf;
0721     header_main.b_o_s = 1;
0722     header_main.e_o_s = 0;
0723     header_main.granulepos = 0;
0724 
0725     header_codebooks.bytes = state->booklen;
0726     header_codebooks.packet = state->bookbuf;
0727     header_codebooks.b_o_s = 0;
0728     header_codebooks.e_o_s = 0;
0729     header_codebooks.granulepos = 0;
0730 
0731     ogg_stream_init(&streamout, state->serial);
0732 
0733     _commentheader_out(state->vc, state->vendor, &header_comments);
0734 
0735     ogg_stream_packetin(&streamout, &header_main);
0736     ogg_stream_packetin(&streamout, &header_comments);
0737     ogg_stream_packetin(&streamout, &header_codebooks);
0738 
0739     while((result = ogg_stream_flush(&streamout, &ogout)))
0740     {
0741         if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
0742             goto cleanup;
0743         if(state->write(ogout.header,1,ogout.header_len, out) !=
0744                 (size_t) ogout.header_len)
0745             goto cleanup;
0746         if(state->write(ogout.body,1,ogout.body_len, out) != 
0747                 (size_t) ogout.body_len)
0748             goto cleanup;
0749     }
0750 
0751     while(state->sidebuf) {
0752       if(buffer_chain_writelink(state, out) < 0)
0753         goto cleanup;
0754     }
0755     if(buffer_chain_newlink(state) < 0)
0756         goto cleanup;
0757 
0758     while(_fetch_next_packet(state, &op, &ogin))
0759     {
0760         int size;
0761         size = _blocksize(state, &op);
0762         granpos += size;
0763 
0764         if(needflush)
0765         {
0766             if(ogg_stream_flush(&streamout, &ogout))
0767             {
0768                 if(state->sidebuf &&
0769                    buffer_chain_writelink(state, out) < 0)
0770                     goto cleanup;
0771                 if(state->write(ogout.header,1,ogout.header_len, 
0772                             out) != (size_t) ogout.header_len)
0773                     goto cleanup;
0774                 if(state->write(ogout.body,1,ogout.body_len, 
0775                             out) != (size_t) ogout.body_len)
0776                     goto cleanup;
0777             }
0778         }
0779         else if(needout)
0780         {
0781             if(ogg_stream_pageout(&streamout, &ogout))
0782             {
0783                 if(state->sidebuf &&
0784                    buffer_chain_writelink(state, out) < 0)
0785                     goto cleanup;
0786                 if(state->write(ogout.header,1,ogout.header_len, 
0787                             out) != (size_t) ogout.header_len)
0788                     goto cleanup;
0789                 if(state->write(ogout.body,1,ogout.body_len, 
0790                             out) != (size_t) ogout.body_len)
0791                     goto cleanup;
0792             }
0793         }
0794 
0795         needflush=needout=0;
0796 
0797         if(op.granulepos == -1)
0798         {
0799             op.granulepos = granpos;
0800             ogg_stream_packetin(&streamout, &op);
0801         }
0802         else /* granulepos is set, validly. Use it, and force a flush to 
0803                 account for shortened blocks (vcut) when appropriate */ 
0804         {
0805             if(granpos > op.granulepos)
0806             {
0807                 granpos = op.granulepos;
0808                 ogg_stream_packetin(&streamout, &op);
0809                 needflush=1;
0810             }
0811             else 
0812             {
0813                 ogg_stream_packetin(&streamout, &op);
0814                 needout=1;
0815             }
0816         }       
0817     }
0818 
0819     streamout.e_o_s = 1;
0820     while(ogg_stream_flush(&streamout, &ogout))
0821     {
0822         if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
0823             goto cleanup;
0824         if(state->write(ogout.header,1,ogout.header_len, 
0825                     out) != (size_t) ogout.header_len)
0826             goto cleanup;
0827         if(state->write(ogout.body,1,ogout.body_len, 
0828                     out) != (size_t) ogout.body_len)
0829             goto cleanup;
0830     }
0831 
0832     if (state->extrapage)
0833     {
0834         /* This is the first page of a new chain, get rid of the
0835          * sidebuffer */
0836         while(state->sidebuf)
0837             if(buffer_chain_writelink(state, out) < 0)
0838                 goto cleanup;
0839         if(state->write(ogin.header,1,ogin.header_len,
0840                         out) != (size_t) ogin.header_len)
0841             goto cleanup;
0842         if (state->write(ogin.body,1,ogin.body_len, out) !=
0843                 (size_t) ogin.body_len)
0844             goto cleanup;
0845     }
0846 
0847     state->eosin=0; /* clear it, because not all paths to here do */
0848     while(!state->eosin) /* We reached eos, not eof */
0849     {
0850         /* We copy the rest of the stream (other logical streams)
0851          * through, a page at a time. */
0852         while(1)
0853         {
0854             result = ogg_sync_pageout(state->oy, &ogout);
0855             if(result==0)
0856                 break;
0857             if(result<0)
0858                 state->lasterror = _("Corrupt or missing data, continuing...");
0859             else
0860             {
0861                 /* Don't bother going through the rest, we can just 
0862                  * write the page out now */
0863                 if(state->write(ogout.header,1,ogout.header_len, 
0864                         out) != (size_t) ogout.header_len) {
0865                     goto cleanup;
0866                 }
0867                 if(state->write(ogout.body,1,ogout.body_len, out) !=
0868                         (size_t) ogout.body_len) {
0869                     goto cleanup;
0870                 }
0871             }
0872         }
0873         buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
0874         bytes = state->read(buffer,1, CHUNKSIZE, state->in);
0875         ogg_sync_wrote(state->oy, bytes);
0876         if(bytes == 0) 
0877         {
0878             state->eosin = 1;
0879             break;
0880         }
0881     }
0882 
0883 
0884 cleanup:
0885     ogg_stream_clear(&streamout);
0886 
0887     /* We don't ogg_packet_clear() this, because the memory was allocated in
0888        _commentheader_out(), so we mirror that here */
0889     _ogg_free(header_comments.packet);
0890 
0891     free(state->mainbuf);
0892     free(state->bookbuf);
0893     state->mainbuf = state->bookbuf = NULL;
0894 
0895     if(!state->eosin)
0896     {
0897         state->lasterror =
0898             _("Error writing stream to output. "
0899             "Output stream may be corrupted or truncated.");
0900         return -1;
0901     }
0902 
0903     return 0;
0904 }
0905 
0906 /* kid3 */
0907 #endif /* HAVE_VORBIS */