#include <errno.h>
#include <limits.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <ogg/ogg.h>
#include <vorbis/codec.h>
#include <assert.h>
vorbis_info vi;
FILE *in;
+ mode_t file_mode;
+
bool opened;
long serial;
- unsigned char *mainbuf;
- unsigned char *bookbuf;
- int mainlen;
- int booklen;
+
+ ogg_packet packet_main;
+ ogg_packet packet_code_books;
+
char *vendor;
int prevW;
- int extrapage;
- int eosin;
+
+ bool extra_page;
+ bool eos;
char filename[0];
};
+static void
+ogg_packet_init (ogg_packet *p, unsigned char *buf, long len)
+{
+ p->packet = buf;
+ p->bytes = len;
+
+ p->b_o_s = p->e_o_s = 0;
+ p->granulepos = p->packetno = 0;
+}
+
+static bool
+ogg_packet_dup_data (ogg_packet *p)
+{
+ unsigned char *tmp;
+
+ tmp = malloc (p->bytes);
+ if (!tmp)
+ return false;
+
+ memcpy (tmp, p->packet, p->bytes);
+ p->packet = tmp;
+
+ return true;
+}
+
static void
vcedit_state_free (vcedit_state *s)
{
- free (s->mainbuf);
- free (s->bookbuf);
free (s->vendor);
if (s->in) {
{
s->refcount = 1;
+ ogg_packet_init (&s->packet_main, NULL, 0);
+ ogg_packet_init (&s->packet_code_books, NULL, 0);
+
strcpy (s->filename, filename);
return true;
if (len > PATH_MAX)
return NULL;
- s= malloc (sizeof (vcedit_state) + len + 1);
+ s = malloc (sizeof (vcedit_state) + len + 1);
if (!s)
return NULL;
free (s->vendor);
s->vendor = NULL;
- free (s->mainbuf);
- s->mainbuf = NULL;
- s->mainlen = 0;
-
- free (s->bookbuf);
- s->bookbuf = NULL;
- s->booklen = 0;
+ ogg_packet_clear (&s->packet_main);
+ ogg_packet_clear (&s->packet_code_books);
s->serial = 0;
s->opened = false;
* - we don't want to overwrite the vendor string.
*/
static void
-_v_writestring (oggpack_buffer *o, char *s, int len)
+_v_writestring (oggpack_buffer *o, const char *s, int len)
{
while (len--)
oggpack_write (o, *s++, 8);
}
-static int
-_commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op)
+static bool
+write_comments (vcedit_state *s, ogg_packet *packet)
{
+ oggpack_buffer opb;
+ size_t len;
int i;
- oggpack_buffer opb;
+ ogg_packet_init (packet, NULL, 0);
oggpack_writeinit (&opb);
_v_writestring (&opb, "vorbis", 6);
/* vendor */
- oggpack_write (&opb, strlen (vendor), 32);
- _v_writestring (&opb, vendor, strlen (vendor));
+ len = strlen (s->vendor);
+ oggpack_write (&opb, len, 32);
+ _v_writestring (&opb, s->vendor, len);
/* comments */
- oggpack_write (&opb, vc->comments, 32);
+ oggpack_write (&opb, s->vc.comments, 32);
- for (i = 0; i < vc->comments; i++) {
- if (!vc->user_comments[i])
+ for (i = 0; i < s->vc.comments; i++)
+ if (!s->vc.user_comments[i])
oggpack_write (&opb, 0, 32);
else {
- oggpack_write (&opb, vc->comment_lengths[i], 32);
- _v_writestring (&opb, vc->user_comments[i],
- vc->comment_lengths[i]);
+ oggpack_write (&opb, s->vc.comment_lengths[i], 32);
+ _v_writestring (&opb, s->vc.user_comments[i],
+ s->vc.comment_lengths[i]);
}
- }
oggpack_write (&opb, 1, 1);
- op->packet = _ogg_malloc (oggpack_bytes (&opb));
- memcpy (op->packet, opb.buffer, oggpack_bytes (&opb));
+ packet->bytes = oggpack_bytes (&opb);
- op->bytes = oggpack_bytes (&opb);
- op->b_o_s = 0;
- op->e_o_s = 0;
- op->granulepos = 0;
+ packet->packet = _ogg_malloc (packet->bytes);
+ if (!packet->packet)
+ return false;
+
+ memcpy (packet->packet, opb.buffer, packet->bytes);
oggpack_writeclear (&opb);
- return 0;
+ return true;
}
static int
if (result == 1)
return 1;
- if (s->eosin)
+ if (s->eos)
return 0;
while (ogg_sync_pageout (&s->oy, page) != 1) {
}
if (ogg_page_eos (page))
- s->eosin = 1;
+ s->eos = true;
else if (ogg_page_serialno (page) != s->serial) {
- s->eosin = 1;
- s->extrapage = 1;
+ s->eos = true;
+ s->extra_page = true;
+
return 0;
}
vcedit_open (vcedit_state *s)
{
vcedit_error ret;
- ogg_packet *header;
- ogg_packet header_main, header_comments, header_codebooks;
- ogg_page og;
+ ogg_packet packet_comments, *header = &packet_comments;
+ ogg_page page;
+ struct stat st;
char *buffer;
size_t bytes, total = 0;
int i = 0;
if (!s->in)
return VCEDIT_ERR_OPEN;
+ s->file_mode = stat (s->filename, &st) ? 0664 : st.st_mode;
+
ogg_sync_init (&s->oy);
do {
total += bytes;
ogg_sync_wrote (&s->oy, bytes);
- } while (ogg_sync_pageout (&s->oy, &og) != 1);
+ } while (ogg_sync_pageout (&s->oy, &page) != 1);
- s->serial = ogg_page_serialno (&og);
+ s->serial = ogg_page_serialno (&page);
ogg_stream_init (&s->os, s->serial);
vorbis_info_init (&s->vi);
vorbis_comment_init (&s->vc);
- if (ogg_stream_pagein (&s->os, &og) < 0) {
+ if (ogg_stream_pagein (&s->os, &page) < 0) {
ret = VCEDIT_ERR_INVAL;
goto err;
}
- if (ogg_stream_packetout (&s->os, &header_main) != 1) {
+ if (ogg_stream_packetout (&s->os, &s->packet_main) != 1) {
ret = VCEDIT_ERR_INVAL;
goto err;
}
- if (vorbis_synthesis_headerin (&s->vi, &s->vc, &header_main) < 0) {
+ if (!ogg_packet_dup_data (&s->packet_main)) {
+ s->packet_main.packet = NULL;
ret = VCEDIT_ERR_INVAL;
goto err;
}
- s->mainlen = header_main.bytes;
- s->mainbuf = malloc (s->mainlen);
- memcpy (s->mainbuf, header_main.packet, header_main.bytes);
+ if (vorbis_synthesis_headerin (&s->vi, &s->vc, &s->packet_main) < 0) {
+ ret = VCEDIT_ERR_INVAL;
+ goto err;
+ }
- header = &header_comments;
+ ogg_packet_init (&packet_comments, NULL, 0);
while (i < 2) {
if (feof (s->in) || ferror (s->in)) {
while (i < 2) {
int result;
- result = ogg_sync_pageout (&s->oy, &og);
+ result = ogg_sync_pageout (&s->oy, &page);
if (!result)
break; /* Too little data so far */
if (result != 1)
continue;
- ogg_stream_pagein (&s->os, &og);
+ ogg_stream_pagein (&s->os, &page);
while (i < 2) {
result = ogg_stream_packetout (&s->os, header);
goto err;
}
- vorbis_synthesis_headerin (&s->vi, &s->vc, header);
-
- if (i++ == 1) {
- s->booklen = header->bytes;
- s->bookbuf = malloc (s->booklen);
- memcpy (s->bookbuf, header->packet, header->bytes);
+ if (i++ == 1 && !ogg_packet_dup_data (header)) {
+ header->packet = NULL;
+ ret = VCEDIT_ERR_INVAL;
+ goto err;
}
- header = &header_codebooks;
+ vorbis_synthesis_headerin (&s->vi, &s->vc, header);
+
+ header = &s->packet_code_books;
}
}
return ret;
}
-static int
+static bool
write_data (const void *buf, size_t size, size_t nmemb, FILE *stream)
{
while (nmemb > 0) {
w = fwrite (buf, size, nmemb, stream);
if (!w && ferror (stream))
- return 0;
+ return false;
nmemb -= w;
buf += size * w;
}
- return 1;
+ return true;
+}
+
+static bool
+write_page (FILE *f, ogg_page *p)
+{
+ return write_data (p->header, 1, p->header_len, f) &&
+ write_data (p->body, 1, p->body_len, f);
}
vcedit_error
vcedit_write (vcedit_state *s)
{
- ogg_stream_state streamout;
- ogg_packet header_main, header_comments, header_codebooks, op;
- ogg_page ogout, ogin;
+ ogg_stream_state stream;
+ ogg_packet packet;
+ ogg_page page_out, page_in;
ogg_int64_t granpos = 0;
FILE *out;
char *buffer, tmpfile[PATH_MAX];
- int fd, result, bytes, needflush = 0, needout = 0;
+ bool success = false, need_flush = false, need_out = false;
+ int fd, result, bytes;
if (!s->opened)
return VCEDIT_ERR_INVAL;
return VCEDIT_ERR_TMPFILE;
}
- s->prevW = s->extrapage = s->eosin = 0;
+ s->prevW = 0;
+ s->extra_page = s->eos = false;
- header_main.bytes = s->mainlen;
- header_main.packet = s->mainbuf;
- header_main.b_o_s = 1;
- header_main.e_o_s = 0;
- header_main.granulepos = 0;
+ ogg_stream_init (&stream, s->serial);
- header_codebooks.bytes = s->booklen;
- header_codebooks.packet = s->bookbuf;
- header_codebooks.b_o_s = 0;
- header_codebooks.e_o_s = 0;
- header_codebooks.granulepos = 0;
+ /* write "main" packet */
+ s->packet_main.b_o_s = 1;
+ ogg_stream_packetin (&stream, &s->packet_main);
+ s->packet_main.b_o_s = 0;
- ogg_stream_init (&streamout, s->serial);
+ /* prepare and write comments */
+ if (!write_comments (s, &packet)) {
+ ogg_stream_clear (&stream);
+ unlink (tmpfile);
+ fclose (out);
- _commentheader_out (&s->vc, s->vendor, &header_comments);
+ return VCEDIT_ERR_INVAL;
+ }
- ogg_stream_packetin (&streamout, &header_main);
- ogg_stream_packetin (&streamout, &header_comments);
- ogg_stream_packetin (&streamout, &header_codebooks);
+ ogg_stream_packetin (&stream, &packet);
+ ogg_packet_clear (&packet);
- while ((result = ogg_stream_flush (&streamout, &ogout))) {
- if (!write_data (ogout.header, 1, ogout.header_len, out))
- goto cleanup;
+ /* write codebooks */
+ ogg_stream_packetin (&stream, &s->packet_code_books);
- if (!write_data (ogout.body, 1, ogout.body_len, out))
+ while (ogg_stream_flush (&stream, &page_out))
+ if (!write_page (out, &page_out))
goto cleanup;
- }
- while (_fetch_next_packet (s, &op, &ogin)) {
+ while (_fetch_next_packet (s, &packet, &page_in)) {
+ bool write = false;
int size;
- size = _blocksize (s, &op);
+ size = _blocksize (s, &packet);
granpos += size;
- if (needflush) {
- if (ogg_stream_flush (&streamout, &ogout)) {
- if (!write_data (ogout.header, 1, ogout.header_len, out))
- goto cleanup;
+ if (need_flush)
+ write = ogg_stream_flush (&stream, &page_out);
+ else if (need_out)
+ write = ogg_stream_pageout (&stream, &page_out);
- if (!write_data (ogout.body, 1, ogout.body_len, out))
- goto cleanup;
- }
- } else if (needout) {
- if (ogg_stream_pageout (&streamout, &ogout)) {
- if (!write_data (ogout.header, 1, ogout.header_len, out))
- goto cleanup;
-
- if (!write_data (ogout.body, 1, ogout.body_len, out))
- goto cleanup;
- }
- }
+ if (write && !write_page (out, &page_out))
+ goto cleanup;
- needflush = needout = 0;
+ need_flush = need_out = false;
- if (op.granulepos == -1) {
- op.granulepos = granpos;
- ogg_stream_packetin (&streamout, &op);
+ if (packet.granulepos == -1) {
+ packet.granulepos = granpos;
+ ogg_stream_packetin (&stream, &packet);
} else {
/* granulepos is set, validly. Use it, and force a flush to
* account for shortened blocks (vcut) when appropriate
*/
- if (granpos > op.granulepos) {
- granpos = op.granulepos;
- ogg_stream_packetin (&streamout, &op);
- needflush = 1;
+ if (granpos > packet.granulepos) {
+ granpos = packet.granulepos;
+ ogg_stream_packetin (&stream, &packet);
+ need_flush = true;
} else {
- ogg_stream_packetin (&streamout, &op);
- needout = 1;
+ ogg_stream_packetin (&stream, &packet);
+ need_out = true;
}
}
}
- streamout.e_o_s = 1;
-
- while (ogg_stream_flush (&streamout, &ogout)) {
- if (!write_data (ogout.header, 1, ogout.header_len, out))
- goto cleanup;
-
- if (!write_data (ogout.body, 1, ogout.body_len, out))
- goto cleanup;
- }
+ stream.e_o_s = 1;
- if (s->extrapage) {
- if (!write_data (ogin.header, 1, ogin.header_len, out))
+ while (ogg_stream_flush (&stream, &page_out))
+ if (!write_page (out, &page_out))
goto cleanup;
- if (!write_data (ogin.body, 1, ogin.body_len, out))
- goto cleanup;
- }
+ if (s->extra_page && !write_page (out, &page_in))
+ goto cleanup;
/* clear it, because not all paths to here do */
- s->eosin = 0;
+ s->eos = false;
do {
/* We copy the rest of the stream (other logical streams)
* through, a page at a time.
*/
- while ((result = ogg_sync_pageout (&s->oy, &ogout))) {
- if (result != 1)
- continue;
-
- /* Don't bother going through the rest, we can just
- * write the page out now
- */
- if (!write_data (ogout.header, 1, ogout.header_len, out))
+ while ((result = ogg_sync_pageout (&s->oy, &page_out)))
+ if (result == 1 && !write_page (out, &page_out))
goto cleanup;
- if (!write_data (ogout.body, 1, ogout.body_len, out))
- goto cleanup;
- }
-
buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
bytes = fread (buffer, 1, CHUNKSIZE, s->in);
ogg_sync_wrote (&s->oy, bytes);
if (ferror (s->in))
goto cleanup;
+ } while (bytes || !feof (s->in));
- s->eosin = !bytes && feof (s->in);
- } while (!s->eosin);
-
- fclose (out);
- fclose (s->in);
-
- unlink (s->filename);
- rename (tmpfile, s->filename);
+ s->eos = success = true;
cleanup:
- ogg_stream_clear (&streamout);
-
- /* We don't ogg_packet_clear() this, because the memory was
- * allocated in _commentheader_out(), so we mirror that here
- */
- _ogg_free (header_comments.packet);
+ fclose (s->in);
- free (s->mainbuf);
- free (s->bookbuf);
+ if (!success) {
+ unlink (tmpfile);
+ fclose (out);
+ } else {
+ fclose (out);
+ unlink (s->filename);
+ rename (tmpfile, s->filename);
+ chmod (s->filename, s->file_mode);
+ }
- s->mainbuf = s->bookbuf = NULL;
+ ogg_stream_clear (&stream);
- if (!s->eosin)
+ if (!s->eos)
return VCEDIT_ERR_INVAL;
vcedit_clear_internals (s);