Handle short writes gracefully.
[ruby-vorbistagger.git] / ext / vcedit.c
index dfad95caf8a807bb5dee614e2590cfa567eaa726..b9650f8a157ed395281fece20874a1c24165b9df 100644 (file)
@@ -71,10 +71,12 @@ vcedit_state_free (vcedit_state *state)
 }
 
 static bool
-vcedit_state_init (vcedit_state *state)
+vcedit_state_init (vcedit_state *state, const char *filename)
 {
        state->refcount = 1;
 
+       strcpy (state->filename, filename);
+
        return true;
 }
 
@@ -85,6 +87,8 @@ vcedit_state_new (const char *filename)
        size_t len;
 
        len = strlen (filename);
+       if (len > PATH_MAX)
+               return NULL;
 
        state = malloc (sizeof (vcedit_state) + len + 1);
        if (!state)
@@ -92,13 +96,11 @@ vcedit_state_new (const char *filename)
 
        memset (state, 0, sizeof (vcedit_state));
 
-       if (!vcedit_state_init (state)) {
+       if (!vcedit_state_init (state, filename)) {
                vcedit_state_free (state);
                return NULL;
        }
 
-       strcpy (state->filename, filename);
-
        return state;
 }
 
@@ -156,9 +158,8 @@ vcedit_state_unref (vcedit_state *state)
 static void
 _v_writestring (oggpack_buffer *o, char *s, int len)
 {
-       while (len--) {
+       while (len--)
                oggpack_write (o, *s++, 8);
-       }
 }
 
 static int
@@ -228,19 +229,18 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
        int result, bytes;
 
        result = ogg_stream_packetout (&s->os, p);
-
-       if (result > 0)
+       if (result == 1)
                return 1;
 
        if (s->eosin)
                return 0;
 
-       while (ogg_sync_pageout (&s->oy, page) <= 0) {
+       while (ogg_sync_pageout (&s->oy, page) != 1) {
                buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE);
                bytes = fread (buffer, 1, CHUNKSIZE, s->in);
                ogg_sync_wrote (&s->oy, bytes);
 
-               if (!bytes)
+               if (!bytes && feof (s->in))
                        return 0;
        }
 
@@ -261,12 +261,12 @@ vcedit_error
 vcedit_open (vcedit_state *state)
 {
        vcedit_error ret;
-       char *buffer;
-       int bytes, i;
-       int chunks = 0;
        ogg_packet *header;
        ogg_packet header_main, header_comments, header_codebooks;
        ogg_page og;
+       char *buffer;
+       size_t bytes, total = 0;
+       int i = 0;
 
        state->in = fopen (state->filename, "rb");
        if (!state->in)
@@ -274,22 +274,21 @@ vcedit_open (vcedit_state *state)
 
        ogg_sync_init (&state->oy);
 
-       while (1) {
-               buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
-               bytes = fread (buffer, 1, CHUNKSIZE, state->in);
-
-               ogg_sync_wrote (&state->oy, bytes);
-
-               if (ogg_sync_pageout (&state->oy, &og) == 1)
-                       break;
-
+       do {
                /* Bail if we don't find data in the first 40 kB */
-               if (chunks++ >= 10) {
+               if (feof (state->in) || total >= (CHUNKSIZE * 10)) {
                        ogg_sync_clear (&state->oy);
 
                        return VCEDIT_ERR_INVAL;
                }
-       }
+
+               buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
+
+               bytes = fread (buffer, 1, CHUNKSIZE, state->in);
+               total += bytes;
+
+               ogg_sync_wrote (&state->oy, bytes);
+       } while (ogg_sync_pageout (&state->oy, &og) != 1);
 
        state->serial = ogg_page_serialno (&og);
 
@@ -316,48 +315,47 @@ vcedit_open (vcedit_state *state)
        state->mainbuf = malloc (state->mainlen);
        memcpy (state->mainbuf, header_main.packet, header_main.bytes);
 
-       i = 0;
        header = &header_comments;
 
        while (i < 2) {
                while (i < 2) {
-                       int result = ogg_sync_pageout (&state->oy, &og);
+                       int result;
 
+                       result = ogg_sync_pageout (&state->oy, &og);
                        if (!result)
                                break; /* Too little data so far */
 
-                       if (result == 1) {
-                               ogg_stream_pagein (&state->os, &og);
-
-                               while (i < 2) {
-                                       result = ogg_stream_packetout (&state->os, header);
+                       if (result != 1)
+                               continue;
 
-                                       if (!result)
-                                               break;
+                       ogg_stream_pagein (&state->os, &og);
 
-                                       if (result == -1) {
-                                               ret = VCEDIT_ERR_INVAL;
-                                               goto err;
-                                       }
+                       while (i < 2) {
+                               result = ogg_stream_packetout (&state->os, header);
+                               if (!result)
+                                       break;
 
-                                       vorbis_synthesis_headerin (&state->vi, &state->vc, header);
+                               if (result != 1) {
+                                       ret = VCEDIT_ERR_INVAL;
+                                       goto err;
+                               }
 
-                                       if (i == 1) {
-                                               state->booklen = header->bytes;
-                                               state->bookbuf = malloc (state->booklen);
-                                               memcpy (state->bookbuf, header->packet, header->bytes);
-                                       }
+                               vorbis_synthesis_headerin (&state->vi, &state->vc, header);
 
-                                       i++;
-                                       header = &header_codebooks;
+                               if (i++ == 1) {
+                                       state->booklen = header->bytes;
+                                       state->bookbuf = malloc (state->booklen);
+                                       memcpy (state->bookbuf, header->packet, header->bytes);
                                }
+
+                               header = &header_codebooks;
                        }
                }
 
                buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE);
                bytes = fread (buffer, 1, CHUNKSIZE, state->in);
 
-               if (bytes == 0 && i < 2) {
+               if (!bytes && feof (state->in) && i < 2) {
                        ret = VCEDIT_ERR_INVAL;
                        goto err;
                }
@@ -379,6 +377,23 @@ err:
        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)
 {
@@ -389,7 +404,6 @@ vcedit_write (vcedit_state *state)
        FILE *out;
        char *buffer, tmpfile[PATH_MAX];
        int s, result, bytes, needflush = 0, needout = 0;
-       size_t tmp;
 
        if (!state->opened)
                return VCEDIT_ERR_INVAL;
@@ -432,12 +446,10 @@ vcedit_write (vcedit_state *state)
        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;
        }
 
@@ -449,22 +461,18 @@ vcedit_write (vcedit_state *state)
 
                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;
                        }
                }
@@ -492,61 +500,48 @@ 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 (!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;
 
-       while (!state->eosin) { /* We reached eos, not eof */
+       do {
                /* We copy the rest of the stream (other logical streams)
                 * through, a page at a time.
                 */
-               while (1) {
-                       result = ogg_sync_pageout (&state->oy, &ogout);
+               while ((result = ogg_sync_pageout (&state->oy, &ogout))) {
+                       if (result != 1)
+                               continue;
 
-                       if (!result)
-                break;
-
-                       if (result >= 0) {
-                               /* 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)
-                                       goto cleanup;
+                       /* 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))
+                               goto cleanup;
 
-                               tmp = fwrite (ogout.body, 1, ogout.body_len, out);
-                               if (tmp != (size_t) ogout.body_len)
-                                       goto cleanup;
-                       }
+                       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);
 
-               if (!bytes) {
-                       state->eosin = 1;
-                       break;
-               }
-       }
+               state->eosin = !bytes && feof (state->in);
+       } while (!state->eosin);
 
        fclose (out);
        fclose (state->in);