#! /bin/sh -e

# DP: Implement fastjar -u (PR 7854).

dir=
if [ $# -eq 3 -a "$2" = '-d' ]; then
    pdir="-d $3"
    dir="$3/"
elif [ $# -ne 1 ]; then
    echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
    exit 1
fi
case "$1" in
    -patch)
        patch $pdir -f --no-backup-if-mismatch -p0 < $0
        #cd ${dir}gcc && autoconf
        ;;
    -unpatch)
        patch $pdir -f --no-backup-if-mismatch -R -p0 < $0
        #rm ${dir}gcc/configure
        ;;
    *)
        echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
        exit 1
esac
exit 0

diff -urN fastjar.old/Makefile.am fastjar/Makefile.am
--- fastjar.old/Makefile.am	2004-04-15 01:49:37.000000000 +0200
+++ fastjar/Makefile.am	2004-05-30 17:30:34.000000000 +0200
@@ -46,10 +46,11 @@
 LIBIBERTY = ../libiberty/libiberty.a
 
 bin_PROGRAMS = jar grepjar
-jar_SOURCES = jartool.c dostime.c compress.c pushback.c jartool.h \
-zipfile.h dostime.h compress.h pushback.h
+jar_SOURCES = jartool.c dostime.c compress.c pushback.c shift.c jartool.h \
+zipfile.h dostime.h compress.h pushback.h shift.h
 jar_LDADD = $(ZLIBS) $(LIBIBERTY)
 jar_DEPENDENCIES = $(ZDEPS) $(LIBIBERTY)
+jar_CPPFLAGS = $(AM_CPPFLAGS) -DWITH_SHIFT_DOWN
 
 grepjar_SOURCES = jargrep.c dostime.c compress.c pushback.c jartool.h \
 zipfile.h dostime.h compress.h pushback.h
diff -urN fastjar.old/Makefile.in fastjar/Makefile.in
--- fastjar.old/Makefile.in	2004-04-19 04:23:04.000000000 +0200
+++ fastjar/Makefile.in	2004-05-30 17:33:00.000000000 +0200
@@ -165,11 +165,12 @@
 LIBIBERTY = ../libiberty/libiberty.a
 
 bin_PROGRAMS = jar grepjar
-jar_SOURCES = jartool.c dostime.c compress.c pushback.c jartool.h \
-zipfile.h dostime.h compress.h pushback.h
+jar_SOURCES = jartool.c dostime.c compress.c pushback.c shift.c jartool.h \
+zipfile.h dostime.h compress.h pushback.h shift.h
 
 jar_LDADD = $(ZLIBS) $(LIBIBERTY)
 jar_DEPENDENCIES = $(ZDEPS) $(LIBIBERTY)
+jar_CPPFLAGS = -DWITH_SHIFT_DOWN
 
 grepjar_SOURCES = jargrep.c dostime.c compress.c pushback.c jartool.h \
 zipfile.h dostime.h compress.h pushback.h
@@ -217,8 +218,9 @@
 	compress.$(OBJEXT) pushback.$(OBJEXT)
 grepjar_OBJECTS = $(am_grepjar_OBJECTS)
 grepjar_LDFLAGS =
-am_jar_OBJECTS = jartool.$(OBJEXT) dostime.$(OBJEXT) compress.$(OBJEXT) \
-	pushback.$(OBJEXT)
+am_jar_OBJECTS = jar-jartool.$(OBJEXT) jar-dostime.$(OBJEXT) \
+	jar-compress.$(OBJEXT) jar-pushback.$(OBJEXT) \
+	jar-shift.$(OBJEXT)
 jar_OBJECTS = $(am_jar_OBJECTS)
 jar_LDFLAGS =
 
@@ -329,6 +331,36 @@
 .c.obj:
 	$(COMPILE) -c `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'; fi`
 
+jar-jartool.o: jartool.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-jartool.o `test -f 'jartool.c' || echo '$(srcdir)/'`jartool.c
+
+jar-jartool.obj: jartool.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-jartool.obj `if test -f 'jartool.c'; then $(CYGPATH_W) 'jartool.c'; else $(CYGPATH_W) '$(srcdir)/jartool.c'; fi`
+
+jar-dostime.o: dostime.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-dostime.o `test -f 'dostime.c' || echo '$(srcdir)/'`dostime.c
+
+jar-dostime.obj: dostime.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-dostime.obj `if test -f 'dostime.c'; then $(CYGPATH_W) 'dostime.c'; else $(CYGPATH_W) '$(srcdir)/dostime.c'; fi`
+
+jar-compress.o: compress.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-compress.o `test -f 'compress.c' || echo '$(srcdir)/'`compress.c
+
+jar-compress.obj: compress.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-compress.obj `if test -f 'compress.c'; then $(CYGPATH_W) 'compress.c'; else $(CYGPATH_W) '$(srcdir)/compress.c'; fi`
+
+jar-pushback.o: pushback.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-pushback.o `test -f 'pushback.c' || echo '$(srcdir)/'`pushback.c
+
+jar-pushback.obj: pushback.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-pushback.obj `if test -f 'pushback.c'; then $(CYGPATH_W) 'pushback.c'; else $(CYGPATH_W) '$(srcdir)/pushback.c'; fi`
+
+jar-shift.o: shift.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-shift.o `test -f 'shift.c' || echo '$(srcdir)/'`shift.c
+
+jar-shift.obj: shift.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(jar_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o jar-shift.obj `if test -f 'shift.c'; then $(CYGPATH_W) 'shift.c'; else $(CYGPATH_W) '$(srcdir)/shift.c'; fi`
+
 .texi.info:
 	@rm -f $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]
 	$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \
diff -urN fastjar.old/compress.c fastjar/compress.c
--- fastjar.old/compress.c	2002-01-03 05:57:56.000000000 +0100
+++ fastjar/compress.c	2004-05-30 17:30:34.000000000 +0200
@@ -74,6 +74,7 @@
 /*
   compress.c - code for handling deflation
   Copyright (C) 1999  Bryan Burns
+  Copyright (C) 2004  Free Software Foundation, Inc.
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
@@ -109,8 +110,12 @@
 #include "jartool.h"
 #include "pushback.h"
 #include "compress.h"
+#include "shift.h"
+
+int write_data (int, void *, size_t, struct zipentry *);
 
 extern int seekable;
+extern off_t end_of_entries;
 
 static z_stream zs;
 
@@ -133,7 +138,36 @@
   }
 }
 
-int compress_file(int in_fd, int out_fd, struct zipentry *ze){
+int
+write_data (int fd, void *buf, size_t len, struct zipentry *ze)
+{
+#ifdef WITH_SHIFT_DOWN
+  struct zipentry *next = NULL;
+  off_t here = lseek (fd, 0, SEEK_CUR);
+  /*
+   * If we are updating and there is not enough space before the next
+   * entry, expand the file.
+   */
+  if (ze)
+    {
+      next = ze->next_entry;
+      if (next && here + len >= next->offset)
+	{
+	  if (shift_down (fd, next->offset, (here + len) - next->offset, next))
+	    {
+	      perror ("can't expand file");
+	      exit (1);
+	    }
+	}
+    }
+#endif /* WITH_SHIFT_DOWN */
+
+  return write (fd, buf, len);
+}
+
+int compress_file(int in_fd, int out_fd, struct zipentry *ze,
+		  struct zipentry *existing)
+{
   Bytef in_buff[RDSZ];
   Bytef out_buff[RDSZ];
   unsigned int rdamt, wramt;
@@ -183,10 +217,11 @@
     /* If the output buffer is full, dump it to disk */
     if(zs.avail_out == 0){
 
-      if(write(out_fd, out_buff, RDSZ) != RDSZ){
-        perror("write");
-        exit(1);
-      }
+      if (write_data (out_fd, out_buff, RDSZ, existing) != RDSZ)
+	{
+	  perror("write");
+	  exit(1);
+	}
 
       /* clear the output buffer */
       zs.next_out = out_buff;
@@ -201,10 +236,11 @@
 
     wramt = RDSZ - zs.avail_out;
 
-    if(write(out_fd, out_buff, wramt) != (int)wramt){
-      perror("write");
-      exit(1);
-    }
+    if (write_data (out_fd, out_buff, wramt, existing) != (int)wramt)
+      {
+	perror("write");
+	exit(1);
+      }
     /* clear the output buffer */
     zs.next_out = out_buff;
     zs.avail_out = (uInt)RDSZ;
@@ -215,10 +251,11 @@
   while(deflate(&zs, Z_FINISH) == Z_OK){
     wramt = RDSZ - zs.avail_out;
 
-    if(write(out_fd, out_buff, wramt) != (int)wramt){
-      perror("write");
-      exit(1);
-    }
+    if (write_data (out_fd, out_buff, wramt, existing) != (int)wramt)
+      {
+	perror("write");
+	exit(1);
+      }
 
     zs.next_out = out_buff;
     zs.avail_out = (uInt)RDSZ;
@@ -228,10 +265,11 @@
   if(zs.avail_out != RDSZ){
     wramt = RDSZ - zs.avail_out;
 
-    if(write(out_fd, out_buff, wramt) != (int)wramt){
-      perror("write");
-      exit(1);
-    }
+    if (write_data (out_fd, out_buff, wramt, existing) != (int)wramt)
+      {
+	perror("write");
+	exit(1);
+      }
   }
 
   /* update fastjar's entry information */
diff -urN fastjar.old/compress.h fastjar/compress.h
--- fastjar.old/compress.h	2000-12-14 19:45:35.000000000 +0100
+++ fastjar/compress.h	2004-05-30 17:30:34.000000000 +0200
@@ -46,7 +46,7 @@
 void init_compression(void);
 
 /* Compresses the file specified by in_fd and appends it to out_fd */
-int compress_file(int in_fd, int out_fd, struct zipentry *ze);
+int compress_file(int, int, struct zipentry *, struct zipentry *);
 
 /* Frees memory used by compression function */
 void end_compression(void);
diff -urN fastjar.old/fastjar.texi fastjar/fastjar.texi
--- fastjar.old/fastjar.texi	2003-08-13 23:04:12.000000000 +0200
+++ fastjar/fastjar.texi	2004-05-30 17:32:33.000000000 +0200
@@ -125,8 +125,7 @@
 Extract named (or all) files from archive.
 
 @item -u
-Update existing archive. This option is disabled due to bugs (currently
-fails with exit status 1 and does nothing).
+Update existing archive.
 
 @end table
 
diff -urN fastjar.old/jartool.c fastjar/jartool.c
--- fastjar.old/jartool.c	2004-01-07 19:46:04.000000000 +0100
+++ fastjar/jartool.c	2004-05-30 17:30:34.000000000 +0200
@@ -1,6 +1,6 @@
 /*
   jartool.c - main functions for fastjar utility
-  Copyright (C) 2002 Free Software Foundation
+  Copyright (C) 2002, 2004  Free Software Foundation
   Copyright (C) 1999, 2000, 2001  Bryan Burns
   
   This program is free software; you can redistribute it and/or
@@ -238,6 +238,7 @@
 #include "dostime.h"
 #include "pushback.h"
 #include "compress.h"
+#include "shift.h"
 
 /* Some systems have mkdir that takes a single argument.  */
 #ifdef MKDIR_TAKES_ONE_ARG
@@ -273,15 +274,18 @@
 int consume(pb_file *, int);
 int list_jar(int, char**, int);
 int extract_jar(int, char**, int);
-int add_file_to_jar(int, int, const char*, struct stat*);
-int add_to_jar(int, const char*);
-int add_to_jar_with_dir(int, const char*, const char*);
+int add_file_to_jar(int, int, const char*, struct stat*, int);
+int add_to_jar(int, const char*, int);
+int add_to_jar_with_dir(int, const char*, const char*, int);
 int create_central_header(int);
-int make_manifest(int, const char*);
+int make_manifest(int, const char*, int);
+int read_entries (int);
 static void init_args(char **, int);
 static char *get_next_arg (void);
 static char *jt_strdup (char*);
 static void expand_options (int *argcp, char ***argvp);
+static inline struct zipentry *find_entry (const char *);
+static inline int looks_like_dir (const char *);
 
 /* global variables */
 ub1 file_header[30];
@@ -305,6 +309,12 @@
 
 int number_of_entries; /* number of entries in the linked list */
 
+/* What we go by. */
+const char *progname;
+
+/* The offset of the end of the last zip entry. */
+ub4 end_of_entries;
+
 /* This is used to mark options with no short value.  */
 #define LONG_OPT(Num)  ((Num) + 128)
 
@@ -340,6 +350,8 @@
   int new_argc;
   char **new_argv;
 
+  progname = argv[0];
+
   do_compress = TRUE;
   verbose = FALSE;
   
@@ -418,15 +430,11 @@
   new_argv[new_argc] = NULL;
 
   if(action == ACTION_NONE){
-    fprintf(stderr, "One of options -{ctxu} must be specified.\n");
+    fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
+	    progname);
     usage(argv[0]);
   }
 
-  if(action == ACTION_UPDATE){
-    fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
-    exit(1);
-  }
-
   /* Verify unsupported combinations and warn of the use of non
      standard features */
   if(verbose && use_explicit_list_only)
@@ -435,7 +443,8 @@
     fprintf (stderr, "Warning: using non standard '-@' option\n");
   if(read_names_from_stdin
       && (action != ACTION_CREATE && action != ACTION_UPDATE)){
-      fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
+      fprintf(stderr, "%s: option '-@' is supported only with '-c' or '-u'.\n",
+	      progname);
       usage(argv[0]);
   }
 
@@ -445,8 +454,8 @@
       jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
 
       if(jarfd < 0){
-        fprintf(stderr, "Error opening %s for writing!\n", jarfile);
-        perror(jarfile);
+        fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
+		jarfile, strerror (errno));
         exit(1);
       }
       
@@ -470,8 +479,8 @@
       jarfd = open(jarfile, O_RDONLY | O_BINARY);
 
       if(jarfd < 0){
-        fprintf(stderr, "Error opening %s for reading!\n", jarfile);
-        perror(jarfile);
+        fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
+		jarfile, strerror (errno));
         exit(1);
       }
 
@@ -484,28 +493,51 @@
     }
   }
 
+  if (action == ACTION_UPDATE)
+    {
+      if (!jarfile)
+	{
+	  fprintf (stderr, "%s: `-u' mode requires a file name\n",
+		   argv[0]);
+	  exit (1);
+	}
+
+      if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
+	{
+	  fprintf (stderr, "Error opening %s for reading!\n", jarfile);
+	  perror (jarfile);
+	  exit (1);
+	}
+
+      /* Assert that jarfd is seekable. */
+      if (lseek (jarfd, 0, SEEK_CUR) == -1)
+	{
+	  fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
+	  exit (1);
+	}
+
+      seekable = TRUE;
+    }
+
   if(action == ACTION_CREATE || action == ACTION_UPDATE){
     const char *arg;
     init_headers();
 
-   if((action == ACTION_UPDATE) && jarfile) {
-      if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
-	fprintf(stderr, "Error opening %s for reading!\n", jarfile);
-        perror(jarfile);
-        exit(1);
-      }
-    }
-
     if(do_compress)
       init_compression();
-  
+
+    if (action == ACTION_UPDATE)
+      {
+	if (read_entries (jarfd))
+	  exit (1);
+      }
 
     /* Add the META-INF/ directory and the manifest */
     if(manifest && mfile)
-      make_manifest(jarfd, mfile);
-    else if(manifest)
-      make_manifest(jarfd, NULL);
-    
+      make_manifest(jarfd, mfile, action == ACTION_UPDATE);
+    else if(manifest && action == ACTION_CREATE)
+      make_manifest(jarfd, NULL, FALSE);
+
     init_args (new_argv, 0);
     /* now we add the files to the archive */
     while ((arg = get_next_arg ())){
@@ -514,17 +546,19 @@
 	const char *dir_to_change = get_next_arg ();
 	const char *file_to_add = get_next_arg ();
         if (!dir_to_change || !file_to_add) {
-          fprintf(stderr, "Error: missing argument for -C.\n");
-          exit(1);
-        }
-	if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add)) {
-          fprintf(stderr,
-                 "Error adding %s (in directory %s) to jar archive!\n",
-                 file_to_add, dir_to_change);
+          fprintf(stderr, "%s: error: missing argument for -C.\n", progname);
           exit(1);
         }
+	if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
+				action == ACTION_UPDATE))
+	  {
+	    fprintf(stderr,
+		    "Error adding %s (in directory %s) to jar archive!\n",
+		    file_to_add, dir_to_change);
+	    exit(1);
+	  }
       } else {
-        if(add_to_jar(jarfd, arg)){
+        if(add_to_jar(jarfd, arg, action == ACTION_UPDATE)){
           fprintf(stderr, "Error adding %s to jar archive!\n", arg);
           exit(1);
         }
@@ -533,11 +567,20 @@
     /* de-initialize the compression DS */
     if(do_compress)
       end_compression();
+
+    if (action == ACTION_UPDATE)
+      lseek (jarfd, end_of_entries, SEEK_SET);
     
     create_central_header(jarfd);
-    
-    if (close(jarfd) != 0) {
-      fprintf(stderr, "Error closing jar archive!\n");
+
+    /* Check if the file shrunk when we updated it. */
+    if (action == ACTION_UPDATE)
+      ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
+
+    if (jarfd != STDIN_FILENO && close(jarfd) != 0) {
+      fprintf(stderr, "%s: error closing jar archive: %s\n",
+	      progname, strerror (errno));
+      exit (1);
     }
   } else if(action == ACTION_LIST){
     list_jar(jarfd, &new_argv[0], new_argc);
@@ -688,7 +731,175 @@
   number_of_entries++;
 }
 
-int make_manifest(int jfd, const char *mf_name){
+static inline struct zipentry *
+find_entry (const char *fname)
+{
+  struct zipentry *ze;
+
+  for (ze = ziptail; ze; ze = ze->next_entry)
+    {
+      if (!strcmp (ze->filename, fname))
+	return ze;
+    }
+  return NULL;
+}
+
+
+static inline int
+looks_like_dir (const char *fname)
+{
+  struct zipentry *ze;
+  size_t len = strlen (fname);
+
+  for (ze = ziptail; ze; ze = ze->next_entry)
+    {
+      if (strlen (ze->filename) > len
+	  && !strncmp (fname, ze->filename, len)
+	  && ze->filename[len] == '/')
+	return 1;
+    }
+  return 0;
+}
+
+
+/*
+ * Read the zip entries of an existing file, building `ziplist' as we go.
+ */
+int read_entries (int fd)
+{
+  struct zipentry *ze;
+  ub1 intbuf[4];
+  ub1 header[46];
+  ub2 len;
+  ub2 count, i;
+  off_t offset;
+
+  if (lseek (fd, -22, SEEK_END) == -1)
+    {
+      fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
+      return 1;
+    }
+
+  if (read (fd, intbuf, 4) < 4)
+    {
+      perror (progname);
+      return 1;
+    }
+  /* Is there a zipfile comment? */
+  while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
+    {
+      if (lseek (fd, -5, SEEK_CUR) == -1 ||
+	  read (fd, intbuf, 4) != 4)
+	{
+	  fprintf (stderr, "%s: can't find end of central directory: %s\n",
+		   progname, strerror (errno));
+	  return 1;
+	}
+    }
+
+  /* Skip disk numbers. */
+  if (lseek (fd, 6, SEEK_CUR) == -1)
+    {
+      perror (progname);
+      return 1;
+    }
+
+  /* Number of entries in the central directory. */
+  if (read (fd, intbuf, 2) != 2)
+    {
+      perror (progname);
+      return 1;
+    }
+  count = UNPACK_UB2(intbuf, 0);
+
+  if (lseek (fd, 4, SEEK_CUR) == -1)
+    {
+      perror (progname);
+      return 1;
+    }
+
+  /* Offset where the central directory begins. */
+  if (read (fd, intbuf, 4) != 4)
+    {
+      perror (progname);
+      return 1;
+    }
+  offset = UNPACK_UB4(intbuf, 0);
+  end_of_entries = offset;
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    {
+      perror (progname);
+      return 1;
+    }
+
+  if (read (fd, header, 46) != 46)
+    {
+      fprintf (stderr, "%s: %s: unexpected end of file\n",
+	       progname, jarfile);
+      return 1;
+    }
+
+  for (i = 0; i < count; i++)
+    {
+      if (UNPACK_UB4(header, 0) != 0x02014b50)
+	{
+	  fprintf (stderr, "%s: can't find central directory header\n",
+		   progname);
+	  return 1;
+	}
+      ze = (struct zipentry *) malloc (sizeof (struct zipentry));
+      if (!ze)
+	{
+	  perror (progname);
+	  return 1;
+	}
+      memset (ze, 0, sizeof (struct zipentry));
+      ze->flags = UNPACK_UB2(header, CEN_FLAGS);
+      ze->mod_time = UNPACK_UB2(header, CEN_MODTIME);
+      ze->mod_date = UNPACK_UB2(header, CEN_MODDATE);
+      ze->crc = UNPACK_UB4(header, CEN_CRC);
+      ze->usize = UNPACK_UB4(header, CEN_USIZE);
+      ze->csize = UNPACK_UB4(header, CEN_CSIZE);
+      ze->offset = UNPACK_UB4(header, CEN_OFFSET);
+      ze->compressed = (header[CEN_COMP] || header[CEN_COMP+1]);
+      len = UNPACK_UB2(header, CEN_FNLEN);
+      ze->filename = (char *) malloc ((len+1) * sizeof (char));
+      if (!ze->filename)
+	{
+	  perror (progname);
+	  return 1;
+	}
+      if (read (fd, ze->filename, len) != len)
+	{
+	  fprintf (stderr, "%s: %s: unexpected end of file\n",
+		   progname, jarfile);
+	  return 1;
+	}
+      len = UNPACK_UB4(header, CEN_EFLEN);
+      len += UNPACK_UB4(header, CEN_COMLEN);
+      if (lseek (fd, len, SEEK_CUR) == -1)
+	{
+	  perror (progname);
+	  return 1;
+	}
+      add_entry (ze);
+      if (i < count - 1)
+	{
+	  if (read (fd, header, 46) != 46)
+	    {
+	      fprintf (stderr, "%s: %s: unexpected end of file\n",
+		       progname, jarfile);
+	      return 1;
+	    }
+	}
+    }
+
+  lseek (fd, 0, SEEK_SET);
+  return 0;
+}
+
+int make_manifest(int jfd, const char *mf_name, int updating){
   time_t current_time;
   int nlen;   /* length of file name */
   int mod_time; /* file modification time */
@@ -812,7 +1023,7 @@
       exit(1);
     }
 
-    if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
+    if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
       perror("error writing to jar");
       exit(1);
     }
@@ -823,9 +1034,16 @@
 }
 
 /* Implements -C by wrapping add_to_jar.  new_dir is the directory 
-   to switch to. */
+   to switch to.
+
+   `updating', if nonzero, will indicate that we are updating an
+   existing file, and will need to take special care. If set, we will
+   also expect that the linked list of zip entries will be filled in
+   with the jar file's current contents.
+ */
 int 
-add_to_jar_with_dir (int fd, const char* new_dir, const char* file)
+add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
+		     const int updating)
 {
   int retval;
   char old_dir[MAXPATHLEN]; 
@@ -837,7 +1055,7 @@
     perror(new_dir);
     return 1;
   }
-  retval=add_to_jar(fd, file);
+  retval=add_to_jar(fd, file, updating);
   if (chdir(old_dir) == -1) {
     perror(old_dir);
     return 1;
@@ -846,11 +1064,13 @@
 }
 
 int 
-add_to_jar (int fd, const char *file) {
+add_to_jar (int fd, const char *file, const int updating)
+{
   struct stat statbuf;
   DIR *dir;
   struct dirent *de;
   zipentry *ze;
+  zipentry *existing = NULL;
   int stat_return;
 
   /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com> 
@@ -917,9 +1137,6 @@
     PACK_UB2(file_header, LOC_FNLEN, nlen);
     PACK_UB4(file_header, LOC_MODTIME, mod_time);
 
-    if(verbose)
-      printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
-
     ze = (zipentry*)malloc(sizeof(zipentry));
     if(ze == NULL){
       perror("malloc");
@@ -936,10 +1153,36 @@
     ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
     ze->compressed = FALSE;
 
-    add_entry(ze);
+    if (updating)
+      {
+	if ((existing = find_entry (ze->filename)) != NULL)
+	  {
+	    if (existing->usize != 0)
+	      {
+		/* XXX overwriting non-directory with directory? */
+		fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
+			 progname, fullname);
+		return 1;
+	      }
+	  }
+	if (lseek (fd, end_of_entries, SEEK_SET) == -1)
+	  {
+	    fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
+	    perror ("lseek");
+	    return 1;
+	  }
+      }
+
+    if (!existing)
+      {
+	add_entry (ze);
+	write (fd, file_header, 30);
+	write (fd, fullname, nlen);
+	end_of_entries = lseek (fd, 0, SEEK_CUR);
 
-    write(fd, file_header, 30);
-    write(fd, fullname, nlen);
+	if (verbose)
+	  printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
+      }
 
     while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
       if(de->d_name[0] == '.')
@@ -953,7 +1196,7 @@
 
       strcpy(t_ptr, de->d_name);
 
-      if (add_to_jar(fd, fullname)) {
+      if (add_to_jar(fd, fullname, updating)) {
         fprintf(stderr, "Error adding file to jar!\n");
         return 1;
       }
@@ -971,7 +1214,7 @@
       return 1;
     }
     
-    if(add_file_to_jar(fd, add_fd, file, &statbuf)){
+    if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
       fprintf(stderr, "Error adding file to jar!\n");
       return 1;
     }
@@ -982,8 +1225,9 @@
   return 0;
 }
 
-int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
-
+int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
+		    const int updating)
+{
   unsigned short file_name_length;
   unsigned long mod_time;
   ub1 rd_buff[RDSZ];
@@ -991,6 +1235,18 @@
   off_t offset = 0;
   int rdamt;
   struct zipentry *ze;
+  struct zipentry *existing = NULL;
+
+  if (updating)
+    {
+      existing = find_entry (fname);
+      if (existing && looks_like_dir (fname))
+	{
+	  fprintf (stderr, "%s: %s is a directory in the archive\n",
+		   progname, fname);
+	  return 1;
+	}
+    }
 
   mod_time = unix2dostime(&(statbuf->st_mtime));
   file_name_length = strlen(fname);
@@ -1045,13 +1301,29 @@
 
   ze->csize = statbuf->st_size;
   ze->usize = ze->csize;
-  ze->offset = lseek(jfd, 0, SEEK_CUR);
+
+  if (existing)
+    ze->offset = existing->offset;
+  else if (updating)
+    ze->offset = end_of_entries;
+  else
+    ze->offset = lseek(jfd, 0, SEEK_CUR);
+
   if(do_compress)
     ze->compressed = TRUE;
   else
     ze->compressed = FALSE;
-  
-  add_entry(ze);
+
+  if (!existing)
+    add_entry(ze);
+  if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
+    {
+      perror ("lseek");
+      return 1;
+    }
+
+  /* We can safely write the header here, since it will be the same size
+     as before */
   
   /* Write the local header */
   write(jfd, file_header, 30);
@@ -1061,14 +1333,31 @@
 
 
   if(verbose){
-    printf("adding: %s ", fname);
+    if (existing)
+      printf ("updating: %s ", fname);
+    else
+      printf("adding: %s ", fname);
     fflush(stdout);
   }
  
   if(do_compress){
     /* compress the file */
-    compress_file(ffd, jfd, ze);
+    compress_file(ffd, jfd, ze, existing);
   } else {
+    /* If we are not writing the last entry, make space for it. */
+    if (existing && existing->next_entry)
+      {
+	if (ze->usize > existing->usize)
+	  {
+	    if (shift_down (jfd, existing->next_entry->offset,
+			    ze->usize - existing->usize, existing->next_entry))
+	      {
+		fprintf (stderr, "%s: %s\n", progname, strerror (errno));
+		return 1;
+	      }
+	  }
+      }
+
     /* Write the contents of the file (uncompressed) to the zip file */
     /* calculate the CRC as we go along */
     ze->crc = crc32(0L, Z_NULL, 0); 
@@ -1112,12 +1401,42 @@
     /* Sun's jar tool will only allow a data descriptor if the entry is
        compressed, but we'll save 16 bytes/entry if we only use it when
        we can't seek back on the file */
+    /* Technically, you CAN'T have a data descriptor unless the data
+       part has an obvious end, which DEFLATED does. Otherwise, there
+       would not be any way to determine where the data descriptor is.
+       Store an uncompressed file that ends with 0x504b0708, and see.
+       -- csm */
     
     if(write(jfd, data_descriptor, 16) != 16){
       perror("write");
       return 0;
     }
   }
+
+  if (existing)
+    {
+      int dd = (existing->flags & (1 << 3)) ? 12 : 0;
+      if (existing->next_entry && ze->csize < existing->csize + dd)
+	{
+	  if (shift_up (jfd, existing->next_entry->offset,
+			existing->csize + dd - ze->csize,
+			existing->next_entry))
+	    {
+	      perror (progname);
+	      return 1;
+	    }
+	}
+      /* Replace the existing entry data with this entry's. */
+      existing->csize = ze->csize;
+      existing->usize = ze->usize;
+      existing->crc = ze->crc;
+      existing->mod_time = ze->mod_time;
+      existing->mod_date = ze->mod_date;
+      free (ze->filename);
+      free (ze);
+    }
+  else if (updating)
+    end_of_entries = lseek (jfd, 0, SEEK_CUR);
   
   if(verbose)
     printf("(in=%d) (out=%d) (%s %d%%)\n", 
@@ -1890,7 +2209,7 @@
 {
   printf("jar (%s) %s\n\n", PACKAGE, VERSION);
   printf("Copyright 1999, 2000, 2001  Bryan Burns\n");
-  printf("Copyright 2002 Free Software Foundation\n");
+  printf("Copyright 2002, 2004 Free Software Foundation\n");
   printf("\
 This is free software; see the source for copying conditions.  There is NO\n\
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
diff -urN fastjar.old/jartool.h fastjar/jartool.h
--- fastjar.old/jartool.h	2000-12-09 04:08:23.000000000 +0100
+++ fastjar/jartool.h	2004-05-30 17:30:34.000000000 +0200
@@ -52,6 +52,10 @@
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#ifndef __FASTJAR_JARTOOL_H__
+#define __FASTJAR_JARTOOL_H__
+
+#include <sys/types.h>
 #include "config.h"
 
 #define ACTION_NONE 0
@@ -104,9 +108,12 @@
   ub4 usize;
   ub4 offset;
   ub1 compressed;
+  ub2 flags;
   char *filename;
   
   struct zipentry *next_entry;
 };
 
 typedef struct zipentry zipentry;
+
+#endif /* __FASTJAR_JARTOOL_H__ */
diff -urN fastjar.old/shift.c fastjar/shift.c
--- fastjar.old/shift.c	1970-01-01 01:00:00.000000000 +0100
+++ fastjar/shift.c	2004-05-30 17:30:34.000000000 +0200
@@ -0,0 +1,166 @@
+/* shift.c -- utilities to move regions of data in a file.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+  
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+  
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA.  */
+
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "jartool.h"
+#include "shift.h"
+
+#define BUFFER_SIZE 1024
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Shift the contents of a file up by `amount' bytes, starting at `begin'.
+ * The file is not truncated, data from `amount' to `begin - amount' is
+ * overwritten. The current file pointer of `fd' is preserved. Note that
+ * this might be past the new "end" of the file.
+ *
+ * If this function is passed a `struct zipentry', then all `offset'
+ * fields from that entry down the list that are greater than or equal
+ * to `begin' will be decreased by `amount'.
+ * 
+ * fd     - The file descriptor.
+ * begin  - The offset of the first byte that should be shifted.
+ * amount - The number of bytes to shift by.
+ * ze     - A pointer into a list of zip entries that should be updated
+ *          to reflect the modified offset.
+ */
+int
+shift_up (int fd, off_t begin, off_t amount, struct zipentry *ze)
+{
+  extern off_t end_of_entries;
+  int len, moved = 0;
+  ub1 buffer[BUFFER_SIZE];
+  off_t where, end, save;
+
+  if (amount <= 0)
+    return 0;
+
+  if ((save = lseek (fd, 0, SEEK_CUR)) == -1)
+    return 1;
+  if ((end = lseek (fd, 0, SEEK_END)) == -1)
+    return 1;
+  if (end < begin)
+    return 0;
+
+  where = begin;
+
+  do
+    {
+      if (lseek (fd, where, SEEK_SET) < 0)
+	return 1;
+      if ((len = read (fd, buffer, BUFFER_SIZE)) < 0)
+	return 1;
+      if (len == 0)
+	break;
+      if (lseek (fd, where - amount, SEEK_SET) < 0)
+	return 1;
+      if (write (fd, buffer, len) < 0)
+	return 1;
+      where += len;
+    }
+  while (where < end);
+
+  for (; ze; ze = ze->next_entry)
+    {
+      if (ze->offset >= begin)
+	{
+	  ze->offset -= amount;
+	  moved = 1;
+	}
+    }
+  if (moved)
+    end_of_entries -= amount;
+
+  if (lseek (fd, save, SEEK_SET) == -1)
+    return 1;
+  return 0;
+}
+
+/*
+ * Shift the contents of this file down by `amount' bytes, extending the
+ * end of file, starting at `begin'. This function will preserve the
+ * current file pointer of `fd'. Naturally, this function will fail if
+ * `fd' is not seekable.
+ *
+ * If this function is passed a `struct zipentry', then all `offset'
+ * fields from that entry down the list that are greater than or equal
+ * to `begin' will be increased by `amount'.
+ *
+ * fd     - The file descriptor.
+ * begin  - The offset of the first byte that should be shifted.
+ * amount - The number of bytes to shift by.
+ * ze     - A pointer into a list of zip entries that should be updated
+ *          to reflect the modified offset.
+ */
+int
+shift_down (int fd, off_t begin, off_t amount, struct zipentry *ze)
+{
+  extern off_t end_of_entries;
+  int off, len, moved = 0;
+  ub1 buffer[BUFFER_SIZE];
+  off_t where, save;
+
+  if (amount <= 0)
+    return 0;
+
+  if ((save = lseek (fd, 0, SEEK_CUR)) == -1)
+    return 1;
+  if ((where = lseek (fd, 0, SEEK_END)) == -1)
+    return 1;
+  if (where < begin)
+    return 0;
+  off = (where - begin) % BUFFER_SIZE;
+  if (off == 0)
+    where -= BUFFER_SIZE;
+  else
+    where -= off;
+
+  do
+    {
+      if (lseek (fd, where, SEEK_SET) < 0)
+	return 1;
+      if ((len = read (fd, buffer, BUFFER_SIZE)) < 0)
+	return 1;
+      if (lseek (fd, where + amount, SEEK_SET) < 0)
+	return 1;
+      if (write (fd, buffer, len) < 0)
+	return 1;
+      where -= BUFFER_SIZE;
+    }
+  while (where >= begin);
+
+  for (; ze; ze = ze->next_entry)
+    {
+      if (ze->offset >= begin)
+	{
+	  ze->offset += amount;
+	  moved = 1;
+	}
+    }
+  if (moved)
+    end_of_entries += amount;
+
+  if (lseek (fd, save, SEEK_SET) == -1)
+    return 1;
+
+  return 0;
+}
diff -urN fastjar.old/shift.h fastjar/shift.h
--- fastjar.old/shift.h	1970-01-01 01:00:00.000000000 +0100
+++ fastjar/shift.h	2004-05-30 17:30:34.000000000 +0200
@@ -0,0 +1,29 @@
+/* shift.h -- utilities to move regions of data in a file.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+  
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+  
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA.  */
+
+
+#ifndef __FASTJAR_SHIFT_H__
+#define __FASTJAR_SHIFT_H__
+
+#include <sys/types.h>
+#include "jartool.h"
+
+int shift_down (int, off_t, off_t, struct zipentry *);
+int shift_up (int, off_t, off_t, struct zipentry *);
+
+#endif /* __FASTJAR_SHIFT_H__ */
diff -urN fastjar.old/zipfile.h fastjar/zipfile.h
--- fastjar.old/zipfile.h	2000-12-09 04:08:23.000000000 +0100
+++ fastjar/zipfile.h	2004-05-30 17:30:34.000000000 +0200
@@ -47,6 +47,7 @@
 #define LOC_FNLEN   26 /* filename length */
 #define LOC_EFLEN   28 /* extra-field length */
 
+#define CEN_FLAGS    8
 #define CEN_COMP    10 /* compression method */
 #define CEN_MODTIME 12
 #define CEN_MODDATE 14
