sysexits.h
cc-check-functions \
+ clock_gettime \
fgetc_unlocked \
futimens \
getaddrinfo \
iswblank \
mkdtemp \
strsep \
+ utimesnsat \
vasprintf \
wcscasecmp
cc-check-function-in-lib gethostent nsl
cc-check-function-in-lib setsockopt socket
cc-check-function-in-lib getaddrinfo_a anl
+
+ cc-with {-includes time.h} {
+ cc-check-types "struct timespec"
+ }
+
+ cc-with {-includes sys/stat.h} {
+ cc-check-members "struct stat.st_atim.tv_nsec"
+ }
}
###############################################################################
char *path;
char *realpath; /**< used for mailbox 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 */
*/
if (check_new && MailCheckRecent)
{
- 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 = false;
{
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 = true;
rc = 1;
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 (!MailCheckRecent || sb->st_mtime > mailbox->last_visited)
+ if (!MailCheckRecent ||
+ (mutt_stat_timespec_compare(sb, MUTT_STAT_MTIME, &mailbox->last_visited) > 0))
{
rc = 1;
mailbox->new = true;
if (mailbox->newly_created && (sb->st_ctime != sb->st_mtime || sb->st_ctime != sb->st_atime))
mailbox->newly_created = false;
- if (check_stats && (mailbox->stats_last_checked < sb->st_mtime))
+ if (check_stats &&
+ (mutt_stat_timespec_compare(sb, MUTT_STAT_MTIME, &mailbox->stats_last_checked) > 0))
{
struct Context *ctx =
mx_mbox_open(mailbox->path, MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK);
*/
void mutt_mailbox_cleanup(const char *path, struct stat *st)
{
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#else
struct utimbuf ut;
+#endif
if (CheckMboxSize)
{
/* fix up the times so mailbox 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(path, &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(path, NULL);
+#endif
+ }
}
}
return;
mailbox->notified = true;
- time(&mailbox->last_visited);
+#if HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &mailbox->last_visited);
+#else
+ mailbox->last_visited.tv_nsec = 0;
+ time(&mailbox->last_visited.tv_sec);
+#endif
}
/**
bool new; /**< mailbox has new mail */
/* These next three are only set when MailCheckStats is set */
- int msg_count; /**< total number of messages */
- int msg_unread; /**< number of unread messages */
- int msg_flagged; /**< number of flagged messages */
+ int msg_count; /**< total number of messages */
+ int msg_unread; /**< number of unread messages */
+ int msg_flagged; /**< number of flagged messages */
bool notified; /**< user has been notified */
enum MailboxType magic; /**< mailbox type */
bool 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. */
};
/**
*/
struct MhData
{
- 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);
mutt_str_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 = true;
- 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 = true;
+ }
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);
md = NULL;
last = &md;
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;
buf[sizeof(buf) - 1] = '\0';
}
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);
if (!ctx->readonly)
ctx->readonly = access(ctx->path, W_OK) ? true : false;
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;
}
* only the type was accessed. This is important, because detection
* of "new mail" depends on those times set correctly.
*/
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+ 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
struct utimbuf times;
times.actime = st->st_atime;
times.modtime = st->st_mtime;
utime(path, ×);
+#endif
}
return magic;
#define fgetc fgetc_unlocked
#endif
+#ifndef HAVE_STRUCT_TIMESPEC
+struct timespec
+{
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+
+/* flags for mutt_get_stat_timespec */
+enum MuttStatType
+{
+ MUTT_STAT_ATIME,
+ MUTT_STAT_MTIME,
+ MUTT_STAT_CTIME
+};
+
/* flags for mutt_enter_string_full() */
#define MUTT_ALIAS (1 << 0) /**< do alias "completion" by calling up the alias-menu */
#define MUTT_FILE (1 << 1) /**< do file completion */
sleep(s);
}
+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, enum MuttStatType 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, enum MuttStatType 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, enum MuttStatType sba_type,
+ struct stat *sbb, enum MuttStatType 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);
+}
+
/**
* mutt_make_version - Generate the NeoMutt version string
* @retval ptr Version string
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include "mutt.h"
#include "format_flags.h"
struct Address;
void mutt_save_path(char *d, size_t dsize, struct Address *a);
void mutt_sleep(short s);
+int mutt_timespec_compare(struct timespec *a, struct timespec *b);
+void mutt_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type);
+int mutt_stat_timespec_compare(struct stat *sba, enum MuttStatType type, struct timespec *b);
+int mutt_stat_compare(struct stat *sba, enum MuttStatType sba_type, struct stat *sbb, enum MuttStatType sbb_type);
+
#define mutt_mktemp(a, b) mutt_mktemp_pfx_sfx(a, b, "neomutt", NULL)
#define mutt_mktemp_pfx_sfx(a, b, c, d) mutt_mktemp_full(a, b, c, d, __FILE__, __LINE__)
*/
void mx_fastclose_mailbox(struct Context *ctx)
{
+#ifdef HAVE_UTIMENSAT
+ struct timespec ts[2];
+#else
struct utimbuf ut;
+#endif /* HAVE_UTIMENSAT */
if (!ctx)
return;
/* fix up the times so mailbox won't get confused */
- if (ctx->peekonly && ctx->path && (ctx->mtime > ctx->atime))
+ if (ctx->peekonly && ctx->path && (mutt_timespec_compare(&ctx->mtime, &ctx->atime) > 0))
{
- ut.actime = ctx->atime;
- ut.modtime = ctx->mtime;
+#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
notmuch_query_set_sort(q, NOTMUCH_SORT_NEWEST_FIRST);
read_threads_query(ctx, q, true, 0);
- ctx->mtime = time(NULL);
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
rc = 0;
if (ctx->msgcount > data->oldmsgcount)
if (!is_longrun(data))
release_db(data);
if (hdr->changed)
- ctx->mtime = time(NULL);
+ {
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
+ }
mutt_debug(1, "nm: tags modify done [rc=%d]\n", rc);
return rc;
}
if (!is_longrun(data))
release_db(data);
- ctx->mtime = time(NULL);
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
return rc;
}
if (!is_longrun(data))
release_db(data);
- ctx->mtime = time(NULL);
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
mx_update_context(ctx, ctx->msgcount);
data->oldmsgcount = 0;
if (!data || (get_database_mtime(data, &mtime) != 0))
return -1;
- if (ctx->mtime >= mtime)
+ if (ctx->mtime.tv_sec >= mtime)
{
mutt_debug(2, "nm: check unnecessary (db=%lu ctx=%lu)\n", mtime, ctx->mtime);
return 0;
if (!is_longrun(data))
release_db(data);
- ctx->mtime = time(NULL);
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
mutt_debug(1, "nm: ... check done [count=%d, new_flags=%d, occult=%d]\n",
ctx->msgcount, new_flags, occult);
if (!is_longrun(data))
release_db(data);
if (changed)
- ctx->mtime = time(NULL);
+ {
+ ctx->mtime.tv_sec = time(NULL);
+ ctx->mtime.tv_nsec = 0;
+ }
mutt_debug(1, "nm: .... sync done [rc=%d]\n", rc);
return rc;