diff --git a/vorbiscomment/vcedit.c b/vorbiscomment/vcedit.c
index b46dfb6..82ca5eb 100644
--- a/vorbiscomment/vcedit.c
+++ b/vorbiscomment/vcedit.c
@@ -9,25 +9,6 @@
  * last modified: $Id: vcedit.c,v 1.23 2003/09/03 07:58:05 calc Exp $
  */
 
-/* Handle muxed streams and the Vorbis renormalization without having
- * to understand remuxing:
- * Linked list of buffers (buffer_chain).  Start a link and whenever
- * you encounter an unknown page from the current stream (ie we found
- * its bos in the bos section) push it onto the current buffer.  Whenever
- * you encounter the stream being renormalized create a new link in the
- * chain.
- * On writing, write the contents of the first link before every Vorbis
- * page written, and move to the next link.  Assuming the Vorbis pages
- * in match vorbis pages out, the order of pages from different logical
- * streams will be unchanged.
- * Special case: header. After writing the vorbis headers, and before
- * starting renormalization, flush accumulated links (takes care of
- * situations where number of secondary vorbis header pages changes due
- * to remuxing.  Similarly flush links at the end of renormalization
- * and before the start of the next chain is written.
- *
- */
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -47,119 +28,6 @@
 #define CHUNKSIZE 4096
 #define BUFFERCHUNK CHUNKSIZE
 
-/* Helper function, shouldn't need to call directly */
-static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) {
-	int result=0;
-	char *tmp;
-	vcedit_page_buffer *buffer;
-	
-	buffer = &bufferlink->buffer;
-	tmp = realloc(buffer->data,
-		      buffer->data_len + og->header_len + og->body_len);
-	if(tmp) {
-		buffer->data = tmp;
-		memcpy(buffer->data + buffer->data_len, og->header,
-		       og->header_len);
-		buffer->data_len += og->header_len;
-		memcpy(buffer->data + buffer->data_len, og->body,
-	       og->body_len);
-		result = 1;
-		buffer->data_len += og->body_len;
-	} else {
-		result = -1;
-	}
-	
-	return result;
-}
-
-/* Write and free the first link using callbacks */
-static int buffer_chain_writelink(vcedit_state *state, void *out) {
-	int result = 0;
-	vcedit_buffer_chain *tmpchain;
-	vcedit_page_buffer *tmpbuffer;
-
-	tmpchain = state->sidebuf;
-	tmpbuffer = &tmpchain->buffer;
-	if(tmpbuffer->data_len)
-	{
-		if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) !=
-		   (size_t) tmpbuffer->data_len)
-			result = -1;
-		else 
-			result = 1;
-	}
-
-	free(tmpbuffer->data);
-	state->sidebuf = tmpchain->next;
-	free(tmpchain);
-	return result;
-}
-
-
-static int buffer_chain_newlink(vcedit_state *state) {
-	int result = 1;
-	vcedit_buffer_chain *bufferlink;
-	
-	if(!state->sidebuf) {
-		state->sidebuf = malloc (sizeof *state->sidebuf);
-		if(state->sidebuf) {
-			bufferlink = state->sidebuf;
-		} else {
-			result = -1;
-		}
-	} else {
-		bufferlink=state->sidebuf;
-		while(bufferlink->next) {
-			bufferlink = bufferlink->next;
-		}
-		bufferlink->next =  malloc (sizeof *bufferlink->next);
-		if(bufferlink->next) {
-			bufferlink = bufferlink->next;
-		} else {
-			result = -1;
-		}
-	}
-
-	if(result > 0 ) {
-		bufferlink->next = 0;
-		bufferlink->buffer.data = 0;
-		bufferlink->buffer.data_len = 0;
-	}
-	else 
-		state->lasterror =
-			_("Couldn't get enough memory for input buffering.");
-
-  return result;
-}
-
-
-/* Push page onto the end of the buffer chain */
-static int buffer_chain_push(vcedit_state *state, ogg_page *og) {
-	/* If there is no sidebuffer yet we need to create one, otherwise
-	 * traverse to the last buffer and push the new page onto it. */
-	int result=1;
-	vcedit_buffer_chain *bufferlink;
-	if(!state->sidebuf) {
-		result = buffer_chain_newlink(state);
-	}
-	
-	if(result > 0) {
-		bufferlink = state->sidebuf;
-		while(bufferlink->next) {
-			bufferlink = bufferlink->next;
-	}
-		result = page_buffer_push(bufferlink, og);
-	}
-
-	if(result < 0)
-		state->lasterror =
-			_("Couldn't get enough memory for input buffering.");
-
-	return result;
-}
-
-
-
 static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) {
 	ogg_stream_state os;
 	vorbis_info vi;
@@ -203,75 +71,13 @@ static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) {
         return result;
 }
 
-
-static int vcedit_contains_serial (vcedit_state *state, int serialno) {
-	int result = 0;
-	size_t count;
-	for( count=0; count < state->serials.streams_len; count++ ) {
-		if ( *(state->serials.streams + count ) == serialno )
-			result = 1;
-	}
-	
-	return result;
-}
-
-
-static int vcedit_add_serial (vcedit_state *state, long serial) {
-        int result = 0;
-        long *tmp;
-	
-	
-	if( vcedit_contains_serial(state, serial) )
-        {
-		result = 1;
-	} else {
-		tmp   = realloc(state->serials.streams,
-				(state->serials.streams_len + 1) * sizeof *tmp);
-		if(tmp) {
-			state->serials.streams = tmp;
-			*(state->serials.streams +
-			  state->serials.streams_len) = serial;
-			state->serials.streams_len += 1;
-			result = 1;
-		} else {
-			state->lasterror =
-				_("Couldn't get enough memory to register new stream serial number.");
-		        result = -1;
-		}
-	} 
-	return result;
-}
-
-
-/* For the benefit of the secondary header read only.  Quietly creates
- * newlinks and pushes pages onto the buffer in the right way */
-static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) {
-	int result = 0;
-	int pageout_result;
-	pageout_result = ogg_sync_pageout(state->oy, og);
-	if(pageout_result > 0)
-	{
-		if(state->serial == ogg_page_serialno(og))
-			result = buffer_chain_newlink(state);
-		else
-			result = buffer_chain_push(state, og);
-	} else if (pageout_result < 0) {
-		/* Vorbis comment traditionally ignores the not-synced
-		 * error from pageout, so give it a different code. */
-		result = -2;
-	}
-	return result;
-}
-
-
 /* (I'm paranoid about memset(x,0,len) not giving null pointers */
 vcedit_state *vcedit_new_state(void) {
 	vcedit_state *state = malloc(sizeof(vcedit_state));
 	if(state) {
 		memset(state, 0, sizeof(vcedit_state));
-		state->sidebuf = 0;
-		state->serials.streams = 0;
-		state->serials.streams_len = 0;
+		state->saved_pages = NULL;
+		state->padding_blocksize = -1;
 	}
 	return state;
 }
@@ -284,6 +90,10 @@ vorbis_comment *vcedit_comments(vcedit_state *state) {
 	return state->vc;
 }
 
+void vcedit_set_padding(vcedit_state *state, int padding_blocksize) {
+	state->padding_blocksize = padding_blocksize;
+}
+
 static void vcedit_clear_internals(vcedit_state *state) {
     char *tmp;
 	if(state->vc) {
@@ -298,22 +108,8 @@ static void vcedit_clear_internals(vcedit_state *state) {
 		ogg_sync_clear(state->oy);
 		free(state->oy);
 	}
-	if(state->serials.streams_len) {
-		free(state->serials.streams);
-		state->serials.streams_len = 0;
-		state->serials.streams = 0;
-	}
-	while(state->sidebuf) {
-		vcedit_buffer_chain *tmpbuffer;
-		tmpbuffer = state->sidebuf;
-		state->sidebuf = tmpbuffer->next;
-		free(tmpbuffer->buffer.data);
-		free(tmpbuffer);
-	}
 	if(state->vendor)
 		free(state->vendor);
-    if(state->mainbuf)
-        free(state->mainbuf);
     if(state->bookbuf)
         free(state->bookbuf);
     if(state->vi) {
@@ -321,6 +117,15 @@ static void vcedit_clear_internals(vcedit_state *state) {
         free(state->vi);
     }
 
+	while(state->saved_pages != NULL)
+	{
+		saved_page *tmp = state->saved_pages;
+		state->saved_pages = tmp->next;
+		free(tmp->page.header);
+		free(tmp->page.body);
+		free(tmp);
+	}
+
     tmp = state->lasterror;
     memset(state, 0, sizeof(*state));
     state->lasterror = tmp;
@@ -335,8 +140,9 @@ void vcedit_clear(vcedit_state *state)
 	}
 }
 
-/* Next two functions pulled straight from libvorbis, apart from one change
+/* Next two functions pulled straight from libvorbis, apart from these changes:
  * - we don't want to overwrite the vendor string.
+ * - padding can be added to the end of the comment.
  */
 static void _v_writestring(oggpack_buffer *o,char *s, int len)
 {
@@ -346,8 +152,10 @@ static void _v_writestring(oggpack_buffer *o,char *s, int len)
 	}
 }
 
-static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
+static int _commentheader_out(vorbis_comment *vc, char *vendor,
+		ogg_packet *op, int padding_blocksize)
 {
+	long size, padding = 0;
 	oggpack_buffer opb;
 
 	oggpack_writeinit(&opb);
@@ -376,10 +184,20 @@ static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
 	}
 	oggpack_write(&opb,1,1);
 
-	op->packet = malloc(oggpack_bytes(&opb));
-	memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
+	size = oggpack_bytes(&opb);
+	if(padding_blocksize)
+	{
+		const int min_padding = padding_blocksize / 2;
+		
+		padding = size + (padding_blocksize - 1) + min_padding;
+		padding -= (padding % padding_blocksize) + size;
+	}
+
+	op->packet = malloc(size + padding);
+	memcpy(op->packet, opb.buffer, size);
+	memset(op->packet + size, 0, padding);
 
-	op->bytes=oggpack_bytes(&opb);
+	op->bytes=size+padding;
 	op->b_o_s=0;
 	op->e_o_s=0;
 	op->granulepos=0;
@@ -388,281 +206,347 @@ static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
 	return 0;
 }
 
-static int _blocksize(vcedit_state *s, ogg_packet *p)
+/**
+ * Fetches the next page, without reading much more than max_read bytes
+ * of input. If max_read is -1, there is no limit.
+ *
+ * Returns 1 on success, or 0 if there wasn't enough data for a page
+ * (either due to max_read, or a read error / end of file)
+ */
+static int fetch_next_page(vcedit_state *s, ogg_page *page, int max_read)
+{
+	int read = 0, bytes;
+	char *buffer;
+
+	while(ogg_sync_pageout(s->oy, page) <= 0)
+	{
+		if(read >= max_read && max_read >= 0)
+			return 0;
+
+		buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
+		bytes = s->read(buffer, 1, CHUNKSIZE, s->in);
+
+		ogg_sync_wrote(s->oy, bytes);
+		s->filepos += bytes;
+		read += bytes;
+
+		if(bytes < CHUNKSIZE)
+		{
+			/* stop reading data, but loop once more for the
+			 * ogg_sync_pageout call */
+			max_read = 0;
+			s->read_failed = 1;
+			if(bytes == 0)
+				return 0;
+		}
+	}
+
+	return 1;  // got a page
+}
+
+/**
+ * Copies the given page (which must be the last page returned by
+ * fetch_next_page) into a saved_page structure and appends it to
+ * s->saved_pages. last_sp provides a pointer to the last element of that
+ * list, and is updated to point to the new page.
+ *
+ * Returns 0 on success, or -1 if malloc fails.
+ */
+static int save_page(vcedit_state *s, ogg_page *page, saved_page **last_sp)
 {
-	int this = vorbis_packet_blocksize(s->vi, p);
-	int ret = (this + s->prevW)/4;
+	saved_page *sp;
+	unsigned char *header_data, *body_data;
+	long page_endpos;
+
+	/* This assumes the page was just read from the input file. */
+	page_endpos = s->filepos - (s->oy->fill - s->oy->returned);
 
-	if(!s->prevW)
+	sp = malloc(sizeof(saved_page));
+	header_data = malloc(page->header_len);
+	body_data = malloc(page->body_len);
+	if(!sp || !header_data || !body_data)
 	{
-		s->prevW = this;
-		return 0;
+		s->lasterror =
+				_("Couldn't allocate memory to save Ogg pages");
+		if(header_data) free(header_data);
+		if(body_data) free(body_data);
+		if(sp) free(sp);
+		return -1;
 	}
 
-	s->prevW = this;
-	return ret;
+	memcpy(header_data, page->header, page->header_len);
+	memcpy(body_data, page->body, page->body_len);
+
+	sp->page.header = header_data;
+	sp->page.header_len = page->header_len;
+	sp->page.body = body_data;
+	sp->page.body_len = page->body_len;
+
+	sp->page_position = page_endpos
+			- page->header_len - page->body_len;
+	sp->comment_len = 0;
+
+	/* add to linked list */
+	sp->next = NULL;
+	if(*last_sp) (*last_sp)->next = sp;
+	else s->saved_pages = sp;
+	(*last_sp) = sp;
+
+	return 0;
 }
 
-static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
+static int read_headers(vcedit_state *s, ogg_page *page,
+		saved_page **last_sp)
 {
-	int result;
-	char *buffer;
-	int bytes;
-	int serialno;
+	int i = 0, first_page = 1;
 
-	result = ogg_stream_packetout(s->os, p);
+	while(i<2)
+	{
+		int result, vorbis_stream;
+		long page_endpos, seg_startpos, seg_endpos;
+		saved_page *sp;
+		ogg_packet packet;
 
-	if(result > 0)
-		return 1;
-	else {
-		while(1) {
-			if(s->eosin)
-				return 0;
+		/* read an Ogg page */
 
-			while(ogg_sync_pageout(s->oy, page) <= 0)
+		if(first_page)
+		{
+			/* we already have a valid page the first time through */
+			first_page = 0;
+		}
+		else
+		{
+			result = fetch_next_page(s, page, -1);
+			if(result != 1)
 			{
-				buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
-				bytes = s->read(buffer,1, CHUNKSIZE, s->in);
-				ogg_sync_wrote(s->oy, bytes);
-				if(bytes == 0)
-				return 0;
+				s->lasterror = _("EOF or input error"
+						" before end of vorbis headers.");
+				return -1;
+			}
+		}
+
+		/* save the page with some metadata; all pages are saved up to
+		 * and including the last Vorbis header */
+
+		if(save_page(s, page, last_sp) < 0)
+			return -1;
+
+		sp = (*last_sp);
+		page_endpos = sp->page_position
+				+ sp->page.header_len + sp->page.body_len;
+
+		/** if it's the stream we'll be editing, handle the headers **/
+
+		vorbis_stream = (ogg_page_serialno(page) == s->serial);
+		if(vorbis_stream)
+			ogg_stream_pagein(s->os, page);
+		while(vorbis_stream && i<2)
+		{
+			seg_startpos = page_endpos
+					- s->os->body_fill + s->os->body_returned;
+			if(page_endpos - seg_startpos > page->body_len)
+			{
+				/* startpos is pointing to the Ogg header;
+				 * modify it to point to the first byte of the body */
+				seg_startpos = page_endpos - page->body_len;
 			}
 
-			serialno = ogg_page_serialno(page);
-			if(ogg_page_serialno(page) != s->serial)
+			result = ogg_stream_packetout(s->os, &packet);
+			if(result == -1)
 			{
-				if(vcedit_contains_serial(s, serialno)) {
-					result = buffer_chain_push(s, page);
-					if(result < 0)
-						return result;
-				}
-				else
-				{
-					s->eosin = 1;
-					s->extrapage = 1;
-					return 0;
-				}
-			} 
-			else
+				s->lasterror = _("Corrupt secondary header.");
+				return -1;
+			}
+
+			if(result == 0)
+				seg_endpos = page_endpos;
+			else  /* the packet ends on this page */
+				seg_endpos = page_endpos
+						- s->os->body_fill + s->os->body_returned;
+
+			/* We're reading the comment packet; save information about its
+			 * position (even if the packet is still incomplete). */
+			if (i == 0 && (seg_endpos != seg_startpos))
 			{
-			  ogg_stream_pagein(s->os, page);
-			  result = buffer_chain_newlink(s);
-			  if (result < 0)
-				  return result;
+				/* This code will only be executed once per page. */
+				sp->comment_len = seg_endpos - seg_startpos;
+				sp->comment_offset = seg_startpos
+						- (sp->page_position + sp->page.header_len);
+			}
+
+			if(result == 0)  /* no complete packet yet */
+				break;
+
+			/* got a complete header packet */
 
-			  if(ogg_page_eos(page))
-				s->eosin = 1;
+			if(vorbis_synthesis_headerin(s->vi, s->vc, &packet) < 0)
+			{
+				s->lasterror = _("Error decoding secondary header.");
+				return -1;
+			}
+
+			if(i==1)  /* codebook packet */
+			{
+				s->bookbuf = malloc(packet.bytes);
+				if(!s->bookbuf)
+				{
+					s->lasterror =
+							_("Couldn't allocate memory for codebooks.");
+					return -1;
+				}
+				memcpy(s->bookbuf, packet.packet, packet.bytes);
+				s->booklen = packet.bytes;
 			}
-			result = ogg_stream_packetout(s->os, p);
-			if(result > 0)
-				return 1;
+			i++;
 		}
-		/* Here == trouble */
-		return 0;
 	}
+
+	if(s->os->body_returned < s->os->body_fill)
+	{
+		/* The Vorbis I spec requires that "the third header packet finishes
+		 * the page on which it ends", which is convenient since it would be
+		 * annoying to deal with a partial audio packet here.
+		 */
+		s->lasterror =
+				_("Non-header data contained in a Vorbis header page.");
+		return -1;
+	}
+
+	return 0;
 }
 
+
+
+
 int vcedit_open(vcedit_state *state, FILE *in)
 {
-	return vcedit_open_callbacks(state, (void *)in, 
-			(vcedit_read_func)fread, (vcedit_write_func)fwrite);
+	return vcedit_open_callbacks_seekable(state, (void *)in,
+			(vcedit_read_func)fread, (vcedit_write_func)fwrite,
+			(vcedit_seek_func)fseek, (vcedit_tell_func)ftell);
 }
 
 int vcedit_open_callbacks(vcedit_state *state, void *in,
 		vcedit_read_func read_func, vcedit_write_func write_func)
 {
+	return vcedit_open_callbacks_seekable(state, in,
+			read_func, write_func, NULL, NULL);
+}
 
-	char *buffer;
-	int bytes,i;
-	int chunks = 0;
-	int read_bos, test_supported, page_pending;
-	int have_vorbis;
-	ogg_packet *header;
-	ogg_packet	header_main;
-	ogg_packet  header_comments;
-	ogg_packet	header_codebooks;
-	ogg_page    og;
+int vcedit_open_callbacks_seekable(vcedit_state *state, void *in,
+		vcedit_read_func read_func, vcedit_write_func write_func,
+		vcedit_seek_func seek_func, vcedit_tell_func tell_func)
+{
+
+	int have_vorbis = 0;
+	ogg_page og;
+	int result;
+	saved_page *last_sp = NULL;
 
 	state->in = in;
 	state->read = read_func;
 	state->write = write_func;
+	state->seek = seek_func;
+	state->tell = tell_func;
+
+	state->filepos = 0;
+	state->filesize = -1;
+	if (seek_func && tell_func)  /* determine the file size */
+	{
+		state->filepos = tell_func(in);
+		if (state->filepos != -1 && seek_func(in, 0, 2 /*EOF*/) == 0)
+		{
+			state->filesize = tell_func(in);
+			if (seek_func(in, state->filepos, 0) == -1)
+			{
+				state->lasterror = _("Seek failed.");
+				goto err;
+			}
+		}
+	}
 
 	state->oy = malloc(sizeof(ogg_sync_state));
 	ogg_sync_init(state->oy);
 
-    while(1)
-    {
-    	buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
-	    bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
-
-    	ogg_sync_wrote(state->oy, bytes);
-
-        if(ogg_sync_pageout(state->oy, &og) == 1)
-            break;
-
-        if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
-        {
-		    if(bytes<CHUNKSIZE)
-			    state->lasterror = _("Input truncated or empty.");
-    		else
-	    		state->lasterror = _("Input is not an Ogg bitstream.");
-	    goto err;
+	/* Bail if we don't find data in the first 40 kB */
+	result = fetch_next_page(state, &og, 40*1024);
+	if(result < 0)
+	{
+		state->lasterror = _("Input truncated or empty.");
+		goto err;
 	}
-    }
-
-    /* BOS loop, starting with a loaded ogg page. */
-    if(buffer_chain_newlink(state) < 0)
-	    goto err;
-
-    for( read_bos = 1, have_vorbis = 0 ; read_bos; )
-    {
-	    test_supported = vcedit_supported_stream(state, &og);
-	    if(test_supported < 0)
-	    {
-		    goto err;
-	    }
-	    else if (test_supported == 0 || have_vorbis )
-	    {
-		    if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
-			    goto err;
-		    if( buffer_chain_push(state, &og) < 0)
-			    goto err;
-	    }
-	    else if (test_supported > 0)
-	    {
-		    if(buffer_chain_newlink(state) < 0)
-			    goto err;
-		    state->serial = ogg_page_serialno(&og);
-		    if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
-		       goto err;
- 
-		    state->os = malloc(sizeof(ogg_stream_state));
-		    ogg_stream_init(state->os, state->serial);
-
-		    state->vi = malloc(sizeof(vorbis_info));
-		    vorbis_info_init(state->vi);
-
-		    state->vc = malloc(sizeof(vorbis_comment));
-		    vorbis_comment_init(state->vc);
-
-		    if(ogg_stream_pagein(state->os, &og) < 0)
-		    {
-			    state->lasterror = 
-				    _("Error reading first page of Ogg bitstream.");
-			    goto err;
-		    }
-
-		    if(ogg_stream_packetout(state->os, &header_main) != 1)
-		    {
-			    state->lasterror =
-				    _("Error reading initial header packet.");
-			    goto err;
-		    }
-
-		    if(vorbis_synthesis_headerin(state->vi, state->vc,
-						 &header_main) < 0)
-		    {
-			    state->lasterror =
-				    _("Ogg bitstream does not contain Vorbis data.");
-			    goto err;
-		    }
-		    have_vorbis = 1;
-	    }
-	    while(1)
-	    {
-		    buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
-		    bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
-
-		    if(bytes == 0)
-		    {
-			    state->lasterror =
-				    _("EOF before recognised stream.");
-			    goto err;
-		    }
-
-		    ogg_sync_wrote(state->oy, bytes);
-
-		    if(ogg_sync_pageout(state->oy, &og) == 1)
-			    break;
-	    }
-	    if(!ogg_page_bos(&og)) {
-		    read_bos = 0;
-		    page_pending = 1;
-	    }
-    }
-
-        if(!state->os) {
-		state->lasterror = _("Ogg bitstream does not contain a supported data-type.");
+	else if(result == 0)
+	{
+		state->lasterror = _("Input is not an Ogg bitstream.");
 		goto err;
 	}
 
-	state->mainlen = header_main.bytes;
-	state->mainbuf = malloc(state->mainlen);
-	memcpy(state->mainbuf, header_main.packet, header_main.bytes);
-
-	if(ogg_page_serialno(&og) == state->serial)
+	while(ogg_page_bos(&og))
 	{
-		if(buffer_chain_newlink(state) < 0)
+		int test_supported;
+		ogg_packet header_main;
+
+		if(save_page(state, &og, &last_sp) < 0)
 			goto err;
-	}
 
-	else
-	{
-		if(buffer_chain_push(state, &og) < 0)
+		test_supported = vcedit_supported_stream(state, &og);
+		if(test_supported < 0)
+		{
 			goto err;
-		page_pending = 0;
-	}
+		}
+		else if (test_supported > 0 && !have_vorbis)
+		{
+			state->serial = ogg_page_serialno(&og);
 
-	i = 0;
-	header = &header_comments;
-	while(i<2) {
-		while(i<2) {
-			int result;
-			if(!page_pending)
-				result = vcedit_target_pageout(state, &og);
-			else
+			state->os = malloc(sizeof(ogg_stream_state));
+			ogg_stream_init(state->os, state->serial);
+
+			state->vi = malloc(sizeof(vorbis_info));
+			vorbis_info_init(state->vi);
+
+			state->vc = malloc(sizeof(vorbis_comment));
+			vorbis_comment_init(state->vc);
+
+			if(ogg_stream_pagein(state->os, &og) < 0)
 			{
-				result = 1;
-				page_pending = 0;
+				state->lasterror =
+					_("Error reading first page of Ogg bitstream.");
+				goto err;
 			}
-			if(result == 0 || result == -2) break; /* Too little data so far */
-			else if(result == -1) goto err;
-			else if(result == 1)
+
+			if(ogg_stream_packetout(state->os, &header_main) != 1)
 			{
-				ogg_stream_pagein(state->os, &og);
-				while(i<2)
-				{
-					result = ogg_stream_packetout(state->os, header);
-					if(result == 0) break;
-					if(result == -1)
-					{
-						state->lasterror = _("Corrupt secondary header.");
-						goto err;
-					}
-					vorbis_synthesis_headerin(state->vi, state->vc, header);
-					if(i==1)
-					{
-						state->booklen = header->bytes;
-						state->bookbuf = malloc(state->booklen);
-						memcpy(state->bookbuf, header->packet, 
-								header->bytes);
-					}
-					i++;
-					header = &header_codebooks;
-				}
+				state->lasterror =
+						_("Error reading initial header packet.");
+				goto err;
 			}
+
+			if(vorbis_synthesis_headerin(state->vi, state->vc,
+			                             &header_main) < 0)
+			{
+				state->lasterror =
+						_("Ogg bitstream does not contain Vorbis data");
+				goto err;
+			}
+
+			have_vorbis = 1;
 		}
 
-		buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
-		bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
-		if(bytes == 0 && i < 2)
+		result = fetch_next_page(state, &og, -1);
+		if(result < 0)
 		{
-			state->lasterror = _("EOF before end of Vorbis headers.");
+			state->lasterror = _("EOF before recognised stream.");
 			goto err;
 		}
-		ogg_sync_wrote(state->oy, bytes);
 	}
 
+	if(!state->os) {
+		state->lasterror = _("Ogg bitstream does not contain a supported data-type.");
+		goto err;
+	}
+
+	if (read_headers(state, &og, &last_sp) < 0)
+		goto err;
+
 	/* Copy the vendor tag */
 	state->vendor = malloc(strlen(state->vc->vendor) +1);
 	strcpy(state->vendor, state->vc->vendor);
@@ -675,29 +559,77 @@ err:
 	return -1;
 }
 
+static void set_ogg_pageno(ogg_page *og, long pageno){
+	og->header[18] = (unsigned char)(pageno & 0xff);
+	og->header[19] = (unsigned char)((pageno>>8) & 0xff);
+	og->header[20] = (unsigned char)((pageno>>16) & 0xff);
+	og->header[21] = (unsigned char)((pageno>>24) & 0xff);
+}
+
+static int write_page(vcedit_state *s, void *out, ogg_page *page)
+{
+	if(s->write(page->header, 1, page->header_len, out)
+			!= (size_t)page->header_len)
+		goto err;
+
+	if(s->write(page->body, 1, page->body_len, out)
+			!= (size_t)page->body_len)
+		goto err;
+
+	return 0;
+err:
+	s->lasterror = _("Error writing stream to output. "
+		"Output stream may be corrupted or truncated.");
+	return -1;
+}
+
 int vcedit_write(vcedit_state *state, void *out)
 {
 	ogg_stream_state streamout;
-	ogg_packet header_main;
 	ogg_packet header_comments;
 	ogg_packet header_codebooks;
+	saved_page *sp;
+	ogg_page og;
+	int result, streams_open = 0, ret;
+	int padblock = state->padding_blocksize;
+
+	/* First, write out the BOS page (containing the main header) for the
+	 * stream we're editing, and write out all saved pages from other
+	 * streams (in the order they were read). */
+	for(sp = state->saved_pages; sp; sp = sp->next)
+	{
+		ogg_page *page = &sp->page;
+		if(ogg_page_bos(page)) ++streams_open;
+		if(ogg_page_eos(page)) --streams_open;
 
-	ogg_page ogout, ogin;
-	ogg_packet op;
-	ogg_int64_t granpos = 0;
-	int result;
-	char *buffer;
-	int bytes;
-	int needflush=0, needout=0;
+		if(ogg_page_bos(page) || state->serial != ogg_page_serialno(page))
+		{
+			if (write_page(state, out, page) < 0)
+			{
+				ret = -1;
+				goto cleanup;
+			}
+		}
+	}
 
-	state->eosin = 0;
-	state->extrapage = 0;
+	/* We stopped saving pages after receiving the complete set of headers
+	 * for our Vorbis stream, so now we're at the correct place to write
+	 * those headers (the comment and codebook headers). */
 
-	header_main.bytes = state->mainlen;
-	header_main.packet = state->mainbuf;
-	header_main.b_o_s = 1;
-	header_main.e_o_s = 0;
-	header_main.granulepos = 0;
+	if (padblock == 1 || padblock == -1)
+	{
+		/* no padding size given; try to pick a reasonable default */
+		
+		const long bitrate = state->vi ? state->vi->bitrate_nominal : 0;
+		if ((padblock != 1) && (state->filesize < 256*1024))
+			padblock = 0;  /* filesize may be unknown (-1) */
+		else if (state->filesize < 256*1024
+				|| (bitrate > 0 && bitrate < 96000))
+			padblock = 512;
+		else
+			padblock = 4096;
+	}
+	_commentheader_out(state->vc, state->vendor, &header_comments, padblock);
 
 	header_codebooks.bytes = state->booklen;
 	header_codebooks.packet = state->bookbuf;
@@ -706,176 +638,201 @@ int vcedit_write(vcedit_state *state, void *out)
 	header_codebooks.granulepos = 0;
 
 	ogg_stream_init(&streamout, state->serial);
-
-	_commentheader_out(state->vc, state->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)))
+	streamout.b_o_s = 1;   /* indicates the BOS page was already written */
+	streamout.pageno = 1;  /* page number 0 was the BOS */
+	while((result = ogg_stream_flush(&streamout, &og)))
 	{
-		if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
-			goto cleanup;
-		if(state->write(ogout.header,1,ogout.header_len, out) !=
-				(size_t) ogout.header_len)
-			goto cleanup;
-		if(state->write(ogout.body,1,ogout.body_len, out) != 
-				(size_t) ogout.body_len)
+		if (write_page(state, out, &og) < 0)
+		{
+			ret = -1;
 			goto cleanup;
+		}
 	}
 
-	while(state->sidebuf) {
-	  if(buffer_chain_writelink(state, out) < 0)
-	    goto cleanup;
-	}
-	if(buffer_chain_newlink(state) < 0)
-		goto cleanup;
-
-	while(_fetch_next_packet(state, &op, &ogin))
+	/* All headers are written. Now we just need to copy pages -- but since
+	 * we may have added or removed pages in the stream we edited, we'll need
+	 * to renumber its pages. */
+	while((result = fetch_next_page(state, &og, -1)) == 1)
 	{
-		int size;
-		size = _blocksize(state, &op);
-		granpos += size;
+		if(ogg_page_bos(&og)) ++streams_open;
+		if(ogg_page_eos(&og)) --streams_open;
 
-		if(needflush)
+		if(state->serial == ogg_page_serialno(&og))
 		{
-			if(ogg_stream_flush(&streamout, &ogout))
+			if(ogg_page_pageno(&og) != streamout.pageno)
 			{
-				if(state->sidebuf &&
-				   buffer_chain_writelink(state, out) < 0)
-					goto cleanup;
-				if(state->write(ogout.header,1,ogout.header_len, 
-							out) != (size_t) ogout.header_len)
-					goto cleanup;
-				if(state->write(ogout.body,1,ogout.body_len, 
-							out) != (size_t) ogout.body_len)
-					goto cleanup;
+				set_ogg_pageno(&og, streamout.pageno);
+				ogg_page_checksum_set(&og);
 			}
+			streamout.pageno++;
 		}
-		else if(needout)
-		{
-			if(ogg_stream_pageout(&streamout, &ogout))
-			{
-				if(state->sidebuf &&
-				   buffer_chain_writelink(state, out) < 0)
-					goto cleanup;
-				if(state->write(ogout.header,1,ogout.header_len, 
-							out) != (size_t) ogout.header_len)
-					goto cleanup;
-				if(state->write(ogout.body,1,ogout.body_len, 
-							out) != (size_t) ogout.body_len)
-					goto cleanup;
-			}
-		}
-
-		needflush=needout=0;
 
-		if(op.granulepos == -1)
+		if (write_page(state, out, &og) < 0)
 		{
-			op.granulepos = granpos;
-			ogg_stream_packetin(&streamout, &op);
+			ret = -1;
+			goto cleanup;
 		}
-		else /* granulepos is set, validly. Use it, and force a flush to 
-				account for shortened blocks (vcut) when appropriate */ 
-		{
-			if(granpos > op.granulepos)
-			{
-				granpos = op.granulepos;
-				ogg_stream_packetin(&streamout, &op);
-				needflush=1;
-			}
-			else 
-			{
-				ogg_stream_packetin(&streamout, &op);
-				needout=1;
-			}
-		}		
 	}
+	ret = 0;
 
-	streamout.e_o_s = 1;
-	while(ogg_stream_flush(&streamout, &ogout))
+cleanup:
+	ogg_stream_clear(&streamout);
+
+    /* We don't ogg_packet_clear() this, because the memory was allocated in
+       _commentheader_out(), so we mirror that here */
+    _ogg_free(header_comments.packet);
+
+	free(state->bookbuf);
+	state->bookbuf = NULL;
+
+	if((ret == 0) && streams_open)
 	{
-		if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
-			goto cleanup;
-		if(state->write(ogout.header,1,ogout.header_len, 
-					out) != (size_t) ogout.header_len)
-			goto cleanup;
-		if(state->write(ogout.body,1,ogout.body_len, 
-					out) != (size_t) ogout.body_len)
-			goto cleanup;
+		state->lasterror = _("Input file has unclosed streams"
+				" and may be corrupted or truncated.");
+		ret = -1;
 	}
 
-	if (state->extrapage)
-	{
-		/* This is the first page of a new chain, get rid of the
-		 * sidebuffer */
-		while(state->sidebuf)
-			if(buffer_chain_writelink(state, out) < 0)
-				goto cleanup;
-		if(state->write(ogin.header,1,ogin.header_len,
-		                out) != (size_t) ogin.header_len)
-			goto cleanup;
-		if (state->write(ogin.body,1,ogin.body_len, out) !=
-				(size_t) ogin.body_len)
-			goto cleanup;
+	return ret;
+}
+
+/* Updates the comment pages in memory with new content, if there's enough
+ * space for an in-place update. Pads the comment if necessary; if max_padding
+ * is not -1, it specifies the maximum number of padding bytes that are
+ * considered acceptable.
+ *
+ * Returns 1 on success, -1 on failure, or 0 if we can't update the comment
+ * in place (due to a lack of space or the max_padding limit).
+ */
+int vcedit_update_comment_pages(vcedit_state *s, long max_padding)
+{
+	ogg_packet header_comments;
+	saved_page *sp;
+	unsigned char *data;
+	long expected_len = 16, available_len = 0, data_len;
+	int i;
+	int ret = 0;  /* error */
+
+	header_comments.packet = NULL;
+
+	expected_len += strlen(s->vendor);
+	expected_len += 4 * s->vc->comments;
+	for(i = 0; i < s->vc->comments; i++){
+		if(s->vc->user_comments[i])
+			expected_len += s->vc->comment_lengths[i];
 	}
 
-	state->eosin=0; /* clear it, because not all paths to here do */
-	while(!state->eosin) /* We reached eos, not eof */
+	for(sp = s->saved_pages; sp; sp = sp->next)
+		available_len += sp->comment_len;
+
+	if(expected_len > available_len)
+		goto cleanup;
+
+	if(available_len - expected_len > max_padding
+			&& max_padding >= 0)
+		goto cleanup;
+
+	/* We won't add any padding here, but we'll clear any unused portions
+	 * of the packet later. */
+	_commentheader_out(s->vc, s->vendor, &header_comments, 0);
+
+	data = header_comments.packet;
+	data_len = header_comments.bytes;
+	if(data_len > available_len)
+		goto cleanup;
+
+	sp = s->saved_pages;
+	while(data_len)
 	{
-		/* We copy the rest of the stream (other logical streams)
-		 * through, a page at a time. */
-		while(1)
+		unsigned char *dst = sp->page.body + sp->comment_offset;
+		long len = sp->comment_len;
+		if(len > data_len)
+			len = data_len;
+
+		if(len)
 		{
-			result = ogg_sync_pageout(state->oy, &ogout);
-			if(result==0)
-                break;
-			if(result<0)
-				state->lasterror = _("Corrupt or missing data, continuing...");
-			else
-			{
-				/* Don't bother going through the rest, we can just 
-				 * write the page out now */
-				if(state->write(ogout.header,1,ogout.header_len, 
-						out) != (size_t) ogout.header_len) {
-					goto cleanup;
-                }
-				if(state->write(ogout.body,1,ogout.body_len, out) !=
-						(size_t) ogout.body_len) {
-					goto cleanup;
-                }
-			}
+			memcpy(dst, data, len);
+			memset(dst + len, 0, sp->comment_len - len);
+			ogg_page_checksum_set(&sp->page);
+
+			data += len;
+			data_len -= len;
 		}
-		buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
-		bytes = state->read(buffer,1, CHUNKSIZE, state->in);
-		ogg_sync_wrote(state->oy, bytes);
-		if(bytes == 0) 
+		sp = sp->next;
+	}
+
+	while(sp)
+	{
+		long len = sp->comment_len;
+		if(len)
 		{
-			state->eosin = 1;
-			break;
+			memset(sp->page.body + sp->comment_offset, 0, sp->comment_len);
+			ogg_page_checksum_set(&sp->page);
 		}
+		sp = sp->next;
 	}
 
-
+	ret = 1;  /* success */
 cleanup:
-	ogg_stream_clear(&streamout);
+	/* We don't ogg_packet_clear() this, because the memory was allocated in
+	   _commentheader_out(), so we mirror that here */
+	_ogg_free(header_comments.packet);
+	return ret;
+}
 
-    /* We don't ogg_packet_clear() this, because the memory was allocated in
-       _commentheader_out(), so we mirror that here */
-    _ogg_free(header_comments.packet);
+/* Overwrites the comment pages in the given file, which must be the same file
+ * opened by vcedit_open (or have the same position and layout for all comment
+ * pages).
+ *
+ * Returns 0 on success, or a negative value on error:
+ *  -1 if the output file was partially updated before the error occurred,
+ *  -2 if no changes had been made yet.
+ */
+int vcedit_overwrite(vcedit_state *s, void *out)
+{
+	saved_page *sp;
+	long pos = -1;
+	int wrote = 0;
+	ssize_t bytes;
 
-	free(state->mainbuf);
-	free(state->bookbuf);
-    state->mainbuf = state->bookbuf = NULL;
+	for(sp = s->saved_pages; sp; sp = sp->next)
+	{
+		if(sp->comment_len == 0) continue;
+
+		if(pos != sp->page_position)
+		{
+			if (!s->seek) goto err;
+			if (s->seek(out, sp->page_position, SEEK_SET) == -1)
+				goto err;
+			pos = sp->page_position;
+		}
+
+		bytes = s->write(sp->page.header, 1, sp->page.header_len, out);
+		if(bytes > 0) wrote = 1;
+		pos += bytes;
+		if(bytes != (size_t)sp->page.header_len)
+			goto err;
+
+		bytes = s->write(sp->page.body, 1, sp->page.body_len, out);
+		pos += bytes;
+		if(bytes != (size_t)sp->page.body_len)
+			goto err;
+	}
+	return 0;  // success
 
-	if(!state->eosin)
+err:
+	if(wrote)
 	{
-		state->lasterror =
-			_("Error writing stream to output. "
-			"Output stream may be corrupted or truncated.");
+		s->lasterror = _("Error updating stream in place. "
+			"Output stream may be corrupted.");
 		return -1;
 	}
-
-	return 0;
+	else
+	{
+		s->lasterror = _("Error updating stream in place. "
+			"No changes were made.");
+		return -2;
+	}
 }
diff --git a/vorbiscomment/vcedit.h b/vorbiscomment/vcedit.h
index 173876f..009b95a 100644
--- a/vorbiscomment/vcedit.h
+++ b/vorbiscomment/vcedit.h
@@ -21,11 +21,16 @@ extern "C" {
 
 typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *);
 typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *);
+typedef int (*vcedit_seek_func)(void *, long, int);
+typedef long (*vcedit_tell_func)(void *);
 
-typedef struct {
-	long *streams;
-	size_t streams_len;
-} vcedit_serial_nos;
+typedef struct saved_page_t {
+	int page_position;
+	ogg_page page;
+	struct saved_page_t *next;
+	int comment_offset;
+	int comment_len;
+} saved_page;
 
 typedef struct {
 	ogg_sync_state		*oy;
@@ -36,30 +41,38 @@ typedef struct {
 
 	vcedit_read_func read;
 	vcedit_write_func write;
+	vcedit_seek_func seek;
+	vcedit_tell_func tell;
 
 	void		  *in;
 	int serial;
-	vcedit_serial_nos serials;
-	unsigned char	  *mainbuf;
 	unsigned char	  *bookbuf;
-	int	          mainlen;
 	int	          booklen;
 	char   *lasterror;
 	char   *vendor;
-	int prevW;
-	int extrapage;
-	int eosin;
-        struct vcedit_buffer_chain *sidebuf;
+
+	int read_failed;  /* could be caused by EOF */
+	ogg_int64_t filepos;
+	ogg_int64_t filesize;
+	saved_page *saved_pages;
+
+	int padding_blocksize;
 } vcedit_state;
 
 extern vcedit_state *	vcedit_new_state(void);
 extern void		vcedit_clear(vcedit_state *state);
 extern vorbis_comment *	vcedit_comments(vcedit_state *state);
+extern void vcedit_set_padding(vcedit_state *state, int padding_blocksize);
 extern int		vcedit_open(vcedit_state *state, FILE *in);
 extern int		vcedit_open_callbacks(vcedit_state *state, void *in,
 		vcedit_read_func read_func, vcedit_write_func write_func);
+extern int		vcedit_open_callbacks_seekable(vcedit_state *state, void *in,
+		vcedit_read_func read_func, vcedit_write_func write_func,
+		vcedit_seek_func seek_func, vcedit_tell_func tell_func);
 extern int		vcedit_write(vcedit_state *state, void *out);
 extern char *   vcedit_error(vcedit_state *state);
+extern int vcedit_update_comment_pages(vcedit_state *state, long max_padding);
+extern int vcedit_overwrite(vcedit_state *state, void *out);
 
 #ifdef __cplusplus
 }
diff --git a/vorbiscomment/vcomment.c b/vorbiscomment/vcomment.c
index 3e60903..35ae38c 100644
--- a/vorbiscomment/vcomment.c
+++ b/vorbiscomment/vcomment.c
@@ -40,7 +40,9 @@ struct option long_options[] = {
 	{"quiet",0,0,'q'}, /* unused */
 	{"version", 0, 0, 'V'},
 	{"commentfile",1,0,'c'},
+	{"padding",2,0,1},
 	{"raw", 0,0,'R'},
+	{"inplace",0,0,'p'},
 	{NULL,0,0,0}
 };
 
@@ -49,6 +51,8 @@ typedef struct {
 	/* mode and flags */
 	int	mode;
 	int	raw;
+	int	inplace;
+	int	padding;
 
 	/* file names and handles */
 	char	*infilename, *outfilename;
@@ -74,7 +78,8 @@ int  add_comment(char *line, vorbis_comment *vc, int raw);
 param_t	*new_param(void);
 void free_param(param_t *param);
 void parse_options(int argc, char *argv[], param_t *param);
-void open_files(param_t *p);
+void open_input(param_t *p);
+void open_output(param_t *p);
 void close_files(param_t *p, int output_written);
 
 char *
@@ -162,10 +167,10 @@ read_line (FILE *input)
 
 int main(int argc, char **argv)
 {
-	vcedit_state *state;
+	vcedit_state *state = NULL;
 	vorbis_comment *vc;
 	param_t	*param;
-	int i;
+	int i, ret, output_written = 0;
 
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
@@ -177,7 +182,7 @@ int main(int argc, char **argv)
 
 	/* take care of opening the requested files */
 	/* relevent file pointers are returned in the param struct */
-	open_files(param);
+	open_input(param);
 
 	/* which mode are we in? */
 
@@ -189,22 +194,18 @@ int main(int argc, char **argv)
 		{
 			fprintf(stderr, _("Failed to open file as Vorbis: %s\n"), 
 					vcedit_error(state));
-            close_files(param, 0);
-            free_param(param);
-            vcedit_clear(state);
-			return 1;
+			ret = 1;
+			goto cleanup;
 		}
 
 		/* extract and display the comments */
+		open_output(param);
 		vc = vcedit_comments(state);
 		print_comments(param->com, vc, param->raw);
 
 		/* done */
-		vcedit_clear(state);
-
-		close_files(param, 0);
-        free_param(param);
-		return 0;		
+		ret = 0;
+		goto cleanup;
 	}
 
 	if (param->mode == MODE_WRITE || param->mode == MODE_APPEND) {
@@ -215,10 +216,8 @@ int main(int argc, char **argv)
 		{
 			fprintf(stderr, _("Failed to open file as Vorbis: %s\n"), 
 					vcedit_error(state));
-			close_files(param, 0);
-			free_param(param);
-			vcedit_clear(state);
-			return 1;
+			ret = 1;
+			goto cleanup;
 		}
 
 		/* grab and clear the exisiting comments */
@@ -251,29 +250,51 @@ int main(int argc, char **argv)
                         }
 		}
 
-		/* write out the modified stream */
-		if(vcedit_write(state, param->out) < 0)
+		vcedit_set_padding(state, param->padding);
+		if(param->tempoutfile && param->inplace
+				&& vcedit_update_comment_pages(state, 8192) > 0)
 		{
-			fprintf(stderr, _("Failed to write comments to output file: %s\n"), 
-					vcedit_error(state));
-			close_files(param, 0);
-			free_param(param);
-			vcedit_clear(state);
-			return 1;
+			/* ensure the input file isn't deleted in close_files */
+			param->tempoutfile = 0;
+
+			int rv = vcedit_overwrite(state, param->in);
+			if(rv < 0)
+			{
+				fprintf(stderr, _("Failed to update comments in place.\n"));
+				if(rv == -1)
+					fprintf(stderr, _("The file might now be corrupt.\n"));
+				ret = 1;
+				goto cleanup;
+			}
+		}
+		else
+		{
+			/* write out the modified stream */
+			open_output(param);
+			if(vcedit_write(state, param->out) < 0)
+			{
+				fprintf(stderr, _("Failed to write comments to output file: %s\n"), 
+						vcedit_error(state));
+				ret = 1;
+				goto cleanup;
+			}
 		}
 
 		/* done */
-		vcedit_clear(state);
-		
-		close_files(param, 1);
-		free_param(param);
-		return 0;
+		output_written = 1;
+		ret = 0;
+		goto cleanup;
 	}
 
 	/* should never reach this point */
 	fprintf(stderr, _("no action specified\n"));
-    free_param(param);
-	return 1;
+	ret = 1;
+
+cleanup:
+	if(state) vcedit_clear(state);
+	close_files(param, output_written);
+	free_param(param);
+	return ret;
 }
 
 /**********
@@ -404,6 +425,12 @@ void usage(void)
             "                          When listing, write comments to the specified file.\n"
             "                          When editing, read comments from the specified file.\n"));
   printf (_("  -R, --raw               Read and write comments in UTF-8\n"));
+  printf (_("  -p, --inplace           Overwrite the comments without rewriting the file,\n"
+            "                          if possible.\n"));
+  printf (_("  --padding=n             Pad the comment packet to a multiple of n bytes.\n"
+            "                          Specify 0 to disable padding, or 1 to force it using\n"
+            "                          an automatically-determined amount. By default\n"
+            "                          padding is automatic, but disabled for small files.\n"));
   printf ("\n");
 
   printf (_("  -h, --help              Display this help\n"));
@@ -451,6 +478,8 @@ param_t *new_param(void)
 	/* mode and flags */
 	param->mode = MODE_LIST;
 	param->raw = 0;
+	param->inplace = 0;
+	param->padding = -1;
 
 	/* filenames */
 	param->infilename  = NULL;
@@ -485,13 +514,25 @@ void parse_options(int argc, char *argv[], param_t *param)
 
 	setlocale(LC_ALL, "");
 
-	while ((ret = getopt_long(argc, argv, "alwhqVc:t:R",
+	while ((ret = getopt_long(argc, argv, "alwhqVc:t:Rp",
 			long_options, &option_index)) != -1) {
 		switch (ret) {
 			case 0:
 				fprintf(stderr, _("Internal error parsing command options\n"));
 				exit(1);
 				break;
+			case 1:  /* --padding */
+				if(optarg)
+				{
+					if(sscanf(optarg, "%d", &param->padding) != 1) {
+						fprintf(stderr, _("WARNING: Couldn't parse"
+								" padding argument \"%s\"\n"),
+								optarg);
+						param->padding = -1;
+					}
+				}
+				else param->padding = 1;
+				break;
 			case 'l':
 				param->mode = MODE_LIST;
 				break;
@@ -523,6 +564,9 @@ void parse_options(int argc, char *argv[], param_t *param)
 						(param->commentcount+1)*sizeof(char *));
 				param->comments[param->commentcount++] = strdup(optarg);
 				break;
+			case 'p':
+				param->inplace = 1;
+				break;
 			default:
 				usage();
 				exit(1);
@@ -547,15 +591,21 @@ void parse_options(int argc, char *argv[], param_t *param)
 			strcpy(param->outfilename, param->infilename);
 			strcat(param->outfilename, ".vctemp");
 		}
+		else if(param->inplace)
+		{
+			fprintf(stderr, _("No output filename may be provided"
+					" when using the --inplace option.\n"));
+			exit(1);
+		}
 		else
 			param->outfilename = strdup(argv[optind+1]);
 	}
 }
 
 /**********
-   open_files()
+   open_input(), open_output()
 
-   This function takes care of opening the appropriate files
+   These functions takes care of opening the appropriate files
    based on the mode and filenames in the param structure.
    A filename of '-' is interpreted as stdin/out.
 
@@ -564,14 +614,14 @@ void parse_options(int argc, char *argv[], param_t *param)
 
 ***********/
 
-void open_files(param_t *p)
+void open_input(param_t *p)
 {
 	/* for all modes, open the input file */
 
 	if (strncmp(p->infilename,"-",2) == 0) {
 		p->in = stdin;
 	} else {
-		p->in = fopen(p->infilename, "rb");
+		p->in = fopen(p->infilename, p->inplace ? "r+b" : "rb");
 	}
 	if (p->in == NULL) {
 		fprintf(stderr,
@@ -582,6 +632,30 @@ void open_files(param_t *p)
 
 	if (p->mode == MODE_WRITE || p->mode == MODE_APPEND) { 
 
+		/* commentfile is input */
+
+		if ((p->commentfilename == NULL) ||
+				(strncmp(p->commentfilename,"-",2) == 0)) {
+			p->com = stdin;
+		} else {
+			p->com = fopen(p->commentfilename, "r");
+		}
+		if (p->com == NULL) {
+			fprintf(stderr,
+				_("Error opening comment file '%s'.\n"),
+				p->commentfilename);
+			exit(1);
+		}
+
+	}
+
+	/* all done */
+}
+
+void open_output(param_t *p)
+{
+	if (p->mode == MODE_WRITE || p->mode == MODE_APPEND) { 
+
 		/* open output for write mode */
         if(!strcmp(p->infilename, p->outfilename)) {
             fprintf(stderr, _("Input filename may not be the same as output filename\n"));
@@ -600,21 +674,6 @@ void open_files(param_t *p)
 			exit(1);
 		}
 
-		/* commentfile is input */
-		
-		if ((p->commentfilename == NULL) ||
-				(strncmp(p->commentfilename,"-",2) == 0)) {
-			p->com = stdin;
-		} else {
-			p->com = fopen(p->commentfilename, "r");
-		}
-		if (p->com == NULL) {
-			fprintf(stderr,
-				_("Error opening comment file '%s'.\n"),
-				p->commentfilename);
-			exit(1);
-		}
-
 	} else {
 
 		/* in list mode, commentfile is output */
