Keep the original file mode when writing files.
[ruby-vorbistagger.git] / ext / vcedit.c
index cf63e7487217c7a36fcb479590328488928c7c63..414d6d060f31c0d98f6b3a4a12dfa18748e79904 100644 (file)
@@ -23,6 +23,8 @@
 #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>
@@ -41,6 +43,8 @@ struct vcedit_state_St {
        vorbis_info vi;
 
        FILE *in;
+       mode_t file_mode;
+
        bool opened;
        long serial;
        unsigned char *mainbuf;
@@ -163,11 +167,11 @@ _v_writestring (oggpack_buffer *o, char *s, int len)
 }
 
 static int
-_commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op)
+_commentheader_out (vcedit_state *s, ogg_packet *op)
 {
-       int i;
-
        oggpack_buffer opb;
+       size_t len;
+       int i;
 
        oggpack_writeinit (&opb);
 
@@ -176,19 +180,20 @@ _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op)
        _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]);
                }
        }
 
@@ -264,6 +269,7 @@ vcedit_open (vcedit_state *s)
        ogg_packet *header;
        ogg_packet header_main, header_comments, header_codebooks;
        ogg_page og;
+       struct stat st;
        char *buffer;
        size_t bytes, total = 0;
        int i = 0;
@@ -272,6 +278,8 @@ vcedit_open (vcedit_state *s)
        if (!s->in)
                return VCEDIT_ERR_OPEN;
 
+       s->file_mode = stat (s->filename, &st) ? 0664 : st.st_mode;
+
        ogg_sync_init (&s->oy);
 
        do {
@@ -403,6 +411,7 @@ vcedit_write (vcedit_state *s)
        ogg_int64_t granpos = 0;
        FILE *out;
        char *buffer, tmpfile[PATH_MAX];
+       bool success = false;
        int fd, result, bytes, needflush = 0, needout = 0;
 
        if (!s->opened)
@@ -439,7 +448,7 @@ vcedit_write (vcedit_state *s)
 
        ogg_stream_init (&streamout, s->serial);
 
-       _commentheader_out (&s->vc, s->vendor, &header_comments);
+       _commentheader_out (s, &header_comments);
 
        ogg_stream_packetin (&streamout, &header_main);
        ogg_stream_packetin (&streamout, &header_comments);
@@ -546,13 +555,21 @@ vcedit_write (vcedit_state *s)
                s->eosin = !bytes && feof (s->in);
        } while (!s->eosin);
 
-       fclose (out);
+       success = true;
+
+cleanup:
        fclose (s->in);
 
-       unlink (s->filename);
-       rename (tmpfile, s->filename);
+       if (!success) {
+               unlink (tmpfile);
+               fclose (out);
+       } else {
+               fclose (out);
+               unlink (s->filename);
+               rename (tmpfile, s->filename);
+               chmod (s->filename, s->file_mode);
+       }
 
-cleanup:
        ogg_stream_clear (&streamout);
 
     /* We don't ogg_packet_clear() this, because the memory was