[patch] [libid3tag] handle general case when writing id3v2 tags

Sam Clegg <[email protected]> Sat, 30 Oct 2004 14:49:17 +0100
Newsgroups gmane.comp.audio.mad.devel
Message-ID <20041030134917.GA20393@magicboy>
I can remember whether I have submitted this patch before but
I've had it knocking around for while.

The patch is against 0.15.1b in debian/sid.

It works for me and I would like to get it included in libid3tag
as the current version will silently fail to write updated tags
that are not the same size as the original.

I've made rather crude use of malloc to handle moving the audio
data to accommodate the new tag.  If you'd rather I can rewrite
it to use iteration and a fixed size buffer.

It also relies on ftruncate for the case when a tag shrinks
which i'm guessing will require a windows alternative.

cheers,
sam
-- 
sam clegg
:: [email protected] :: http://superduper.net/ :: PGP : D91EE369 
$superduper: .signature,v 1.13 2003/06/17 10:29:24 sam Exp $
write_id3v2.diff (text/plain, 2 KB)
--- file.c	2004-01-23 09:41:32.000000000 +0000
+++ new_file.c	2004-10-30 14:34:24.000000000 +0100
@@ -577,10 +577,14 @@
 {
   assert(!data || length > 0);
 
-  if (data &&
-      ((file->ntags == 1 && !(file->flags & ID3_FILE_FLAG_ID3V1)) ||
-       (file->ntags == 2 &&  (file->flags & ID3_FILE_FLAG_ID3V1))) &&
-      file->tags[0].length == length) {
+  if (!data
+      ||  (!(file->ntags == 1 && !(file->flags & ID3_FILE_FLAG_ID3V1)) && 
+           !(file->ntags == 2 &&  (file->flags & ID3_FILE_FLAG_ID3V1)))) {
+    /* no v2 tag. nothing to do */
+    goto done;
+  }
+
+  if (file->tags[0].length == length) {
     /* easy special case: rewrite existing tag in-place */
 
     if (fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 ||
@@ -589,11 +593,46 @@
       return -1;
 
     goto done;
+  } else {
+    /* the new tag has a different size */
+    int file_size;
+    int remainder_size;
+    char *remainder;
+
+    /* read in the remainder of the file */
+    fseek(file->iofile, 0, SEEK_END);
+    file_size = ftell(file->iofile);
+    remainder_size = file_size - file->tags[0].location - file->tags[0].length;
+    remainder = (char*)malloc(remainder_size);
+    if (fseek(file->iofile, file->tags[0].location + file->tags[0].length, SEEK_SET) == -1 ||
+	fread(remainder, remainder_size, 1, file->iofile) != 1) {
+      free(remainder);
+      return -1;
+    }
+
+    /* write the tag where the old one was */
+    if (fseek(file->iofile, file->tags[0].location, SEEK_SET) == -1 ||
+	fwrite(data, length, 1, file->iofile) != 1) {
+      free(remainder);
+      return -1;
+    }
+
+    /* write the reaminder */
+    if (fwrite(remainder, remainder_size, 1, file->iofile) != 1) {
+      free(remainder);
+      return -1;
   }
 
-  /* hard general case: rewrite entire file */
+    free(remainder);
 
-  /* ... */
+    /* flush the FILE */
+    if (fflush(file->iofile) == EOF)
+      return -1;
+
+    /* truncate if required */
+    if (ftell(file->iofile) < file_size)
+      ftruncate(fileno(file->iofile), ftell(file->iofile));
+  }
 
  done:
   return 0;
signature.asc (application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)

iD8DBQFBg5vcLOvxONke42kRAjMgAKCh0sWF1TKLuf6XjcaoRVO+1eSf8QCfRjnf
lv4JLXL4ay/jBTA0kmXflfM=
=hGMd
-----END PGP SIGNATURE-----