Check the error flag when reading data.
[ruby-vorbistagger.git] / ext / vcedit.c
index 75bf80db99aac4b3205172cb5c14b520b4d9193a..cf63e7487217c7a36fcb479590328488928c7c63 100644 (file)
@@ -56,26 +56,26 @@ struct vcedit_state_St {
 };
 
 static void
-vcedit_state_free (vcedit_state *state)
+vcedit_state_free (vcedit_state *s)
 {
-       free (state->mainbuf);
-       free (state->bookbuf);
-       free (state->vendor);
+       free (s->mainbuf);
+       free (s->bookbuf);
+       free (s->vendor);
 
-       if (state->in) {
-               fclose (state->in);
-               state->in = NULL;
+       if (s->in) {
+               fclose (s->in);
+               s->in = NULL;
        }
 
-       free (state);
+       free (s);
 }
 
 static bool
-vcedit_state_init (vcedit_state *state, const char *filename)
+vcedit_state_init (vcedit_state *s, const char *filename)
 {
-       state->refcount = 1;
+       s->refcount = 1;
 
-       strcpy (state->filename, filename);
+       strcpy (s->filename, filename);
 
        return true;
 }
@@ -83,73 +83,73 @@ vcedit_state_init (vcedit_state *state, const char *filename)
 vcedit_state *
 vcedit_state_new (const char *filename)
 {
-       vcedit_state *state;
+       vcedit_state *s;
        size_t len;
 
        len = strlen (filename);
        if (len > PATH_MAX)
                return NULL;
 
-       state = malloc (sizeof (vcedit_state) + len + 1);
-       if (!state)
+       s= malloc (sizeof (vcedit_state) + len + 1);
+       if (!s)
                return NULL;
 
-       memset (state, 0, sizeof (vcedit_state));
+       memset (s, 0, sizeof (vcedit_state));
 
-       if (!vcedit_state_init (state, filename)) {
-               vcedit_state_free (state);
+       if (!vcedit_state_init (s, filename)) {
+               vcedit_state_free (s);
                return NULL;
        }
 
-       return state;
+       return s;
 }
 
 vorbis_comment *
-vcedit_comments (vcedit_state *state)
+vcedit_comments (vcedit_state *s)
 {
-       return state->opened ? &state->vc : NULL;
+       return s->opened ? &s->vc : NULL;
 }
 
 static void
-vcedit_clear_internals (vcedit_state *state)
+vcedit_clear_internals (vcedit_state *s)
 {
-       ogg_stream_clear (&state->os);
-       ogg_sync_clear (&state->oy);
+       ogg_stream_clear (&s->os);
+       ogg_sync_clear (&s->oy);
 
-       vorbis_info_clear (&state->vi);
-       vorbis_comment_clear (&state->vc);
+       vorbis_info_clear (&s->vi);
+       vorbis_comment_clear (&s->vc);
 
-       free (state->vendor);
-       state->vendor = NULL;
+       free (s->vendor);
+       s->vendor = NULL;
 
-       free (state->mainbuf);
-       state->mainbuf = NULL;
-       state->mainlen = 0;
+       free (s->mainbuf);
+       s->mainbuf = NULL;
+       s->mainlen = 0;
 
-       free (state->bookbuf);
-       state->bookbuf = NULL;
-       state->booklen = 0;
+       free (s->bookbuf);
+       s->bookbuf = NULL;
+       s->booklen = 0;
 
-       state->serial = 0;
-       state->opened = false;
+       s->serial = 0;
+       s->opened = false;
 }
 
 void
-vcedit_state_ref (vcedit_state *state)
+vcedit_state_ref (vcedit_state *s)
 {
-       state->refcount++;
+       s->refcount++;
 }
 
 void
-vcedit_state_unref (vcedit_state *state)
+vcedit_state_unref (vcedit_state *s)
 {
-       if (--state->refcount)
+       if (--s->refcount)
                return;
 
-       if (state->opened)
-               vcedit_clear_internals (state);
+       if (s->opened)
+               vcedit_clear_internals (s);
 
-       vcedit_state_free (state);
+       vcedit_state_free (s);
 }
 
 /* Next two functions pulled straight from libvorbis, apart from one change
@@ -240,7 +240,7 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
                bytes = fread (buffer, 1, CHUNKSIZE, s->in);
                ogg_sync_wrote (&s->oy, bytes);
 
-               if (!bytes && feof (s->in))
+               if (!bytes && (feof (s->in) || ferror (s->in)))
                        return 0;
        }
 
@@ -258,7 +258,7 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
 }
 
 vcedit_error
-vcedit_open (vcedit_state *state)
+vcedit_open (vcedit_state *s)
 {
        vcedit_error ret;
        ogg_packet *header;
@@ -268,70 +268,75 @@ vcedit_open (vcedit_state *state)
        size_t bytes, total = 0;
        int i = 0;
 
-       state->in = fopen (state->filename, "rb");
-       if (!state->in)
+       s->in = fopen (s->filename, "rb");
+       if (!s->in)
                return VCEDIT_ERR_OPEN;
 
-       ogg_sync_init (&state->oy);
+       ogg_sync_init (&s->oy);
 
        do {
                /* Bail if we don't find data in the first 40 kB */
-               if (feof (state->in) || total >= (CHUNKSIZE * 10)) {
-                       ogg_sync_clear (&state->oy);
+               if (feof (s->in) || ferror (s->in) || total >= (CHUNKSIZE * 10)) {
+                       ogg_sync_clear (&s->oy);
 
                        return VCEDIT_ERR_INVAL;
                }
 
-               buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
+               buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
 
-               bytes = fread (buffer, 1, CHUNKSIZE, state->in);
+               bytes = fread (buffer, 1, CHUNKSIZE, s->in);
                total += bytes;
 
-               ogg_sync_wrote (&state->oy, bytes);
-       } while (ogg_sync_pageout (&state->oy, &og) != 1);
+               ogg_sync_wrote (&s->oy, bytes);
+       } while (ogg_sync_pageout (&s->oy, &og) != 1);
 
-       state->serial = ogg_page_serialno (&og);
+       s->serial = ogg_page_serialno (&og);
 
-       ogg_stream_init (&state->os, state->serial);
-       vorbis_info_init (&state->vi);
-       vorbis_comment_init (&state->vc);
+       ogg_stream_init (&s->os, s->serial);
+       vorbis_info_init (&s->vi);
+       vorbis_comment_init (&s->vc);
 
-       if (ogg_stream_pagein (&state->os, &og) < 0) {
+       if (ogg_stream_pagein (&s->os, &og) < 0) {
                ret = VCEDIT_ERR_INVAL;
                goto err;
        }
 
-       if (ogg_stream_packetout (&state->os, &header_main) != 1) {
+       if (ogg_stream_packetout (&s->os, &header_main) != 1) {
                ret = VCEDIT_ERR_INVAL;
                goto err;
        }
 
-       if (vorbis_synthesis_headerin (&state->vi, &state->vc, &header_main) < 0) {
+       if (vorbis_synthesis_headerin (&s->vi, &s->vc, &header_main) < 0) {
                ret = VCEDIT_ERR_INVAL;
                goto err;
        }
 
-       state->mainlen = header_main.bytes;
-       state->mainbuf = malloc (state->mainlen);
-       memcpy (state->mainbuf, header_main.packet, header_main.bytes);
+       s->mainlen = header_main.bytes;
+       s->mainbuf = malloc (s->mainlen);
+       memcpy (s->mainbuf, header_main.packet, header_main.bytes);
 
        header = &header_comments;
 
        while (i < 2) {
+               if (feof (s->in) || ferror (s->in)) {
+                       ret = VCEDIT_ERR_INVAL;
+                       goto err;
+               }
+
                while (i < 2) {
                        int result;
 
-                       result = ogg_sync_pageout (&state->oy, &og);
+                       result = ogg_sync_pageout (&s->oy, &og);
                        if (!result)
                                break; /* Too little data so far */
 
                        if (result != 1)
                                continue;
 
-                       ogg_stream_pagein (&state->os, &og);
+                       ogg_stream_pagein (&s->os, &og);
 
                        while (i < 2) {
-                               result = ogg_stream_packetout (&state->os, header);
+                               result = ogg_stream_packetout (&s->os, header);
                                if (!result)
                                        break;
 
@@ -340,45 +345,57 @@ vcedit_open (vcedit_state *state)
                                        goto err;
                                }
 
-                               vorbis_synthesis_headerin (&state->vi, &state->vc, header);
+                               vorbis_synthesis_headerin (&s->vi, &s->vc, header);
 
                                if (i++ == 1) {
-                                       state->booklen = header->bytes;
-                                       state->bookbuf = malloc (state->booklen);
-                                       memcpy (state->bookbuf, header->packet, header->bytes);
+                                       s->booklen = header->bytes;
+                                       s->bookbuf = malloc (s->booklen);
+                                       memcpy (s->bookbuf, header->packet, header->bytes);
                                }
 
                                header = &header_codebooks;
                        }
                }
 
-               buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
-               bytes = fread (buffer, 1, CHUNKSIZE, state->in);
-
-               if (!bytes && feof (state->in) && i < 2) {
-                       ret = VCEDIT_ERR_INVAL;
-                       goto err;
-               }
+               buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
 
-               ogg_sync_wrote (&state->oy, bytes);
+               bytes = fread (buffer, 1, CHUNKSIZE, s->in);
+               ogg_sync_wrote (&s->oy, bytes);
        }
 
        /* Copy the vendor tag */
-       state->vendor = strdup (state->vc.vendor);
+       s->vendor = strdup (s->vc.vendor);
 
        /* Headers are done! */
-       state->opened = true;
+       s->opened = true;
 
        return VCEDIT_ERR_SUCCESS;
 
 err:
-       vcedit_clear_internals (state);
+       vcedit_clear_internals (s);
 
        return ret;
 }
 
+static int
+write_data (const void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+       while (nmemb > 0) {
+               size_t w;
+
+               w = fwrite (buf, size, nmemb, stream);
+               if (!w && ferror (stream))
+                       return 0;
+
+               nmemb -= w;
+               buf += size * w;
+       }
+
+       return 1;
+}
+
 vcedit_error
-vcedit_write (vcedit_state *state)
+vcedit_write (vcedit_state *s)
 {
        ogg_stream_state streamout;
        ogg_packet header_main, header_comments, header_codebooks, op;
@@ -386,83 +403,76 @@ vcedit_write (vcedit_state *state)
        ogg_int64_t granpos = 0;
        FILE *out;
        char *buffer, tmpfile[PATH_MAX];
-       int s, result, bytes, needflush = 0, needout = 0;
-       size_t tmp;
+       int fd, result, bytes, needflush = 0, needout = 0;
 
-       if (!state->opened)
+       if (!s->opened)
                return VCEDIT_ERR_INVAL;
 
-       strcpy (tmpfile, state->filename);
+       strcpy (tmpfile, s->filename);
        strcat (tmpfile, ".XXXXXX");
 
-       s = mkstemp (tmpfile);
-       if (s == -1)
+       fd = mkstemp (tmpfile);
+       if (fd == -1)
                return VCEDIT_ERR_TMPFILE;
 
-       out = fdopen (s, "wb");
+       out = fdopen (fd, "wb");
        if (!out) {
                unlink (tmpfile);
-               close (s);
+               close (fd);
 
                return VCEDIT_ERR_TMPFILE;
        }
 
-       state->prevW = state->extrapage = state->eosin = 0;
+       s->prevW = s->extrapage = s->eosin = 0;
 
-       header_main.bytes = state->mainlen;
-       header_main.packet = state->mainbuf;
+       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;
 
-       header_codebooks.bytes = state->booklen;
-       header_codebooks.packet = state->bookbuf;
+       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;
 
-       ogg_stream_init (&streamout, state->serial);
+       ogg_stream_init (&streamout, s->serial);
 
-       _commentheader_out (&state->vc, state->vendor, &header_comments);
+       _commentheader_out (&s->vc, s->vendor, &header_comments);
 
        ogg_stream_packetin (&streamout, &header_main);
        ogg_stream_packetin (&streamout, &header_comments);
        ogg_stream_packetin (&streamout, &header_codebooks);
 
        while ((result = ogg_stream_flush (&streamout, &ogout))) {
-               tmp = fwrite (ogout.header, 1, ogout.header_len, out);
-               if (tmp != (size_t) ogout.header_len)
+               if (!write_data (ogout.header, 1, ogout.header_len, out))
                        goto cleanup;
 
-               tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-               if (tmp != (size_t) ogout.body_len)
+               if (!write_data (ogout.body, 1, ogout.body_len, out))
                        goto cleanup;
        }
 
-       while (_fetch_next_packet (state, &op, &ogin)) {
+       while (_fetch_next_packet (s, &op, &ogin)) {
                int size;
 
-               size = _blocksize (state, &op);
+               size = _blocksize (s, &op);
                granpos += size;
 
                if (needflush) {
                        if (ogg_stream_flush (&streamout, &ogout)) {
-                               tmp = fwrite (ogout.header, 1, ogout.header_len, out);
-                               if (tmp != (size_t) ogout.header_len)
+                               if (!write_data (ogout.header, 1, ogout.header_len, out))
                                        goto cleanup;
 
-                               tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-                               if (tmp != (size_t) ogout.body_len)
+                               if (!write_data (ogout.body, 1, ogout.body_len, out))
                                        goto cleanup;
                        }
                } else if (needout) {
                        if (ogg_stream_pageout (&streamout, &ogout)) {
-                               tmp = fwrite (ogout.header, 1, ogout.header_len, out);
-                               if (tmp != (size_t) ogout.header_len)
+                               if (!write_data (ogout.header, 1, ogout.header_len, out))
                                        goto cleanup;
 
-                               tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-                               if (tmp != (size_t) ogout.body_len)
+                               if (!write_data (ogout.body, 1, ogout.body_len, out))
                                        goto cleanup;
                        }
                }
@@ -490,60 +500,57 @@ vcedit_write (vcedit_state *state)
        streamout.e_o_s = 1;
 
        while (ogg_stream_flush (&streamout, &ogout)) {
-               tmp = fwrite (ogout.header, 1, ogout.header_len, out);
-               if (tmp != (size_t) ogout.header_len)
+               if (!write_data (ogout.header, 1, ogout.header_len, out))
                        goto cleanup;
 
-               tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-               if (tmp != (size_t) ogout.body_len)
+               if (!write_data (ogout.body, 1, ogout.body_len, out))
                        goto cleanup;
        }
 
-       if (state->extrapage) {
-               tmp = fwrite (ogin.header, 1, ogin.header_len, out);
-               if (tmp != (size_t) ogin.header_len)
+       if (s->extrapage) {
+               if (!write_data (ogin.header, 1, ogin.header_len, out))
                        goto cleanup;
 
-               tmp = fwrite (ogin.body, 1, ogin.body_len, out);
-               if (tmp != (size_t) ogin.body_len)
+               if (!write_data (ogin.body, 1, ogin.body_len, out))
                        goto cleanup;
        }
 
        /* clear it, because not all paths to here do */
-       state->eosin = 0;
+       s->eosin = 0;
 
        do {
                /* We copy the rest of the stream (other logical streams)
                 * through, a page at a time.
                 */
-               while ((result = ogg_sync_pageout (&state->oy, &ogout))) {
+               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
                         */
-                       tmp = fwrite (ogout.header, 1, ogout.header_len, out);
-                       if (tmp != (size_t) ogout.header_len)
+                       if (!write_data (ogout.header, 1, ogout.header_len, out))
                                goto cleanup;
 
-                       tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-                       if (tmp != (size_t) ogout.body_len)
+                       if (!write_data (ogout.body, 1, ogout.body_len, out))
                                goto cleanup;
                }
 
-               buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
-               bytes = fread (buffer, 1, CHUNKSIZE, state->in);
-               ogg_sync_wrote (&state->oy, bytes);
+               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;
 
-               state->eosin = !bytes && feof (state->in);
-       } while (!state->eosin);
+               s->eosin = !bytes && feof (s->in);
+       } while (!s->eosin);
 
        fclose (out);
-       fclose (state->in);
+       fclose (s->in);
 
-       unlink (state->filename);
-       rename (tmpfile, state->filename);
+       unlink (s->filename);
+       rename (tmpfile, s->filename);
 
 cleanup:
        ogg_stream_clear (&streamout);
@@ -553,16 +560,16 @@ cleanup:
         */
     _ogg_free (header_comments.packet);
 
-       free (state->mainbuf);
-       free (state->bookbuf);
+       free (s->mainbuf);
+       free (s->bookbuf);
 
-    state->mainbuf = state->bookbuf = NULL;
+    s->mainbuf = s->bookbuf = NULL;
 
-       if (!state->eosin)
+       if (!s->eosin)
                return VCEDIT_ERR_INVAL;
 
-       vcedit_clear_internals (state);
+       vcedit_clear_internals (s);
 
-       return (vcedit_open (state) == VCEDIT_ERR_SUCCESS) ?
+       return (vcedit_open (s) == VCEDIT_ERR_SUCCESS) ?
               VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN;
 }