Coherent FTP mirroring -- old files and dirs not present on server

Alexey Dokuchaev <[email protected]> Thu, 22 Sep 2005 16:34:39 +0700
Newsgroups gmane.comp.web.wget.patches
Message-ID <[email protected]>
--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello,

During last couple of years, I can see Wget is again under active
development.  So I've decided to refine my patch for version 1.8.2.

Wget still lacks a very useful feature of removing files, symlinks and
directories not any longer present on server while mirroring FTP sites,
I implemented it myself.  It might be of use to someone else, so I'm
submitting it here for review, in hope that it could be included in the
the next release of Wget in its refined form.  Now, applying the patch
teaches version 1.10.1 of Wget to accept -J (--remove-old) option to do
the job.

This patch essentially makes wget(1) useful for mirroring large FTP
sites (such as ftp.FreeBSD.org).

There are actually two versions of the patch (both attached).  One works
on *BSD/Linux, other works on any POSIX system (differences are in
`struct dirent' fields -- true POSIX has only d_name, and one has to
call lstat(2) to find out the type).

Enjoy!

./danfe

--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="wget-1.10.1-J-DIFF"

diff -ru wget-1.10.1.orig/src/ftp.c wget-1.10.1/src/ftp.c
--- wget-1.10.1.orig/src/ftp.c	Tue Aug  9 05:23:09 2005
+++ wget-1.10.1/src/ftp.c	Thu Sep 22 14:54:31 2005
@@ -41,6 +41,7 @@
 # include <unistd.h>
 #endif
 #include <sys/types.h>
+#include <sys/param.h>
 #include <assert.h>
 #include <errno.h>
 
@@ -1363,6 +1364,9 @@
 static struct fileinfo *delelement PARAMS ((struct fileinfo *,
 					    struct fileinfo **));
 static void freefileinfo PARAMS ((struct fileinfo *f));
+#ifndef WINDOWS
+static void ftp_remove_missing (char *, struct fileinfo *);
+#endif /* WINDOWS */
 
 /* Retrieve a list of files given in struct fileinfo linked list.  If
    a file is a symbolic link, do not retrieve it, but rather try to
@@ -1593,6 +1597,12 @@
       f = f->next;
     }
 
+#ifndef WINDOWS
+  if (opt.remove_old)
+    /* Now remove left-overs.  */
+    ftp_remove_missing (url_file_name (u), orig);
+#endif /* WINDOWS */
+
   /* We do not want to call ftp_retrieve_dirs here */
   if (opt.recursive &&
       !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel))
@@ -1928,3 +1938,224 @@
       f = next;
     }
 }
+
+#ifndef WINDOWS
+/* Synchronize local and remote directory contents (i.e. remove files,
+   directories, and symlinks missing on server).  We do this by obtaining
+   a list of local files first, sorting both linked lists, comparing
+   them, and removing local entries missing in server's file list.
+
+   This is required for coherent FTP mirroring.
+
+   Sorting is needed since we are not sure that both server and local
+   directory traversing will use the same sorting rules (hidden files
+   position in the list, case sensetivity, etc.  Without sorting at all,
+   comparing two file lists would take O(n^2) time, instead of O(n).
+
+   We could first check whether file lists are sorted in the same
+   manner, and if they are, do not attempt to further sort them, but
+   this seems kinda cumbersome and thus omitted.  If someone feels like
+   doing this, be my guest.
+
+   Implemented by Alexey Dokuchaev ([email protected]).  */
+
+static struct fileinfo *getlocallist (const char *, int *);
+int comparefileinfo (const void *, const void *);
+static void unlinkmissing (const char *, const char *);
+
+static void
+ftp_remove_missing (char *tgt, struct fileinfo *r)
+{
+  struct fileinfo **loc, **rmt, *l, *o;
+  int n = 1, m, i, j;
+
+  /* #### There probably is a better way of getting the number of list
+     elements, so I don't have to traverse the list.  I just didn't bother
+     to find it.  ;-) */
+  while (r->next)
+  {
+    n++;
+    r = r->next;
+  }
+  rmt = (struct fileinfo **)xmalloc (n * sizeof (struct fileinfo *));
+  for (i = n; i;)
+  {
+    rmt[--i] = r;
+    r = r->prev;
+  }
+
+  qsort (rmt, n, sizeof (struct fileinfo *), comparefileinfo);
+
+  j = strlen (tgt);
+  while (j && tgt[--j] != '/');
+  if (!j)
+    tgt[j++] = '.';
+  tgt[j] = '\0';
+
+  if (!(o = l = getlocallist (tgt, &m)))
+  {
+    logprintf (LOG_NOTQUIET, _("Failed to clean up `%s'.\n"), tgt);
+    xfree (rmt);
+    return;
+  }
+  else if (o == (struct fileinfo *)-1)
+  {
+    logprintf (LOG_VERBOSE, _("Directory `%s' is empty, nothing to clean up.\n"), tgt);
+    xfree (rmt);
+    return;
+  }
+
+  loc = (struct fileinfo **)xmalloc (m * sizeof (struct fileinfo *));
+  for (; m - i;)
+  {
+    loc[i++] = l;
+    l = l->next;
+  }
+
+  qsort (loc, m, sizeof (struct fileinfo *), comparefileinfo);
+
+  for (i = j = 0; i < n && j < m;)
+  {
+    int q = strcmp (rmt[i]->name, loc[j]->name);
+
+    if (q > 0)
+      unlinkmissing (tgt, loc[j++]->name);
+    else
+    {
+      i++;
+      if (!q)
+	j++;
+    }
+  }
+
+  /* If local list turned out longer than that of server, some files
+     need to be removed anyway.  Do it.  */
+  for (; j < m;)
+    unlinkmissing (tgt, loc[j++]->name);
+
+  freefileinfo (o);
+  xfree (rmt);
+  xfree (loc);
+}
+
+/* Obtain linked list of local directory contents.  */
+static struct fileinfo *
+getlocallist (const char *dir, int *n)
+{
+  DIR *d;
+  struct dirent *dp;
+  struct fileinfo *cur, *prev = NULL, *orig = (struct fileinfo *)-1;
+  size_t len;
+
+  if (!(d = opendir (dir)))
+    return NULL;
+
+  for (*n = 0; (dp = readdir (d));)
+  {
+    /* #### should check for DT_DIR || DT_REG || DT_LNK here.  Also,
+       might consider removing redundand strcmp()ing.  */
+    if (strcmp (dp->d_name, ".") && strcmp (dp->d_name, ".."))
+    {
+      cur = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+      cur->type = dp->d_type;
+      cur->name = (char *)xmalloc (len = (strlen (dp->d_name) + 1));
+      memcpy (cur->name, dp->d_name, len);
+      cur->linkto = NULL;   /* for freefileinfo() */
+      cur->prev = prev;
+      cur->next = NULL;
+      if (prev)
+	prev->next = cur;
+      prev = cur;
+      if (orig == (struct fileinfo *)-1)
+	orig = cur;
+      (*n)++;
+    }
+  }
+  closedir (d);
+  return orig;   /* don't forget to freefileinfo() it! */
+}
+
+static int removedir (const char *);
+
+static void
+unlinkmissing (const char *path, const char *nm)
+{
+  struct stat sb;
+  char *fp = xmalloc (strlen (path) + strlen (nm) + 1);
+
+  strcpy (fp, path); strcat (fp, "/"); strcat (fp, nm);
+
+  /* We only want to remove regular files, directories and symbolic
+     links.  Do not touch files of other types (pipes, sockets, etc).  */
+  if (!lstat (fp, &sb))
+  {
+    if (S_ISREG (sb.st_mode))
+    {
+      if (unlink (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale file `%s': %s.\n"), nm,
+			strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale file `%s'.\n"), nm);
+    }
+    else if (S_ISLNK (sb.st_mode))
+    {
+      if (unlink (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale symlink `%s': %s.\n"), nm,
+			strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale symlink `%s'.\n"), nm);
+    }
+    else if (S_ISDIR (sb.st_mode))
+    {
+      if (removedir (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale directory `%s': %s.\n"),
+			nm, strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale directory `%s'.\n"), nm);
+    }
+  }
+  xfree (fp);
+}
+
+static int
+removedir (const char *path)
+{
+  DIR *d;
+  struct dirent *dp;
+  char *fp, *p;
+  int r = 0;
+
+  if (!(d = opendir (path)))
+    return 1;
+
+  fp = xmalloc (strlen (path) + MAXPATHLEN);
+  strcpy (fp, path); strcat (fp, "/");
+  p = fp + strlen (fp);
+
+  while ((dp = readdir (d)) != NULL)
+  {
+    if (strcmp (dp->d_name, ".") && strcmp (dp->d_name, ".."))
+    {
+      strcat (fp, dp->d_name);
+
+      if (dp->d_type != DT_DIR)
+	r |= unlink (fp);
+      else
+	r |= removedir (fp);
+
+      *p = '\0';
+    }
+  }
+
+  xfree (fp);
+  closedir (d);
+  r |= rmdir (path);
+  return r;
+}
+
+int
+comparefileinfo (const void *a, const void *b)
+{
+  return strcmp((*(struct fileinfo **)a)->name, (*(struct fileinfo **)b)->name);
+}
+#endif /* WINDOWS */
diff -ru wget-1.10.1.orig/src/ftp.h wget-1.10.1/src/ftp.h
--- wget-1.10.1.orig/src/ftp.h	Thu Apr  7 03:42:22 2005
+++ wget-1.10.1/src/ftp.h	Thu Sep 22 15:04:45 2005
@@ -30,6 +30,9 @@
 #ifndef FTP_H
 #define FTP_H
 
+/* Need it for enum ftype. */
+#include <dirent.h>
+
 #include "host.h"
 
 /* System types. */
@@ -71,10 +74,10 @@
 /* File types.  */
 enum ftype
 {
-  FT_PLAINFILE,
-  FT_DIRECTORY,
-  FT_SYMLINK,
-  FT_UNKNOWN
+  FT_PLAINFILE = DT_REG,
+  FT_DIRECTORY = DT_DIR,
+  FT_SYMLINK = DT_LNK,
+  FT_UNKNOWN = DT_UNKNOWN
 };
 
 
diff -ru wget-1.10.1.orig/src/init.c wget-1.10.1/src/init.c
--- wget-1.10.1.orig/src/init.c	Tue Aug  9 05:54:16 2005
+++ wget-1.10.1/src/init.c	Thu Sep 22 15:11:57 2005
@@ -221,6 +221,9 @@
   { "reject",		&opt.rejects,		cmd_vector },
   { "relativeonly",	&opt.relative_only,	cmd_boolean },
   { "removelisting",	&opt.remove_listing,	cmd_boolean },
+#ifndef WINDOWS
+  { "removeold",	&opt.remove_old,	cmd_boolean },
+#endif /* WINDOWS */
   { "restrictfilenames", NULL,			cmd_spec_restrict_file_names },
   { "retrsymlinks",	&opt.retr_symlinks,	cmd_boolean },
   { "retryconnrefused",	&opt.retry_connrefused,	cmd_boolean },
@@ -320,6 +323,10 @@
   opt.restrict_files_os = restrict_windows;
 #endif
   opt.restrict_files_ctrl = 1;
+
+#ifndef WINDOWS
+  opt.remove_old = 0;
+#endif /* WINDOWS */
 }
 
 /* Return the user's home directory (strdup-ed), or NULL if none is
diff -ru wget-1.10.1.orig/src/main.c wget-1.10.1/src/main.c
--- wget-1.10.1.orig/src/main.c	Fri Jul  1 08:20:30 2005
+++ wget-1.10.1/src/main.c	Thu Sep 22 15:18:15 2005
@@ -245,6 +245,7 @@
     { "reject", 'R', OPT_VALUE, "reject", -1 },
     { "relative", 'L', OPT_BOOLEAN, "relativeonly", -1 },
     { "remove-listing", 0, OPT_BOOLEAN, "removelisting", -1 },
+    { "remove-old", 'J', OPT_BOOLEAN, "removeold", -1 },
     { "restrict-file-names", 0, OPT_BOOLEAN, "restrictfilenames", -1 },
     { "retr-symlinks", 0, OPT_BOOLEAN, "retrsymlinks", -1 },
     { "retry-connrefused", 0, OPT_BOOLEAN, "retryconnrefused", -1 },
@@ -602,6 +603,8 @@
   -K,  --backup-converted   before converting file X, back up as X.orig.\n"),
     N_("\
   -m,  --mirror             shortcut for -N -r -l inf --no-remove-listing.\n"),
+    N_("\
+  -J,  --remove-old         remove files and directories not present on server.\n"),
     N_("\
   -p,  --page-requisites    get all images, etc. needed to display HTML page.\n"),
     N_("\
diff -ru wget-1.10.1.orig/src/options.h wget-1.10.1/src/options.h
--- wget-1.10.1.orig/src/options.h	Tue Aug  9 05:54:16 2005
+++ wget-1.10.1/src/options.h	Thu Sep 22 15:19:25 2005
@@ -140,6 +140,10 @@
 				   locally? */
   int remove_listing;		/* Do we remove .listing files
 				   generated by FTP? */
+#ifndef WINDOWS
+  int remove_old;		/* Do we remove files and directories
+				   not present on FTP server? */
+#endif /* WINDOWS */
   int htmlify;			/* Do we HTML-ify the OS-dependent
 				   listings? */
 

--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="wget-1.10.1-J-DIFF-POSIX"

diff -ru wget-1.10.1.orig/src/ftp.c wget-1.10.1/src/ftp.c
--- wget-1.10.1.orig/src/ftp.c	Tue Aug  9 05:23:09 2005
+++ wget-1.10.1/src/ftp.c	Thu Sep 22 16:15:15 2005
@@ -41,6 +41,7 @@
 # include <unistd.h>
 #endif
 #include <sys/types.h>
+#include <sys/param.h>
 #include <assert.h>
 #include <errno.h>
 
@@ -1363,6 +1364,9 @@
 static struct fileinfo *delelement PARAMS ((struct fileinfo *,
 					    struct fileinfo **));
 static void freefileinfo PARAMS ((struct fileinfo *f));
+#ifndef WINDOWS
+static void ftp_remove_missing (char *, struct fileinfo *);
+#endif /* WINDOWS */
 
 /* Retrieve a list of files given in struct fileinfo linked list.  If
    a file is a symbolic link, do not retrieve it, but rather try to
@@ -1593,6 +1597,12 @@
       f = f->next;
     }
 
+#ifndef WINDOWS
+  if (opt.remove_old)
+    /* Now remove left-overs.  */
+    ftp_remove_missing (url_file_name (u), orig);
+#endif /* WINDOWS */
+
   /* We do not want to call ftp_retrieve_dirs here */
   if (opt.recursive &&
       !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel))
@@ -1928,3 +1938,229 @@
       f = next;
     }
 }
+
+#ifndef WINDOWS
+/* Synchronize local and remote directory contents (i.e. remove files,
+   directories, and symlinks missing on server).  We do this by obtaining
+   a list of local files first, sorting both linked lists, comparing
+   them, and removing local entries missing in server's file list.
+
+   This is required for coherent FTP mirroring.
+
+   Sorting is needed since we are not sure that both server and local
+   directory traversing will use the same sorting rules (hidden files
+   position in the list, case sensetivity, etc.  Without sorting at all,
+   comparing two file lists would take O(n^2) time, instead of O(n).
+
+   We could first check whether file lists are sorted in the same
+   manner, and if they are, do not attempt to further sort them, but
+   this seems kinda cumbersome and thus omitted.  If someone feels like
+   doing this, be my guest.
+
+   Implemented by Alexey Dokuchaev ([email protected]).  */
+
+static struct fileinfo *getlocallist (const char *, int *);
+int comparefileinfo (const void *, const void *);
+static void unlinkmissing (const char *, const char *);
+
+static void
+ftp_remove_missing (char *tgt, struct fileinfo *r)
+{
+  struct fileinfo **loc, **rmt, *l, *o;
+  int n = 1, m, i, j;
+
+  /* #### There probably is a better way of getting the number of list
+     elements, so I don't have to traverse the list.  I just didn't bother
+     to find it.  ;-) */
+  while (r->next)
+  {
+    n++;
+    r = r->next;
+  }
+  rmt = (struct fileinfo **)xmalloc (n * sizeof (struct fileinfo *));
+  for (i = n; i;)
+  {
+    rmt[--i] = r;
+    r = r->prev;
+  }
+
+  qsort (rmt, n, sizeof (struct fileinfo *), comparefileinfo);
+
+  j = strlen (tgt);
+  while (j && tgt[--j] != '/');
+  if (!j)
+    tgt[j++] = '.';
+  tgt[j] = '\0';
+
+  if (!(o = l = getlocallist (tgt, &m)))
+  {
+    logprintf (LOG_NOTQUIET, _("Failed to clean up `%s'.\n"), tgt);
+    xfree (rmt);
+    return;
+  }
+  else if (o == (struct fileinfo *)-1)
+  {
+    logprintf (LOG_VERBOSE, _("Directory `%s' is empty, nothing to clean up.\n"), tgt);
+    xfree (rmt);
+    return;
+  }
+
+  loc = (struct fileinfo **)xmalloc (m * sizeof (struct fileinfo *));
+  for (; m - i;)
+  {
+    loc[i++] = l;
+    l = l->next;
+  }
+
+  qsort (loc, m, sizeof (struct fileinfo *), comparefileinfo);
+
+  for (i = j = 0; i < n && j < m;)
+  {
+    int q = strcmp (rmt[i]->name, loc[j]->name);
+
+    if (q > 0)
+      unlinkmissing (tgt, loc[j++]->name);
+    else
+    {
+      i++;
+      if (!q)
+	j++;
+    }
+  }
+
+  /* If local list turned out longer than that of server, some files
+     need to be removed anyway.  Do it.  */
+  for (; j < m;)
+    unlinkmissing (tgt, loc[j++]->name);
+
+  freefileinfo (o);
+  xfree (rmt);
+  xfree (loc);
+}
+
+/* Obtain linked list of local directory contents.  */
+static struct fileinfo *
+getlocallist (const char *dir, int *n)
+{
+  DIR *d;
+  struct dirent *dp;
+  struct fileinfo *cur, *prev = NULL, *orig = (struct fileinfo *)-1;
+  size_t len;
+
+  if (!(d = opendir (dir)))
+    return NULL;
+
+  for (*n = 0; (dp = readdir (d));)
+  {
+    /* #### should check for DT_DIR || DT_REG || DT_LNK here.  Also,
+       might consider removing redundand strcmp()ing.  */
+    if (strcmp (dp->d_name, ".") && strcmp (dp->d_name, ".."))
+    {
+      struct stat sb;
+
+      cur = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
+      cur->name = (char *)xmalloc (len = (strlen (dp->d_name) + 1));
+      memcpy (cur->name, dp->d_name, len);
+      if (!lstat (dp->d_name, &sb))
+	cur->type = sb.st_mode & S_IFMT;
+      else
+	cur->type = FT_UNKNOWN;
+      cur->linkto = NULL;   /* for freefileinfo() */
+      cur->prev = prev;
+      cur->next = NULL;
+      if (prev)
+	prev->next = cur;
+      prev = cur;
+      if (orig == (struct fileinfo *)-1)
+	orig = cur;
+      (*n)++;
+    }
+  }
+  closedir (d);
+  return orig;   /* don't forget to freefileinfo() it! */
+}
+
+static int removedir (const char *);
+
+static void
+unlinkmissing (const char *path, const char *nm)
+{
+  struct stat sb;
+  char *fp = xmalloc (strlen (path) + strlen (nm) + 1);
+
+  strcpy (fp, path); strcat (fp, "/"); strcat (fp, nm);
+
+  /* We only want to remove regular files, directories and symbolic
+     links.  Do not touch files of other types (pipes, sockets, etc).  */
+  if (!lstat (fp, &sb))
+  {
+    if (S_ISREG (sb.st_mode))
+    {
+      if (unlink (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale file `%s': %s.\n"), nm,
+			strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale file `%s'.\n"), nm);
+    }
+    else if (S_ISLNK (sb.st_mode))
+    {
+      if (unlink (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale symlink `%s': %s.\n"), nm,
+			strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale symlink `%s'.\n"), nm);
+    }
+    else if (S_ISDIR (sb.st_mode))
+    {
+      if (removedir (fp))
+	logprintf (LOG_NOTQUIET, _("Could not remove stale directory `%s': %s.\n"),
+			nm, strerror (errno));
+      else
+	logprintf (LOG_VERBOSE, _("Removed stale directory `%s'.\n"), nm);
+    }
+  }
+  xfree (fp);
+}
+
+static int
+removedir (const char *path)
+{
+  DIR *d;
+  struct dirent *dp;
+  char *fp, *p;
+  int r = 0;
+
+  if (!(d = opendir (path)))
+    return 1;
+
+  fp = xmalloc (strlen (path) + MAXPATHLEN);
+  strcpy (fp, path); strcat (fp, "/");
+  p = fp + strlen (fp);
+
+  while ((dp = readdir (d)) != NULL)
+  {
+    if (strcmp (dp->d_name, ".") && strcmp (dp->d_name, ".."))
+    {
+      strcat (fp, dp->d_name);
+
+      if (dp->d_type != DT_DIR)
+	r |= unlink (fp);
+      else
+	r |= removedir (fp);
+
+      *p = '\0';
+    }
+  }
+
+  xfree (fp);
+  closedir (d);
+  r |= rmdir (path);
+  return r;
+}
+
+int
+comparefileinfo (const void *a, const void *b)
+{
+  return strcmp((*(struct fileinfo **)a)->name, (*(struct fileinfo **)b)->name);
+}
+#endif /* WINDOWS */
diff -ru wget-1.10.1.orig/src/ftp.h wget-1.10.1/src/ftp.h
--- wget-1.10.1.orig/src/ftp.h	Thu Apr  7 03:42:22 2005
+++ wget-1.10.1/src/ftp.h	Thu Sep 22 16:14:17 2005
@@ -30,6 +30,9 @@
 #ifndef FTP_H
 #define FTP_H
 
+/* Need it for enum ftype. */
+#include <dirent.h>
+
 #include "host.h"
 
 /* System types. */
@@ -71,10 +74,10 @@
 /* File types.  */
 enum ftype
 {
-  FT_PLAINFILE,
-  FT_DIRECTORY,
-  FT_SYMLINK,
-  FT_UNKNOWN
+  FT_PLAINFILE = DT_REG,
+  FT_DIRECTORY = DT_DIR,
+  FT_SYMLINK = DT_LNK,
+  FT_UNKNOWN = DT_UNKNOWN
 };
 
 
diff -ru wget-1.10.1.orig/src/init.c wget-1.10.1/src/init.c
--- wget-1.10.1.orig/src/init.c	Tue Aug  9 05:54:16 2005
+++ wget-1.10.1/src/init.c	Thu Sep 22 16:14:17 2005
@@ -221,6 +221,9 @@
   { "reject",		&opt.rejects,		cmd_vector },
   { "relativeonly",	&opt.relative_only,	cmd_boolean },
   { "removelisting",	&opt.remove_listing,	cmd_boolean },
+#ifndef WINDOWS
+  { "removeold",	&opt.remove_old,	cmd_boolean },
+#endif /* WINDOWS */
   { "restrictfilenames", NULL,			cmd_spec_restrict_file_names },
   { "retrsymlinks",	&opt.retr_symlinks,	cmd_boolean },
   { "retryconnrefused",	&opt.retry_connrefused,	cmd_boolean },
@@ -320,6 +323,10 @@
   opt.restrict_files_os = restrict_windows;
 #endif
   opt.restrict_files_ctrl = 1;
+
+#ifndef WINDOWS
+  opt.remove_old = 0;
+#endif /* WINDOWS */
 }
 
 /* Return the user's home directory (strdup-ed), or NULL if none is
diff -ru wget-1.10.1.orig/src/main.c wget-1.10.1/src/main.c
--- wget-1.10.1.orig/src/main.c	Fri Jul  1 08:20:30 2005
+++ wget-1.10.1/src/main.c	Thu Sep 22 16:14:17 2005
@@ -245,6 +245,7 @@
     { "reject", 'R', OPT_VALUE, "reject", -1 },
     { "relative", 'L', OPT_BOOLEAN, "relativeonly", -1 },
     { "remove-listing", 0, OPT_BOOLEAN, "removelisting", -1 },
+    { "remove-old", 'J', OPT_BOOLEAN, "removeold", -1 },
     { "restrict-file-names", 0, OPT_BOOLEAN, "restrictfilenames", -1 },
     { "retr-symlinks", 0, OPT_BOOLEAN, "retrsymlinks", -1 },
     { "retry-connrefused", 0, OPT_BOOLEAN, "retryconnrefused", -1 },
@@ -602,6 +603,8 @@
   -K,  --backup-converted   before converting file X, back up as X.orig.\n"),
     N_("\
   -m,  --mirror             shortcut for -N -r -l inf --no-remove-listing.\n"),
+    N_("\
+  -J,  --remove-old         remove files and directories not present on server.\n"),
     N_("\
   -p,  --page-requisites    get all images, etc. needed to display HTML page.\n"),
     N_("\
diff -ru wget-1.10.1.orig/src/options.h wget-1.10.1/src/options.h
--- wget-1.10.1.orig/src/options.h	Tue Aug  9 05:54:16 2005
+++ wget-1.10.1/src/options.h	Thu Sep 22 16:14:17 2005
@@ -140,6 +140,10 @@
 				   locally? */
   int remove_listing;		/* Do we remove .listing files
 				   generated by FTP? */
+#ifndef WINDOWS
+  int remove_old;		/* Do we remove files and directories
+				   not present on FTP server? */
+#endif /* WINDOWS */
   int htmlify;			/* Do we HTML-ify the OS-dependent
 				   listings? */
 

--X1bOJ3K7DJ5YkBrT--