]> Dogcows Code - chaz/tar/blobdiff - src/incremen.c
Add suffix.c
[chaz/tar] / src / incremen.c
index c0d515dd32860e9240720dc9e084b1cdea900fbc..bc5fdaded62509ed85bf5b4b9f50a37ad2223489 100644 (file)
@@ -1,11 +1,11 @@
 /* GNU dump extensions to tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
 /* GNU dump extensions to tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007 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
 
    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, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -18,7 +18,6 @@
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <system.h>
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include <system.h>
-#include <getline.h>
 #include <hash.h>
 #include <quotearg.h>
 #include "common.h"
 #include <hash.h>
 #include <quotearg.h>
 #include "common.h"
@@ -46,7 +45,7 @@ enum children
 #define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
 #define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
 #define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
 #define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
 #define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
 #define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
-                      
+
 #define DIR_SET_FLAG(d,f) (d)->flags |= (f)
 #define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
 
 #define DIR_SET_FLAG(d,f) (d)->flags |= (f)
 #define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
 
@@ -63,6 +62,8 @@ struct directory
     unsigned flags;             /* See DIRF_ macros above */
     struct directory *orig;     /* If the directory was renamed, points to
                                   the original directory structure */
     unsigned flags;             /* See DIRF_ macros above */
     struct directory *orig;     /* If the directory was renamed, points to
                                   the original directory structure */
+    const char *tagfile;        /* Tag file, if the directory falls under
+                                  exclusion_tag_under */
     char name[1];              /* file name of directory */
   };
 
     char name[1];              /* file name of directory */
   };
 
@@ -125,9 +126,10 @@ make_directory (const char *name)
   strcpy (directory->name, name);
   if (ISSLASH (directory->name[namelen-1]))
     directory->name[namelen-1] = 0;
   strcpy (directory->name, name);
   if (ISSLASH (directory->name[namelen-1]))
     directory->name[namelen-1] = 0;
+  directory->tagfile = NULL;
   return directory;
 }
   return directory;
 }
-  
+
 /* Create and link a new directory entry for directory NAME, having a
    device number DEV and an inode number INO, with NFS indicating
    whether it is an NFS device and FOUND indicating whether we have
 /* Create and link a new directory entry for directory NAME, having a
    device number DEV and an inode number INO, with NFS indicating
    whether it is an NFS device and FOUND indicating whether we have
@@ -154,7 +156,7 @@ note_directory (char const *name, struct timespec mtime,
     }
   else
     directory->contents = NULL;
     }
   else
     directory->contents = NULL;
-  
+
   if (! ((directory_table
          || (directory_table = hash_initialize (0, 0,
                                                 hash_directory_name,
   if (! ((directory_table
          || (directory_table = hash_initialize (0, 0,
                                                 hash_directory_name,
@@ -211,7 +213,7 @@ void
 update_parent_directory (const char *name)
 {
   struct directory *directory;
 update_parent_directory (const char *name)
 {
   struct directory *directory;
-  char *p, *name_buffer;
+  char *p;
 
   p = dir_name (name);
   directory = find_directory (p);
 
   p = dir_name (name);
   directory = find_directory (p);
@@ -230,17 +232,17 @@ static struct directory *
 procdir (char *name_buffer, struct stat *stat_data,
         dev_t device,
         enum children children,
 procdir (char *name_buffer, struct stat *stat_data,
         dev_t device,
         enum children children,
-        bool verbose)
+        bool verbose,
+        char *entry)
 {
   struct directory *directory;
   bool nfs = NFS_FILE_STAT (*stat_data);
 {
   struct directory *directory;
   bool nfs = NFS_FILE_STAT (*stat_data);
-  struct name *np;
 
   if ((directory = find_directory (name_buffer)) != NULL)
     {
       if (DIR_IS_INITED (directory))
        return directory;
 
   if ((directory = find_directory (name_buffer)) != NULL)
     {
       if (DIR_IS_INITED (directory))
        return directory;
-      
+
       /* With NFS, the same file can have two different devices
         if an NFS directory is mounted in multiple locations,
         which is relatively common when automounting.
       /* With NFS, the same file can have two different devices
         if an NFS directory is mounted in multiple locations,
         which is relatively common when automounting.
@@ -279,7 +281,7 @@ procdir (char *name_buffer, struct stat *stat_data,
        }
       else
        directory->children = CHANGED_CHILDREN;
        }
       else
        directory->children = CHANGED_CHILDREN;
-      
+
       DIR_SET_FLAG (directory, DIRF_FOUND);
     }
   else
       DIR_SET_FLAG (directory, DIRF_FOUND);
     }
   else
@@ -311,7 +313,7 @@ procdir (char *name_buffer, struct stat *stat_data,
          if (verbose)
            WARN ((0, 0, _("%s: Directory is new"),
                   quotearg_colon (name_buffer)));
          if (verbose)
            WARN ((0, 0, _("%s: Directory is new"),
                   quotearg_colon (name_buffer)));
-         directory->children = 
+         directory->children =
            (listed_incremental_option
             || (OLDER_STAT_TIME (*stat_data, m)
                 || (after_date_option
            (listed_incremental_option
             || (OLDER_STAT_TIME (*stat_data, m)
                 || (after_date_option
@@ -325,13 +327,47 @@ procdir (char *name_buffer, struct stat *stat_data,
      omit it... */
   if (one_file_system_option && device != stat_data->st_dev
       /* ... except if it was explicitely given in the command line */
      omit it... */
   if (one_file_system_option && device != stat_data->st_dev
       /* ... except if it was explicitely given in the command line */
-      && !((np = name_scan (name_buffer, true)) && np->explicit))
+      && !is_individual_file (name_buffer))
     directory->children = NO_CHILDREN;
     directory->children = NO_CHILDREN;
-  else if (children == ALL_CHILDREN) 
+  else if (children == ALL_CHILDREN)
     directory->children = ALL_CHILDREN;
 
   DIR_SET_FLAG (directory, DIRF_INIT);
     directory->children = ALL_CHILDREN;
 
   DIR_SET_FLAG (directory, DIRF_INIT);
-  
+
+  {
+    const char *tag_file_name;
+    size_t len;
+    
+    switch (check_exclusion_tags (name_buffer, &tag_file_name))
+      {
+      case exclusion_tag_all:
+       /* This warning can be duplicated by code in dump_file0, but only
+          in case when the topmost directory being archived contains
+          an exclusion tag. */
+       exclusion_tag_warning (name_buffer, tag_file_name,
+                              _("directory not dumped"));
+       if (entry)
+         *entry = 'N';
+       directory->children = NO_CHILDREN;
+       break;
+
+      case exclusion_tag_contents:
+       exclusion_tag_warning (name_buffer, tag_file_name,
+                              _("contents not dumped"));
+       directory->children = NO_CHILDREN;
+       break;
+       
+      case exclusion_tag_under:
+       exclusion_tag_warning (name_buffer, tag_file_name,
+                              _("contents not dumped"));
+       directory->tagfile = tag_file_name;
+       break;
+       
+      case exclusion_tag_none:
+       break;
+      }
+  }
+
   return directory;
 }
 
   return directory;
 }
 
@@ -344,7 +380,7 @@ dumpdir_locate (const char *dump, const char *name)
     while (*dump)
       {
        /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
     while (*dump)
       {
        /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
-          alphabetical ordering. 
+          alphabetical ordering.
           They normally do not occur in dumpdirs from the snapshot files,
           but this function is also used by purge_directory, which operates
           on a dumpdir from the archive, hence the need for this test. */
           They normally do not occur in dumpdirs from the snapshot files,
           but this function is also used by purge_directory, which operates
           on a dumpdir from the archive, hence the need for this test. */
@@ -379,7 +415,9 @@ dumpdir_size (const char *p)
 static int
 compare_dirnames (const void *first, const void *second)
 {
 static int
 compare_dirnames (const void *first, const void *second)
 {
-  return strcmp (*(const char**)first, *(const char**)second);
+  char const *const *name1 = first;
+  char const *const *name2 = second;
+  return strcmp (*name1, *name2);
 }
 
 /* Compare dumpdir array from DIRECTORY with directory listing DIR and
 }
 
 /* Compare dumpdir array from DIRECTORY with directory listing DIR and
@@ -388,7 +426,7 @@ compare_dirnames (const void *first, const void *second)
    DIR must be returned by a previous call to savedir().
 
    File names in DIRECTORY->contents must be sorted
    DIR must be returned by a previous call to savedir().
 
    File names in DIRECTORY->contents must be sorted
-   alphabetically. 
+   alphabetically.
 
    DIRECTORY->contents is replaced with the created template. Each entry is
    prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
 
    DIRECTORY->contents is replaced with the created template. Each entry is
    prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
@@ -436,9 +474,17 @@ makedumpdir (struct directory *directory, const char *dir)
       const char *loc = dumpdir_locate (dump, array[i]);
       if (loc)
        {
       const char *loc = dumpdir_locate (dump, array[i]);
       if (loc)
        {
-         *new_dump_ptr++ = ' ';
+         if (directory->tagfile)
+           *new_dump_ptr = strcmp (directory->tagfile, array[i]) == 0 ?
+                              ' ' : 'I';
+         else
+           *new_dump_ptr = ' ';
+         new_dump_ptr++;
          dump = loc + strlen (loc) + 1;
        }
          dump = loc + strlen (loc) + 1;
        }
+      else if (directory->tagfile)
+       *new_dump_ptr++ = strcmp (directory->tagfile, array[i]) == 0 ?
+                              ' ' : 'I';
       else
        *new_dump_ptr++ = 'Y'; /* New entry */
 
       else
        *new_dump_ptr++ = 'Y'; /* New entry */
 
@@ -454,9 +500,9 @@ makedumpdir (struct directory *directory, const char *dir)
 
 /* Recursively scan the given directory. */
 static char *
 
 /* Recursively scan the given directory. */
 static char *
-scan_directory (char *dir_name, dev_t device)
+scan_directory (char *dir, dev_t device)
 {
 {
-  char *dirp = savedir (dir_name);     /* for scanning directory */
+  char *dirp = savedir (dir);  /* for scanning directory */
   char *name_buffer;           /* directory, `/', and directory member */
   size_t name_buffer_size;     /* allocated size of name_buffer, minus 2 */
   size_t name_length;          /* used length in name_buffer */
   char *name_buffer;           /* directory, `/', and directory member */
   size_t name_buffer_size;     /* allocated size of name_buffer, minus 2 */
   size_t name_length;          /* used length in name_buffer */
@@ -464,19 +510,19 @@ scan_directory (char *dir_name, dev_t device)
   struct directory *directory;
   
   if (! dirp)
   struct directory *directory;
   
   if (! dirp)
-    savedir_error (dir_name);
+    savedir_error (dir);
 
 
-  name_buffer_size = strlen (dir_name) + NAME_FIELD_SIZE;
+  name_buffer_size = strlen (dir) + NAME_FIELD_SIZE;
   name_buffer = xmalloc (name_buffer_size + 2);
   name_buffer = xmalloc (name_buffer_size + 2);
-  strcpy (name_buffer, dir_name);
-  if (! ISSLASH (dir_name[strlen (dir_name) - 1]))
+  strcpy (name_buffer, dir);
+  if (! ISSLASH (dir[strlen (dir) - 1]))
     strcat (name_buffer, "/");
   name_length = strlen (name_buffer);
 
   if (deref_stat (dereference_option, name_buffer, &stat_data))
     {
       stat_diag (name_buffer);
     strcat (name_buffer, "/");
   name_length = strlen (name_buffer);
 
   if (deref_stat (dereference_option, name_buffer, &stat_data))
     {
       stat_diag (name_buffer);
-      /* FIXME: used to be 
+      /* FIXME: used to be
            children = CHANGED_CHILDREN;
         but changed to: */
       free (name_buffer);
            children = CHANGED_CHILDREN;
         but changed to: */
       free (name_buffer);
@@ -484,8 +530,9 @@ scan_directory (char *dir_name, dev_t device)
       return NULL;
     }
 
       return NULL;
     }
 
-  directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
-    
+  directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false,
+                      NULL);
+  
   if (dirp && directory->children != NO_CHILDREN)
     {
       char  *entry;    /* directory entry being scanned */
   if (dirp && directory->children != NO_CHILDREN)
     {
       char  *entry;    /* directory entry being scanned */
@@ -505,8 +552,10 @@ scan_directory (char *dir_name, dev_t device)
              name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
            }
          strcpy (name_buffer + name_length, entry + 1);
              name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
            }
          strcpy (name_buffer + name_length, entry + 1);
-         
-         if (excluded_name (name_buffer))
+
+         if (*entry == 'I') /* Ignored entry */
+           *entry = 'N';
+         else if (excluded_name (name_buffer))
            *entry = 'N';
          else
            {
            *entry = 'N';
          else
            {
@@ -516,23 +565,23 @@ scan_directory (char *dir_name, dev_t device)
                  *entry = 'N';
                  continue;
                }
                  *entry = 'N';
                  continue;
                }
-             
+
              if (S_ISDIR (stat_data.st_mode))
                {
              if (S_ISDIR (stat_data.st_mode))
                {
+                 *entry = 'D';
                  procdir (name_buffer, &stat_data, device,
                           directory->children,
                  procdir (name_buffer, &stat_data, device,
                           directory->children,
-                          verbose_option);
-                 *entry = 'D';
+                          verbose_option, entry);
                }
                }
-             
+
              else if (one_file_system_option && device != stat_data.st_dev)
                *entry = 'N';
 
              else if (*entry == 'Y')
                /* New entry, skip further checks */;
              else if (one_file_system_option && device != stat_data.st_dev)
                *entry = 'N';
 
              else if (*entry == 'Y')
                /* New entry, skip further checks */;
-             
+
              /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
              /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
-             
+
              else if (OLDER_STAT_TIME (stat_data, m)
                       && (!after_date_option
                           || OLDER_STAT_TIME (stat_data, c)))
              else if (OLDER_STAT_TIME (stat_data, m)
                       && (!after_date_option
                           || OLDER_STAT_TIME (stat_data, c)))
@@ -542,7 +591,7 @@ scan_directory (char *dir_name, dev_t device)
            }
        }
     }
            }
        }
     }
-  
+
   free (name_buffer);
   if (dirp)
     free (dirp);
   free (name_buffer);
   if (dirp)
     free (dirp);
@@ -551,19 +600,26 @@ scan_directory (char *dir_name, dev_t device)
 }
 
 char *
 }
 
 char *
-get_directory_contents (char *dir_name, dev_t device)
+get_directory_contents (char *dir, dev_t device)
 {
 {
-  return scan_directory (dir_name, device);
+  return scan_directory (dir, device);
 }
 
 \f
 static void
 obstack_code_rename (struct obstack *stk, char *from, char *to)
 {
 }
 
 \f
 static void
 obstack_code_rename (struct obstack *stk, char *from, char *to)
 {
+  char *s;
+
+  s = from[0] == 0 ? from :
+                     safer_name_suffix (from, false, absolute_names_option);
   obstack_1grow (stk, 'R');
   obstack_1grow (stk, 'R');
-  obstack_grow (stk, from, strlen (from) + 1);
+  obstack_grow (stk, s, strlen (s) + 1);
+
+  s = to[0] == 0 ? to:
+                   safer_name_suffix (to, false, absolute_names_option);
   obstack_1grow (stk, 'T');
   obstack_1grow (stk, 'T');
-  obstack_grow (stk, to, strlen (to) + 1);
+  obstack_grow (stk, s, strlen (s) + 1);
 }
 
 static bool
 }
 
 static bool
@@ -571,12 +627,12 @@ rename_handler (void *data, void *proc_data)
 {
   struct directory *dir = data;
   struct obstack *stk = proc_data;
 {
   struct directory *dir = data;
   struct obstack *stk = proc_data;
-  
+
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
 
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
 
-      /* Detect eventual cycles and clear DIRF_RENAMED flag, so this entries
+      /* Detect eventual cycles and clear DIRF_RENAMED flag, so these entries
         are ignored when hit by this function next time.
         If the chain forms a cycle, prev points to the entry DIR is renamed
         from. In this case it still retains DIRF_RENAMED flag, which will be
         are ignored when hit by this function next time.
         If the chain forms a cycle, prev points to the entry DIR is renamed
         from. In this case it still retains DIRF_RENAMED flag, which will be
@@ -621,7 +677,7 @@ append_incremental_renames (const char *dump)
 
   if (directory_table == NULL)
     return dump;
 
   if (directory_table == NULL)
     return dump;
-  
+
   obstack_init (&stk);
   if (dump)
     {
   obstack_init (&stk);
   if (dump)
     {
@@ -630,7 +686,7 @@ append_incremental_renames (const char *dump)
     }
   else
     size = 0;
     }
   else
     size = 0;
-  
+
   hash_do_for_each (directory_table, rename_handler, &stk);
   if (obstack_object_size (&stk) != size)
     {
   hash_do_for_each (directory_table, rename_handler, &stk);
   if (obstack_object_size (&stk) != size)
     {
@@ -662,7 +718,8 @@ read_incr_db_01 (int version, const char *initbuf)
 {
   int n;
   uintmax_t u;
 {
   int n;
   uintmax_t u;
-  time_t t = u;
+  time_t sec;
+  long int nsec;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
@@ -683,41 +740,49 @@ read_incr_db_01 (int version, const char *initbuf)
       buf = strdup (initbuf);
       bufsize = strlen (buf) + 1;
     }
       buf = strdup (initbuf);
       bufsize = strlen (buf) + 1;
     }
-  
-  t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-  if (buf == ebuf || (u == 0 && errno == EINVAL))
-    ERROR ((0, 0, "%s:%ld: %s",
+
+  sec = TYPE_MINIMUM (time_t);
+  nsec = -1;
+  errno = 0;
+  u = strtoumax (buf, &ebuf, 10);
+  if (!errno && TYPE_MAXIMUM (time_t) < u)
+    errno = ERANGE;
+  if (errno || buf == ebuf)
+    ERROR ((0, errno, "%s:%ld: %s",
            quotearg_colon (listed_incremental_option),
            lineno,
            _("Invalid time stamp")));
            quotearg_colon (listed_incremental_option),
            lineno,
            _("Invalid time stamp")));
-  else if (t != u)
-    ERROR ((0, 0, "%s:%ld: %s",
-           quotearg_colon (listed_incremental_option),
-           lineno,
-           _("Time stamp out of range")));
-  else if (version == 1)
-    {
-      newer_mtime_option.tv_sec = t;
-
-      t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-      if (buf == ebuf || (u == 0 && errno == EINVAL))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option),
-               lineno,
-               _("Invalid time stamp")));
-      else if (t != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option),
-               lineno,
-               _("Time stamp out of range")));
-      newer_mtime_option.tv_nsec = t;
-    }
   else
     {
   else
     {
-      /* pre-1 incremental format does not contain nanoseconds */
-      newer_mtime_option.tv_sec = t;
-      newer_mtime_option.tv_nsec = 0;
+      sec = u;
+
+      if (version == 1 && *ebuf)
+       {
+         char const *buf_ns = ebuf + 1;
+         errno = 0;
+         u = strtoumax (buf_ns, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || buf_ns == ebuf)
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option),
+                     lineno,
+                     _("Invalid time stamp")));
+             sec = TYPE_MINIMUM (time_t);
+           }
+         else
+           nsec = u;
+       }
+      else
+       {
+         /* pre-1 incremental format does not contain nanoseconds */
+         nsec = 0;
+       }
     }
     }
+  newer_mtime_option.tv_sec = sec;
+  newer_mtime_option.tv_nsec = nsec;
+
 
   while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
     {
 
   while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
     {
@@ -726,65 +791,79 @@ read_incr_db_01 (int version, const char *initbuf)
       bool nfs = buf[0] == '+';
       char *strp = buf + nfs;
       struct timespec mtime;
       bool nfs = buf[0] == '+';
       char *strp = buf + nfs;
       struct timespec mtime;
-      
+
       lineno++;
       lineno++;
-      
+
       if (buf[n - 1] == '\n')
        buf[n - 1] = '\0';
       if (buf[n - 1] == '\n')
        buf[n - 1] = '\0';
-      
+
       if (version == 1)
        {
          errno = 0;
       if (version == 1)
        {
          errno = 0;
-         mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Invalid modification time (seconds)")));
-         else if (mtime.tv_sec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (seconds) out of range")));
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && TYPE_MAXIMUM (time_t) < u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option), lineno,
+                     _("Invalid modification time (seconds)")));
+             sec = (time_t) -1;
+           }
+         else
+           sec = u;
          strp = ebuf;
          strp = ebuf;
-         
+
          errno = 0;
          errno = 0;
-         mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Invalid modification time (nanoseconds)")));
-         else if (mtime.tv_nsec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (nanoseconds) out of range")));
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option), lineno,
+                     _("Invalid modification time (nanoseconds)")));
+             nsec = -1;
+           }
+         else
+           nsec = u;
+         mtime.tv_sec = sec;
+         mtime.tv_nsec = nsec;
          strp = ebuf;
        }
       else
        memset (&mtime, 0, sizeof mtime);
          strp = ebuf;
        }
       else
        memset (&mtime, 0, sizeof mtime);
-      
+
       errno = 0;
       errno = 0;
-      dev = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Invalid device number")));
-      else if (dev != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Device number out of range")));
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (dev_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
+                 quotearg_colon (listed_incremental_option), lineno,
+                 _("Invalid device number")));
+         dev = (dev_t) -1;
+       }
+      else
+       dev = u;
       strp = ebuf;
 
       errno = 0;
       strp = ebuf;
 
       errno = 0;
-      ino = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Invalid inode number")));
-      else if (ino != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Inode number out of range")));
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (ino_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
+                 quotearg_colon (listed_incremental_option), lineno,
+                 _("Invalid inode number")));
+         ino = (ino_t) -1;
+       }
+      else
+       ino = u;
       strp = ebuf;
       strp = ebuf;
-      
+
       strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
       strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
@@ -811,31 +890,128 @@ read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
 }
 
 /* Read from file FP a nul-terminated string and convert it to
 }
 
 /* Read from file FP a nul-terminated string and convert it to
-   uintmax_t. Return the resulting value in PVAL.
+   intmax_t.  Return the resulting value in PVAL.  Assume '-' has
+   already been read.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value is less than MIN_VAL.  */
+
+static void
+read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
+{
+  int c;
+  size_t i;
+  char buf[INT_BUFSIZE_BOUND (intmax_t)];
+  char *ep;
+  buf[0] = '-';
+
+  for (i = 1; ISDIGIT (c = getc (fp)); i++)
+    {
+      if (i == sizeof buf - 1)
+       FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
+      buf[i] = c;
+    }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+    }
+
+  buf[i] = 0;
+  errno = 0;
+  *pval = strtoimax (buf, &ep, 10);
+  if (c || errno || *pval < min_val)
+    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting value in PVAL.  Assume C has
+   already been read.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value exceeds MAX_VAL.
 
 
-   Throw fatal error if the string cannot be converted.
-   
    Return the last character read or EOF on end of file. */
 
 static int
    Return the last character read or EOF on end of file. */
 
 static int
-read_num (FILE *fp, uintmax_t *pval)
+read_unsigned_num (int c, FILE *fp, uintmax_t max_val, uintmax_t *pval)
 {
 {
-  int c;
   size_t i;
   char buf[UINTMAX_STRSIZE_BOUND], *ep;
 
   size_t i;
   char buf[UINTMAX_STRSIZE_BOUND], *ep;
 
-  for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
+  for (i = 0; ISDIGIT (c); i++)
     {
       if (i == sizeof buf - 1)
        FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
       buf[i] = c;
     {
       if (i == sizeof buf - 1)
        FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
       buf[i] = c;
+      c = getc (fp);
     }
     }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else if (i == 0)
+       return c;
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+    }
+
   buf[i] = 0;
   buf[i] = 0;
+  errno = 0;
   *pval = strtoumax (buf, &ep, 10);
   *pval = strtoumax (buf, &ep, 10);
-  if (*ep)
-    FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
+  if (c || errno || max_val < *pval)
+    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
   return c;
   return c;
-}  
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value exceeds MAX_VAL.
+
+   Return the last character read or EOF on end of file. */
+
+static int
+read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
+{
+  return read_unsigned_num (getc (fp), fp, max_val, pval);
+}
+
+/* Read from FP two NUL-terminated strings representing a struct
+   timespec.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted.  */
+
+static void
+read_timespec (FILE *fp, struct timespec *pval)
+{
+  int c = getc (fp);
+  intmax_t i;
+  uintmax_t u;
+
+  if (c == '-')
+    {
+      read_negative_num (fp, TYPE_MINIMUM (time_t), &i);
+      c = 0;
+      pval->tv_sec = i;
+    }
+  else
+    {
+      c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u);
+      pval->tv_sec = u;
+    }
+
+  if (c || read_num (fp, BILLION - 1, &u))
+    FATAL_ERROR ((0, 0, "%s: %s",
+                 quotearg_colon (listed_incremental_option),
+                 _("Unexpected EOF in snapshot file")));
+  pval->tv_nsec = u;
+}
 
 /* Read incremental snapshot format 2 */
 static void
 
 /* Read incremental snapshot format 2 */
 static void
@@ -843,28 +1019,10 @@ read_incr_db_2 ()
 {
   uintmax_t u;
   struct obstack stk;
 {
   uintmax_t u;
   struct obstack stk;
-  
+
   obstack_init (&stk);
 
   obstack_init (&stk);
 
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_sec = u;
-  if (newer_mtime_option.tv_sec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
-
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_nsec = u;
-  if (newer_mtime_option.tv_nsec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
+  read_timespec (listed_incremental_stream, &newer_mtime_option);
 
   for (;;)
     {
 
   for (;;)
     {
@@ -875,43 +1033,21 @@ read_incr_db_2 ()
       char *name;
       char *content;
       size_t s;
       char *name;
       char *content;
       size_t s;
-      
-      if (read_num (listed_incremental_stream, &u))
+
+      if (read_num (listed_incremental_stream, 1, &u))
        return; /* Normal return */
 
       nfs = u;
        return; /* Normal return */
 
       nfs = u;
-      
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_sec = u;
-      if (mtime.tv_sec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (seconds) out of range")));
-      
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_nsec = u;
-      if (mtime.tv_nsec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (nanoseconds) out of range")));
 
 
-      if (read_num (listed_incremental_stream, &u))
+      read_timespec (listed_incremental_stream, &mtime);
+
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u))
        break;
       dev = u;
        break;
       dev = u;
-      if (dev != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Device number out of range")));
 
 
-      if (read_num (listed_incremental_stream, &u))
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u))
        break;
       ino = u;
        break;
       ino = u;
-      if (ino != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Inode number out of range")));
 
       if (read_obstack (listed_incremental_stream, &stk, &s))
        break;
 
       if (read_obstack (listed_incremental_stream, &stk, &s))
        break;
@@ -922,16 +1058,16 @@ read_incr_db_2 ()
        ;
       if (getc (listed_incremental_stream) != 0)
        FATAL_ERROR ((0, 0, "%s: %s",
        ;
       if (getc (listed_incremental_stream) != 0)
        FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
+                     quotearg_colon (listed_incremental_option),
                      _("Missing record terminator")));
                      _("Missing record terminator")));
-      
+
       content = obstack_finish (&stk);
       note_directory (name, mtime, dev, ino, nfs, false, content);
       obstack_free (&stk, content);
     }
   FATAL_ERROR ((0, 0, "%s: %s",
       content = obstack_finish (&stk);
       note_directory (name, mtime, dev, ino, nfs, false, content);
       obstack_free (&stk, content);
     }
   FATAL_ERROR ((0, 0, "%s: %s",
-               quotearg_colon (listed_incremental_option), 
-               _("Unexpected EOF")));
+               quotearg_colon (listed_incremental_option),
+               _("Unexpected EOF in snapshot file")));
 }
 
 /* Read incremental snapshot file (directory file).
 }
 
 /* Read incremental snapshot file (directory file).
@@ -946,7 +1082,6 @@ read_directory_file (void)
   int fd;
   char *buf = 0;
   size_t bufsize;
   int fd;
   char *buf = 0;
   size_t bufsize;
-  long lineno = 1;
 
   /* Open the file for both read and write.  That way, we can write
      it later without having to reopen it, and don't have to worry if
 
   /* Open the file for both read and write.  That way, we can write
      it later without having to reopen it, and don't have to worry if
@@ -969,7 +1104,7 @@ read_directory_file (void)
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
-      int incremental_version;
+      uintmax_t incremental_version;
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
@@ -980,7 +1115,7 @@ read_directory_file (void)
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
-         incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
+         incremental_version = strtoumax (ebuf + 1, NULL, 10);
        }
       else
        incremental_version = 0;
        }
       else
        incremental_version = 0;
@@ -995,12 +1130,12 @@ read_directory_file (void)
        case TAR_INCREMENTAL_VERSION:
          read_incr_db_2 ();
          break;
        case TAR_INCREMENTAL_VERSION:
          read_incr_db_2 ();
          break;
-         
+
        default:
        default:
-         ERROR ((1, 0, _("Unsupported incremental format version: %d"),
+         ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
                  incremental_version));
        }
                  incremental_version));
        }
-      
+
     }
 
   if (ferror (listed_incremental_stream))
     }
 
   if (ferror (listed_incremental_stream))
@@ -1024,7 +1159,9 @@ write_directory_file_entry (void *entry, void *data)
 
       s = DIR_IS_NFS (directory) ? "1" : "0";
       fwrite (s, 2, 1, fp);
 
       s = DIR_IS_NFS (directory) ? "1" : "0";
       fwrite (s, 2, 1, fp);
-      s = umaxtostr (directory->mtime.tv_sec, buf);
+      s = (TYPE_SIGNED (time_t)
+          ? imaxtostr (directory->mtime.tv_sec, buf)
+          : umaxtostr (directory->mtime.tv_sec, buf));
       fwrite (s, strlen (s) + 1, 1, fp);
       s = umaxtostr (directory->mtime.tv_nsec, buf);
       fwrite (s, strlen (s) + 1, 1, fp);
       fwrite (s, strlen (s) + 1, 1, fp);
       s = umaxtostr (directory->mtime.tv_nsec, buf);
       fwrite (s, strlen (s) + 1, 1, fp);
@@ -1067,7 +1204,9 @@ write_directory_file (void)
   fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
           TAR_INCREMENTAL_VERSION);
 
   fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
           TAR_INCREMENTAL_VERSION);
 
-  s = umaxtostr (start_time.tv_sec, buf);
+  s = (TYPE_SIGNED (time_t)
+       ? imaxtostr (start_time.tv_sec, buf)
+       : umaxtostr (start_time.tv_sec, buf));
   fwrite (s, strlen (s) + 1, 1, fp);
   s = umaxtostr (start_time.tv_nsec, buf);
   fwrite (s, strlen (s) + 1, 1, fp);
   fwrite (s, strlen (s) + 1, 1, fp);
   s = umaxtostr (start_time.tv_nsec, buf);
   fwrite (s, strlen (s) + 1, 1, fp);
@@ -1140,7 +1279,7 @@ dumpdir_ok (char *dumpdir)
   char *p;
   int has_tempdir = 0;
   int expect = 0;
   char *p;
   int has_tempdir = 0;
   int expect = 0;
-  
+
   for (p = dumpdir; *p; p += strlen (p) + 1)
     {
       if (expect && *p != expect)
   for (p = dumpdir; *p; p += strlen (p) + 1)
     {
       if (expect && *p != expect)
@@ -1162,7 +1301,7 @@ dumpdir_ok (char *dumpdir)
          else
            has_tempdir = 1;
          break;
          else
            has_tempdir = 1;
          break;
-         
+
        case 'R':
          if (p[1] == 0)
            {
        case 'R':
          if (p[1] == 0)
            {
@@ -1177,14 +1316,14 @@ dumpdir_ok (char *dumpdir)
            }
          expect = 'T';
          break;
            }
          expect = 'T';
          break;
-           
+
        case 'T':
          if (expect != 'T')
            {
              ERROR ((0, 0,
                      _("Malformed dumpdir: 'T' not preceeded by 'R'")));
              return false;
        case 'T':
          if (expect != 'T')
            {
              ERROR ((0, 0,
                      _("Malformed dumpdir: 'T' not preceeded by 'R'")));
              return false;
-           }  
+           }
          if (p[1] == 0 && !has_tempdir)
            {
              ERROR ((0, 0,
          if (p[1] == 0 && !has_tempdir)
            {
              ERROR ((0, 0,
@@ -1193,7 +1332,7 @@ dumpdir_ok (char *dumpdir)
            }
          expect = 0;
          break;
            }
          expect = 0;
          break;
-         
+
        case 'N':
        case 'Y':
        case 'D':
        case 'N':
        case 'Y':
        case 'D':
@@ -1218,7 +1357,7 @@ dumpdir_ok (char *dumpdir)
 
   return true;
 }
 
   return true;
 }
-      
+
 /* Examine the directories under directory_name and delete any
    files that were not there at the time of the back-up. */
 static bool
 /* Examine the directories under directory_name and delete any
    files that were not there at the time of the back-up. */
 static bool
@@ -1227,7 +1366,7 @@ try_purge_directory (char const *directory_name)
   char *current_dir;
   char *cur, *arc, *p;
   char *temp_stub = NULL;
   char *current_dir;
   char *cur, *arc, *p;
   char *temp_stub = NULL;
-  
+
   if (!is_dumpdir (&current_stat_info))
     return false;
 
   if (!is_dumpdir (&current_stat_info))
     return false;
 
@@ -1241,13 +1380,13 @@ try_purge_directory (char const *directory_name)
   /* Verify if dump directory is sane */
   if (!dumpdir_ok (current_stat_info.dumpdir))
     return false;
   /* Verify if dump directory is sane */
   if (!dumpdir_ok (current_stat_info.dumpdir))
     return false;
-       
+
   /* Process renames */
   for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
     {
       if (*arc == 'X')
        {
   /* Process renames */
   for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
     {
       if (*arc == 'X')
        {
-#define TEMP_DIR_TEMPLATE "tar.XXXXXX"   
+#define TEMP_DIR_TEMPLATE "tar.XXXXXX"
          size_t len = strlen (arc + 1);
          temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
          memcpy (temp_stub, arc + 1, len);
          size_t len = strlen (arc + 1);
          temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
          memcpy (temp_stub, arc + 1, len);
@@ -1271,11 +1410,24 @@ try_purge_directory (char const *directory_name)
          arc += strlen (arc) + 1;
          dst = arc + 1;
 
          arc += strlen (arc) + 1;
          dst = arc + 1;
 
+         /* Ensure that neither source nor destination are absolute file
+            names (unless permitted by -P option), and that they do not
+            contain dubious parts (e.g. ../).
+
+            This is an extra safety precaution. Besides, it might be
+            necessary to extract from archives created with tar versions
+            prior to 1.19. */
+         
+         if (*src)
+           src = safer_name_suffix (src, false, absolute_names_option);
+         if (*dst)
+           dst = safer_name_suffix (dst, false, absolute_names_option);
+         
          if (*src == 0)
            src = temp_stub;
          else if (*dst == 0)
            dst = temp_stub;
          if (*src == 0)
            src = temp_stub;
          else if (*dst == 0)
            dst = temp_stub;
-           
+
          if (!rename_directory (src, dst))
            {
              free (temp_stub);
          if (!rename_directory (src, dst))
            {
              free (temp_stub);
@@ -1288,7 +1440,7 @@ try_purge_directory (char const *directory_name)
     }
 
   free (temp_stub);
     }
 
   free (temp_stub);
-  
+
   /* Process deletes */
   p = NULL;
   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
   /* Process deletes */
   p = NULL;
   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
@@ -1299,22 +1451,23 @@ try_purge_directory (char const *directory_name)
        free (p);
       p = new_name (directory_name, cur);
 
        free (p);
       p = new_name (directory_name, cur);
 
-      if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
-         || (*entry == 'D' && S_ISDIR (st.st_mode))
-         || (*entry == 'Y' && !S_ISDIR (st.st_mode)))
+      if (deref_stat (false, p, &st))
        {
        {
-         if (deref_stat (false, p, &st))
+         if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
+                                 dirs and check it here? */
            {
            {
-             if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
-                                     dirs and check it here? */
-               {
-                 stat_diag (p);
-                 WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
-                        quotearg_colon (p)));
-               }
-             continue;
+             stat_diag (p);
+             WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
+                    quotearg_colon (p)));
            }
            }
-         else if (one_file_system_option && st.st_dev != root_device)
+         continue;
+       }
+
+      if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
+         || (*entry == 'D' && !S_ISDIR (st.st_mode))
+         || (*entry == 'Y' && S_ISDIR (st.st_mode)))
+       {
+         if (one_file_system_option && st.st_dev != root_device)
            {
              WARN ((0, 0,
                     _("%s: directory is on a different device: not purging"),
            {
              WARN ((0, 0,
                     _("%s: directory is on a different device: not purging"),
@@ -1336,7 +1489,7 @@ try_purge_directory (char const *directory_name)
        }
     }
   free (p);
        }
     }
   free (p);
-  
+
   free (current_dir);
   return true;
 }
   free (current_dir);
   return true;
 }
@@ -1347,10 +1500,11 @@ purge_directory (char const *directory_name)
   if (!try_purge_directory (directory_name))
     skip_member ();
 }
   if (!try_purge_directory (directory_name))
     skip_member ();
 }
-     
+
 void
 list_dumpdir (char *buffer, size_t size)
 {
 void
 list_dumpdir (char *buffer, size_t size)
 {
+  int state = 0;
   while (size)
     {
       switch (*buffer)
   while (size)
     {
       switch (*buffer)
@@ -1361,7 +1515,12 @@ list_dumpdir (char *buffer, size_t size)
        case 'R':
        case 'T':
        case 'X':
        case 'R':
        case 'T':
        case 'X':
-         fprintf (stdlis, "%c ", *buffer);
+         fprintf (stdlis, "%c", *buffer);
+         if (state == 0)
+           {
+             fprintf (stdlis, " ");
+             state = 1;
+           }
          buffer++;
          size--;
          break;
          buffer++;
          size--;
          break;
@@ -1370,6 +1529,7 @@ list_dumpdir (char *buffer, size_t size)
          fputc ('\n', stdlis);
          buffer++;
          size--;
          fputc ('\n', stdlis);
          buffer++;
          size--;
+         state = 0;
          break;
 
        default:
          break;
 
        default:
This page took 0.053076 seconds and 4 git commands to generate.