void mutt_buffy_cleanup (const char *buf, struct stat *st)
{
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#else
struct utimbuf ut;
+#endif
BUFFY *tmp;
if (option(OPTCHECKMBOXSIZE))
/* fix up the times so buffy won't get confused */
if (st->st_mtime > st->st_atime)
{
+#ifdef HAVE_UTIMENSAT
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_OMIT;
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_NOW;
+ utimensat (0, buf, ts, 0);
+#else
ut.actime = st->st_atime;
ut.modtime = time (NULL);
utime (buf, &ut);
+#endif
}
else
+ {
+#ifdef HAVE_UTIMENSAT
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_NOW;
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_NOW;
+ utimensat (0, buf, ts, 0);
+#else
utime (buf, NULL);
+#endif
+ }
}
}
*/
if (check_new && option(OPTMAILCHECKRECENT))
{
- if (stat(path, &sb) == 0 && sb.st_mtime < mailbox->last_visited)
+ if (stat(path, &sb) == 0 &&
+ mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &mailbox->last_visited) < 0)
{
rc = 0;
check_new = 0;
{
snprintf(msgpath, sizeof(msgpath), "%s/%s", path, de->d_name);
/* ensure this message was received since leaving this mailbox */
- if (stat(msgpath, &sb) == 0 && (sb.st_ctime <= mailbox->last_visited))
+ if (stat(msgpath, &sb) == 0 &&
+ (mutt_stat_timespec_compare (&sb, MUTT_STAT_CTIME, &mailbox->last_visited) <= 0))
continue;
}
mailbox->new = 1;
if (option (OPTCHECKMBOXSIZE))
new_or_changed = sb->st_size > mailbox->size;
else
- new_or_changed = sb->st_mtime > sb->st_atime
- || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime);
+ new_or_changed = (mutt_stat_compare (sb, MUTT_STAT_MTIME, sb, MUTT_STAT_ATIME) > 0)
+ || (mailbox->newly_created &&
+ (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_MTIME) == 0) &&
+ (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_ATIME) == 0));
if (new_or_changed)
{
- if (!option(OPTMAILCHECKRECENT) || sb->st_mtime > mailbox->last_visited)
+ if (!option(OPTMAILCHECKRECENT) ||
+ (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->last_visited) > 0))
{
rc = 1;
mailbox->new = 1;
mailbox->newly_created = 0;
if (check_stats &&
- (mailbox->stats_last_checked < sb->st_mtime))
+ (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->stats_last_checked) > 0))
{
if (mx_open_mailbox (mailbox->path,
MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK,
return;
buffy->notified = 1;
- time(&buffy->last_visited);
+#if HAVE_CLOCK_GETTIME
+ clock_gettime (CLOCK_REALTIME, &buffy->last_visited);
+#else
+ buffy->last_visited.tv_nsec = 0;
+ time(&buffy->last_visited.tv_sec);
+#endif
}
int mutt_buffy_notify (void)
short notified; /* user has been notified */
short magic; /* mailbox type */
short newly_created; /* mbox or mmdf just popped into existence */
- time_t last_visited; /* time of last exit from this mailbox */
- time_t stats_last_checked; /* mtime of mailbox the last time stats where checked. */
+ struct timespec last_visited; /* time of last exit from this mailbox */
+ struct timespec stats_last_checked; /* mtime of mailbox the last time stats where checked. */
}
BUFFY;
dnl Set the atime of files
AC_CHECK_FUNCS(futimens)
+dnl Check for struct timespec
+AC_CHECK_TYPES([struct timespec],,,[[#include <time.h>]])
+
+dnl Check for stat nanosecond resolutions
+AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec,
+ struct stat.st_mtim.tv_nsec,
+ struct stat.st_ctim.tv_nsec],,,[[#include <sys/stat.h>]])
+
+dnl Check for utimesnsat
+AC_CHECK_FUNCS(utimensat)
+
+dnl Check for clock_gettime
+AC_CHECK_FUNCS(clock_gettime)
+
dnl AIX may not have fchdir()
AC_CHECK_FUNCS(fchdir, , [mutt_cv_fchdir=no])
HEADER *hdr;
struct stat sb;
#ifdef NFS_ATTRIBUTE_HACK
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#endif /* HAVE_UTIMENSAT */
struct utimbuf newtime;
#endif
progress_t progress;
mutt_perror (ctx->path);
return (-1);
}
- ctx->atime = sb.st_atime;
- ctx->mtime = sb.st_mtime;
+ mutt_get_stat_timespec (&ctx->atime, &sb, MUTT_STAT_ATIME);
+ mutt_get_stat_timespec (&ctx->mtime, &sb, MUTT_STAT_MTIME);
ctx->size = sb.st_size;
#ifdef NFS_ATTRIBUTE_HACK
if (sb.st_mtime > sb.st_atime)
{
- newtime.modtime = sb.st_mtime;
+#ifdef HAVE_UTIMENSAT
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_NOW;
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
+ utimensat (0, ctx->path, ts, 0);
+#else
newtime.actime = time (NULL);
+ newtime.modtime = sb.st_mtime;
utime (ctx->path, &newtime);
+#endif /* HAVE_UTIMENSAT */
}
#endif
int count = 0, lines = 0;
LOFF_T loc;
#ifdef NFS_ATTRIBUTE_HACK
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#endif /* HAVE_UTIMENSAT */
struct utimbuf newtime;
#endif
progress_t progress;
}
ctx->size = sb.st_size;
- ctx->mtime = sb.st_mtime;
- ctx->atime = sb.st_atime;
+ mutt_get_stat_timespec (&ctx->mtime, &sb, MUTT_STAT_MTIME);
+ mutt_get_stat_timespec (&ctx->atime, &sb, MUTT_STAT_ATIME);
#ifdef NFS_ATTRIBUTE_HACK
if (sb.st_mtime > sb.st_atime)
{
- newtime.modtime = sb.st_mtime;
+#ifdef HAVE_UTIMENSAT
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_NOW;
+ ts[1].tv_sec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
+ utimensat (0, ctx->path, ts, 0);
+#else
newtime.actime = time (NULL);
+ newtime.modtime = sb.st_mtime;
utime (ctx->path, &newtime);
+#endif /* HAVE_UTIMENSAT */
}
#endif
if (stat (ctx->path, &st) == 0)
{
- if (st.st_mtime == ctx->mtime && st.st_size == ctx->size)
+ if ((mutt_stat_timespec_compare (&st, MUTT_STAT_MTIME, &ctx->mtime) == 0) &&
+ st.st_size == ctx->size)
return (0);
if (st.st_size == ctx->size)
{
/* the file was touched, but it is still the same length, so just exit */
- ctx->mtime = st.st_mtime;
+ mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
return (0);
}
struct mh_data
{
- time_t mtime_cur;
+ struct timespec mtime_cur;
mode_t mh_umask;
};
if ((snprintf(path, sizeof(path), "%s/.mh_sequences", b->path) < sizeof(path)) &&
(stat(path, &sb) == 0))
- return (sb.st_mtime > b->last_visited);
+ return (mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &b->last_visited) > 0);
return -1;
}
if ((snprintf(path, sizeof(path), "%s/%d", b->path, msgno) < sizeof(path)) &&
(stat(path, &sb) == 0))
- return (sb.st_mtime <= b->last_visited);
+ return (mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &b->last_visited) <= 0);
return -1;
}
{
snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "cur");
if (stat (buf, &st) == 0)
- data->mtime_cur = st.st_mtime;
+ mutt_get_stat_timespec (&data->mtime_cur, &st, MUTT_STAT_MTIME);
snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "new");
}
else
{
snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
if (stat (buf, &st) == 0)
- data->mtime_cur = st.st_mtime;
+ mutt_get_stat_timespec (&data->mtime_cur, &st, MUTT_STAT_MTIME);
strfcpy (buf, ctx->path, sizeof (buf));
}
if (stat (buf, &st) == 0)
- ctx->mtime = st.st_mtime;
+ mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
}
/*
return -1;
/* determine which subdirectories need to be scanned */
- if (st_new.st_mtime > ctx->mtime)
+ if (mutt_stat_timespec_compare (&st_new, MUTT_STAT_MTIME, &ctx->mtime) > 0)
changed = 1;
- if (st_cur.st_mtime > data->mtime_cur)
+ if (mutt_stat_timespec_compare (&st_cur, MUTT_STAT_MTIME, &data->mtime_cur) > 0)
changed |= 2;
if (!changed)
return 0; /* nothing to do */
/* update the modification times on the mailbox */
- data->mtime_cur = st_cur.st_mtime;
- ctx->mtime = st_new.st_mtime;
+ mutt_get_stat_timespec (&data->mtime_cur, &st_cur, MUTT_STAT_MTIME);
+ mutt_get_stat_timespec (&ctx->mtime, &st_new, MUTT_STAT_MTIME);
/* do a fast scan of just the filenames in
* the subdirectories that have changed.
if (i == -1 && stat (buf, &st_cur) == -1)
modified = 1;
- if (st.st_mtime > ctx->mtime || st_cur.st_mtime > data->mtime_cur)
+ if ((mutt_stat_timespec_compare (&st, MUTT_STAT_MTIME, &ctx->mtime) > 0) ||
+ (mutt_stat_timespec_compare (&st_cur, MUTT_STAT_MTIME, &data->mtime_cur) > 0))
modified = 1;
if (!modified)
return 0;
- data->mtime_cur = st_cur.st_mtime;
- ctx->mtime = st.st_mtime;
+ mutt_get_stat_timespec (&data->mtime_cur, &st_cur, MUTT_STAT_MTIME);
+ mutt_get_stat_timespec (&ctx->mtime, &st, MUTT_STAT_MTIME);
memset (&mhs, 0, sizeof (mhs));
# define fgetc fgetc_unlocked
#endif
+#ifndef HAVE_STRUCT_TIMESPEC
+struct timespec
+{
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+
/* nifty trick I stole from ELM 2.5alpha. */
#ifdef MAIN_C
#define WHERE
/* flags for _mutt_system() */
#define MUTT_DETACH_PROCESS 1 /* detach subprocess from group */
+/* flags for mutt_get_stat_timespec */
+typedef enum
+{
+ MUTT_STAT_ATIME,
+ MUTT_STAT_MTIME,
+ MUTT_STAT_CTIME
+} mutt_stat_type;
+
/* flags for mutt_FormatString() */
typedef enum
{
char *path;
char *realpath; /* used for buffy comparison and the sidebar */
FILE *fp;
- time_t atime;
- time_t mtime;
+ struct timespec atime;
+ struct timespec mtime;
off_t size;
off_t vsize;
char *pattern; /* limit pattern string */
#endif
}
+int mutt_timespec_compare (struct timespec *a, struct timespec *b)
+{
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+
+ if (a->tv_nsec < b->tv_nsec)
+ return -1;
+ if (a->tv_nsec > b->tv_nsec)
+ return 1;
+ return 0;
+}
+
+void mutt_get_stat_timespec (struct timespec *dest, struct stat *sb, mutt_stat_type type)
+{
+ dest->tv_nsec = 0;
+
+ switch (type)
+ {
+ case MUTT_STAT_ATIME:
+ dest->tv_sec = sb->st_atime;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ dest->tv_nsec = sb->st_atim.tv_nsec;
+#endif
+ break;
+ case MUTT_STAT_MTIME:
+ dest->tv_sec = sb->st_mtime;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ dest->tv_nsec = sb->st_mtim.tv_nsec;
+#endif
+ break;
+ case MUTT_STAT_CTIME:
+ dest->tv_sec = sb->st_ctime;
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+ dest->tv_nsec = sb->st_ctim.tv_nsec;
+#endif
+ break;
+ }
+}
+
+int mutt_stat_timespec_compare (struct stat *sba, mutt_stat_type type, struct timespec *b)
+{
+ struct timespec a;
+
+ mutt_get_stat_timespec (&a, sba, type);
+ return mutt_timespec_compare (&a, b);
+}
+
+int mutt_stat_compare (struct stat *sba, mutt_stat_type sba_type,
+ struct stat *sbb, mutt_stat_type sbb_type)
+{
+ struct timespec a, b;
+
+ mutt_get_stat_timespec (&a, sba, sba_type);
+ mutt_get_stat_timespec (&b, sbb, sbb_type);
+ return mutt_timespec_compare (&a, &b);
+}
+
const char *mutt_make_version (void)
{
static char vstring[STRING];
}
else if ((f = fopen (path, "r")) != NULL)
{
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#else
struct utimbuf times;
+#endif /* HAVE_UTIMENSAT */
int ch;
/* Some mailbox creation tools erroneously append a blank line to
* only the type was accessed. This is important, because detection
* of "new mail" depends on those times set correctly.
*/
+#ifdef HAVE_UTIMENSAT
+ mutt_get_stat_timespec (&ts[0], &st, MUTT_STAT_ATIME);
+ mutt_get_stat_timespec (&ts[1], &st, MUTT_STAT_MTIME);
+ utimensat (0, path, ts, 0);
+#else
times.actime = st.st_atime;
times.modtime = st.st_mtime;
utime (path, ×);
+#endif
}
}
else
void mx_fastclose_mailbox (CONTEXT *ctx)
{
int i;
- struct utimbuf ut;
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#else
+ struct utimbuf ut;
+#endif /* HAVE_UTIMENSAT */
if(!ctx)
return;
/* fix up the times so buffy won't get confused */
- if (ctx->peekonly && ctx->path && (ctx->mtime > ctx->atime)) {
- ut.actime = ctx->atime;
- ut.modtime = ctx->mtime;
+ if (ctx->peekonly && ctx->path &&
+ (mutt_timespec_compare (&ctx->mtime, &ctx->atime) > 0))
+ {
+#ifdef HAVE_UTIMENSAT
+ ts[0] = ctx->atime;
+ ts[1] = ctx->mtime;
+ utimensat (0, ctx->path, ts, 0);
+#else
+ ut.actime = ctx->atime.tv_sec;
+ ut.modtime = ctx->mtime.tv_sec;
utime (ctx->path, &ut);
+#endif /* HAVE_UTIMENSAT */
}
/* never announce that a mailbox we've just left has new mail. #3290
time_t mutt_parse_date (const char *, HEADER *);
int is_from (const char *, char *, size_t, time_t *);
void mutt_touch_atime (int);
+int mutt_timespec_compare (struct timespec *a, struct timespec *b);
+void mutt_get_stat_timespec (struct timespec *dest, struct stat *sb, mutt_stat_type type);
+int mutt_stat_timespec_compare (struct stat *sba, mutt_stat_type type, struct timespec *b);
+int mutt_stat_compare (struct stat *sba, mutt_stat_type sba_type, struct stat *sbb, mutt_stat_type sbb_type);
+
const char *mutt_attach_fmt (
char *dest,