getgroups: fix logic error
[gnulib.git] / lib / utimecmp.c
index 1ff61f3..9dcb652 100644 (file)
@@ -1,11 +1,12 @@
 /* utimecmp.c -- compare file time stamps
 
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2009 Free Software
+   Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify
+   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 version.
+   the Free Software Foundation; either version 3 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
    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.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* Written by Paul Eggert.  */
 
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
 
 #include "utimecmp.h"
 
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-
 #include <limits.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
 #include "hash.h"
-#include "timespec.h"
+#include "intprops.h"
+#include "stat-time.h"
 #include "utimens.h"
+#include "verify.h"
 #include "xalloc.h"
 
-/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
-#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
-
 #ifndef MAX
 # define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
-#ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
-#endif
-
-/* The extra casts work around common compiler bugs.  */
-#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
-   It is necessary at least when t == time_t.  */
-#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
-                             ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
-#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
-
 enum { BILLION = 1000 * 1000 * 1000 };
 
 /* Best possible resolution that utimens can set and stat can return,
    due to system-call limitations.  It must be a power of 10 that is
    no greater than 1 billion.  */
-#if HAVE_WORKING_UTIMES && defined ST_MTIM_NSEC
+#if HAVE_UTIMENSAT
+enum { SYSCALL_RESOLUTION = 1 };
+#elif ((HAVE_FUTIMESAT || HAVE_WORKING_UTIMES)                  \
+       && (defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC            \
+          || defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC     \
+          || defined HAVE_STRUCT_STAT_ST_ATIMENSEC             \
+          || defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC  \
+          || defined HAVE_STRUCT_STAT_ST_SPARE1))
 enum { SYSCALL_RESOLUTION = 1000 };
 #else
 enum { SYSCALL_RESOLUTION = BILLION };
@@ -116,6 +105,8 @@ dev_info_compare (void const *x, void const *y)
    DST_NAME and status DST_STAT) is older than SRC_STAT, the same age
    as SRC_STAT, or newer than SRC_STAT, respectively.
 
+   DST_NAME may be NULL if OPTIONS is 0.
+
    If OPTIONS & UTIMECMP_TRUNCATE_SOURCE, do the comparison after SRC is
    converted to the destination's timestamp resolution as filtered through
    utimens.  In this case, return -2 if the exact answer cannot be
@@ -140,14 +131,14 @@ utimecmp (char const *dst_name,
 
      time_t might be unsigned.  */
 
-  verify (time_t_is_integer, (time_t) 0.5 == 0);
-  verify (twos_complement_arithmetic, -1 == ~1 + 1);
+  verify (TYPE_IS_INTEGER (time_t));
+  verify (TYPE_TWOS_COMPLEMENT (int));
 
   /* Destination and source time stamps.  */
   time_t dst_s = dst_stat->st_mtime;
   time_t src_s = src_stat->st_mtime;
-  int dst_ns = TIMESPEC_NS (dst_stat->st_mtim);
-  int src_ns = TIMESPEC_NS (src_stat->st_mtim);
+  int dst_ns = get_stat_mtime_ns (dst_stat);
+  int src_ns = get_stat_mtime_ns (src_stat);
 
   if (options & UTIMECMP_TRUNCATE_SOURCE)
     {
@@ -163,6 +154,16 @@ utimecmp (char const *dst_name,
       /* Time stamp resolution in nanoseconds.  */
       int res;
 
+      /* Quick exit, if possible.  Since the worst resolution is 2
+        seconds, anything that differs by more than that does not
+        needs source truncation.  */
+      if (dst_s == src_s && dst_ns == src_ns)
+       return 0;
+      if (dst_s <= src_s - 2)
+       return -1;
+      if (src_s <= dst_s - 2)
+       return 1;
+
       if (! ht)
        ht = hash_initialize (16, NULL, dev_info_hash, dev_info_compare, free);
       if (! new_dst_res)
@@ -185,6 +186,19 @@ utimecmp (char const *dst_name,
 
       res = dst_res->resolution;
 
+#ifdef _PC_TIMESTAMP_RESOLUTION
+      /* If the system will tell us the resolution, we're set! */
+      if (! dst_res->exact)
+       {
+         res = pathconf (dst_name, _PC_TIMESTAMP_RESOLUTION);
+         if (0 < res)
+           {
+             dst_res->resolution = res;
+             dst_res->exact = true;
+           }
+       }
+#endif
+
       if (! dst_res->exact)
        {
          /* This file system's resolution is not known exactly.
@@ -193,8 +207,8 @@ utimecmp (char const *dst_name,
          time_t dst_a_s = dst_stat->st_atime;
          time_t dst_c_s = dst_stat->st_ctime;
          time_t dst_m_s = dst_s;
-         int dst_a_ns = TIMESPEC_NS (dst_stat->st_atim);
-         int dst_c_ns = TIMESPEC_NS (dst_stat->st_ctim);
+         int dst_a_ns = get_stat_atime_ns (dst_stat);
+         int dst_c_ns = get_stat_ctime_ns (dst_stat);
          int dst_m_ns = dst_ns;
 
          /* Set RES to an upper bound on the file system resolution
@@ -274,26 +288,29 @@ utimecmp (char const *dst_name,
              /* Set the modification time.  But don't try to set the
                 modification time of symbolic links; on many hosts this sets
                 the time of the pointed-to file.  */
-             if (S_ISLNK (dst_stat->st_mode)
-                 || utimens (dst_name, timespec) != 0)
+             if ((S_ISLNK (dst_stat->st_mode)
+                  ? lutimens (dst_name, timespec)
+                  : utimens (dst_name, timespec)) != 0)
                return -2;
 
-             /* Read the modification time that was set.  It's safe to call
-                'stat' here instead of worrying about 'lstat'; either the
-                caller used 'stat', or the caller used 'lstat' and found
-                something other than a symbolic link.  */
+             /* Read the modification time that was set.  */
              {
-               int stat_result = stat (dst_name, &dst_status);
+               int stat_result = (S_ISLNK (dst_stat->st_mode)
+                                  ? lstat (dst_name, &dst_status)
+                                  : stat (dst_name, &dst_status));
 
                if (stat_result
                    | (dst_status.st_mtime ^ dst_m_s)
-                   | (TIMESPEC_NS (dst_status.st_mtim) ^ dst_m_ns))
+                   | (get_stat_mtime_ns (&dst_status) ^ dst_m_ns))
                  {
                    /* The modification time changed, or we can't tell whether
                       it changed.  Change it back as best we can.  */
                    timespec[1].tv_sec = dst_m_s;
                    timespec[1].tv_nsec = dst_m_ns;
-                   utimens (dst_name, timespec);
+                   if (S_ISLNK (dst_stat->st_mode))
+                     lutimens (dst_name, timespec);
+                   else
+                     utimens (dst_name, timespec);
                  }
 
                if (stat_result != 0)
@@ -305,7 +322,7 @@ utimecmp (char const *dst_name,
              {
                int old_res = res;
                int a = (BILLION * (dst_status.st_mtime & 1)
-                        + TIMESPEC_NS (dst_status.st_mtim));
+                        + get_stat_mtime_ns (&dst_status));
 
                res = SYSCALL_RESOLUTION;