vcedit functions now return an error code instead of setting lasterror.
Ogg::Vorbis::Tagger turns these error codes into their own exception
classes.
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
+#include <assert.h>
#include "vcedit.h"
#include "comments.h"
static VALUE c_close (VALUE self);
-static VALUE cComments, eVTError;
+static VALUE cComments, eVT, eOpen, eInvalidData, eInvalidComment,
+ eTempFile, eReopen;
static ID id_length;
static void
o->state = vcedit_state_new (StringValuePtr (filename));
if (!o->state)
- rb_raise (eVTError, "vcedit_new_state() failed - %s",
- vcedit_error (o->state));
-
- if (vcedit_open (o->state) < 0)
- rb_raise (eVTError, "vcedit_open() failed - %s",
- vcedit_error (o->state));
+ rb_raise (rb_eNoMemError, "Out of Memory");
+
+ switch (vcedit_open (o->state)) {
+ case VCEDIT_ERR_OPEN:
+ rb_raise (eOpen, "Cannot open file");
+ case VCEDIT_ERR_INVAL:
+ rb_raise (eInvalidData, "Invalid data");
+ default:
+ break;
+ }
vc = vcedit_comments (o->state);
- if (!vc)
- rb_raise (eVTError, "vcedit_comments() failed - %s",
- vcedit_error (o->state));
+
+ /* vcedit_open() succeeded, so vcedit_comments() cannot
+ * return NULL.
+ */
+ assert (vc);
/* check whether all comments are well-formed */
for (i = 0; i < vc->comments; i++) {
ptr = strchr (content, '=');
if (!ptr || ptr == content)
- rb_raise (eVTError, "malformed comment - %s", content);
+ rb_raise (eInvalidComment, "invalid comment - %s", content);
}
o->comments = rb_class_new_instance (0, NULL, cComments);
comments_sync (o->comments, o->state);
- if (vcedit_write (o->state) < 0)
- rb_raise (rb_eIOError, "write failed - %s",
- vcedit_error (o->state));
+ switch (vcedit_write (o->state)) {
+ case VCEDIT_ERR_INVAL:
+ rb_raise (eInvalidData, "Invalid data");
+ case VCEDIT_ERR_TMPFILE:
+ rb_raise (eTempFile, "Cannot create temporary file");
+ case VCEDIT_ERR_REOPEN:
+ rb_raise (eReopen, "Cannot reopen file");
+ default:
+ break;
+ }
return rb_funcall (o->comments, id_length, 0);
}
rb_define_method (cVT, "write", c_write, 0);
rb_define_method (cVT, "comments", c_comments, 0);
- eVTError = rb_define_class_under (cVT, "TaggerError", eVorbis);
+ eVT = rb_define_class_under (cVT, "TaggerError", eVorbis);
+ eOpen = rb_define_class_under (cVT, "OpenError", eVT);
+ eInvalidData = rb_define_class_under (cVT, "InvalidDataError", eVT);
+ eInvalidComment = rb_define_class_under (cVT, "InvalidCommentError",
+ eInvalidData);
+ eTempFile = rb_define_class_under (cVT, "TempFileError", eVT);
+ eReopen = rb_define_class_under (cVT, "ReopenError", eVT);
id_length = rb_intern ("length");
char filename[PATH_MAX];
FILE *in;
+ bool opened;
long serial;
unsigned char *mainbuf;
unsigned char *bookbuf;
int mainlen;
int booklen;
- const char *lasterror;
char *vendor;
int prevW;
int extrapage;
return state;
}
-const char *
-vcedit_error (vcedit_state *state)
-{
- return state->lasterror;
-}
-
vorbis_comment *
vcedit_comments (vcedit_state *state)
{
- return state->vc;
+ return state->opened ? state->vc : NULL;
}
static void
state->booklen = 0;
state->serial = 0;
+ state->opened = false;
}
void
return _fetch_next_packet (s, p, page);
}
-int
+vcedit_error
vcedit_open (vcedit_state *state)
{
+ vcedit_error ret;
char *buffer;
int bytes, i;
int chunks = 0;
ogg_page og;
state->in = fopen (state->filename, "rb");
- if (!state->in) {
- state->lasterror = "Cannot open file.";
- return -1;
- }
+ if (!state->in)
+ return VCEDIT_ERR_OPEN;
ogg_sync_init (state->oy);
/* Bail if we don't find data in the first 40 kB */
if (chunks++ >= 10) {
- if (bytes < CHUNKSIZE)
- state->lasterror = "Input truncated or empty.";
- else
- state->lasterror = "Input is not an Ogg bitstream.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
vorbis_comment_init (state->vc);
if (ogg_stream_pagein (state->os, &og) < 0) {
- state->lasterror = "Error reading first page of Ogg bitstream.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
if (ogg_stream_packetout (state->os, &header_main) != 1) {
- state->lasterror = "Error reading initial header packet.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
if (vorbis_synthesis_headerin (state->vi, state->vc, &header_main) < 0) {
- state->lasterror = "Ogg bitstream does not contain vorbis data.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
break;
if (result == -1) {
- state->lasterror = "Corrupt secondary header.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
bytes = fread (buffer, 1, CHUNKSIZE, state->in);
if (bytes == 0 && i < 2) {
- state->lasterror = "EOF before end of vorbis headers.";
+ ret = VCEDIT_ERR_INVAL;
goto err;
}
state->vendor = strdup (state->vc->vendor);
/* Headers are done! */
- return 0;
+ state->opened = true;
+
+ return VCEDIT_ERR_SUCCESS;
err:
vcedit_clear_internals (state);
- return -1;
+ return ret;
}
-int
+vcedit_error
vcedit_write (vcedit_state *state)
{
ogg_stream_state streamout;
int s, result, bytes, needflush = 0, needout = 0;
size_t tmp;
+ if (!state->opened)
+ return VCEDIT_ERR_INVAL;
+
strcpy (tmpfile, state->filename);
strcat (tmpfile, ".XXXXXX");
s = mkstemp (tmpfile);
- if (s == -1) {
- state->lasterror = "Error writing stream to output. "
- "Cannot open temporary file.";
- return -1;
- }
+ if (s == -1)
+ return VCEDIT_ERR_TMPFILE;
out = fdopen (s, "wb");
if (!out) {
unlink (tmpfile);
close (s);
- state->lasterror = "Error writing stream to output. "
- "Cannot open temporary file.";
- return -1;
+
+ return VCEDIT_ERR_TMPFILE;
}
state->prevW = state->extrapage = state->eosin = 0;
if (!result)
break;
- if (result < 0)
- state->lasterror = "Corrupt or missing data, continuing...";
- else {
+ if (result >= 0) {
/* Don't bother going through the rest, we can just
* write the page out now
*/
state->mainbuf = state->bookbuf = NULL;
- if (!state->eosin) {
- state->lasterror = "Error writing stream to output. "
- "Output stream may be corrupted or truncated.";
- return -1;
- }
+ if (!state->eosin)
+ return VCEDIT_ERR_INVAL;
vcedit_clear_internals (state);
- vcedit_open (state);
- return 0;
+ return (vcedit_open (state) == VCEDIT_ERR_SUCCESS) ?
+ VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN;
}
#include <ogg/ogg.h>
#include <vorbis/codec.h>
+typedef enum {
+ VCEDIT_ERR_SUCCESS = 0,
+ VCEDIT_ERR_OPEN,
+ VCEDIT_ERR_INVAL,
+ VCEDIT_ERR_TMPFILE,
+ VCEDIT_ERR_REOPEN
+} vcedit_error;
+
typedef struct vcedit_state_St vcedit_state;
vcedit_state *vcedit_state_new (const char *filename);
void vcedit_state_ref (vcedit_state *state);
void vcedit_state_unref (vcedit_state *state);
vorbis_comment *vcedit_comments (vcedit_state *state);
-int vcedit_open (vcedit_state *state);
-int vcedit_write (vcedit_state *state);
-const char *vcedit_error (vcedit_state *state);
+vcedit_error vcedit_open (vcedit_state *state);
+vcedit_error vcedit_write (vcedit_state *state);
#ifdef __cplusplus
}