#include "mutt_curses.h"
#include "mx.h"
-int MonitorFilesChanged;
-int MonitorContextChanged;
+int MonitorFilesChanged = 0;
+int MonitorContextChanged = 0;
+static int INotifyFd = -1;
+static struct Monitor *Monitor = NULL;
+static size_t PollFdsCount = 0;
+static size_t PollFdsLen = 0;
+static struct pollfd *PollFds = NULL;
+
+static int MonitorContextDescriptor = -1;
+
+#define INOTIFY_MASK_DIR (IN_MOVED_TO | IN_ATTRIB | IN_CLOSE_WRITE | IN_ISDIR)
+#define INOTIFY_MASK_FILE IN_CLOSE_WRITE
+
+#define EVENT_BUFLEN MAX(4096, sizeof(struct inotify_event) + NAME_MAX + 1)
+
+#define RESOLVERES_FAIL_NOMAILBOX -3
+#define RESOLVERES_FAIL_NOMAGIC -2
+#define RESOLVERES_FAIL_STAT -1
+#define RESOLVERES_OK_NOTEXISTING 0
+#define RESOLVERES_OK_EXISTING 1
+
+/**
+ * struct Monitor - A watch on a file
+ */
struct Monitor
{
struct Monitor *next;
int desc;
};
-static int INotifyFd = -1;
-static struct Monitor *Monitor = NULL;
-static size_t PollFdsCount = 0;
-static size_t PollFdsLen = 0;
-static struct pollfd *PollFds;
-
-static int MonitorContextDescriptor = -1;
-
+/**
+ * struct MonitorInfo - Information about a monitored file
+ */
struct MonitorInfo
{
short magic;
char path_buf[PATH_MAX]; /* access via path only (maybe not initialized) */
};
-#define INOTIFY_MASK_DIR (IN_MOVED_TO | IN_ATTRIB | IN_CLOSE_WRITE | IN_ISDIR)
-#define INOTIFY_MASK_FILE IN_CLOSE_WRITE
-
+/**
+ * mutt_poll_fd_add - Add a file to the watch list
+ * @param fd File to watch
+ * @param events Events to listen for, e.g. POLLIN
+ */
static void mutt_poll_fd_add(int fd, short events)
{
int i = 0;
- for (; i < PollFdsCount && PollFds[i].fd != fd; ++i)
+ for (; (i < PollFdsCount) && (PollFds[i].fd != fd); ++i)
;
if (i == PollFdsCount)
PollFds[i].events |= events;
}
+/**
+ * mutt_poll_fd_remove - Remove a file from the watch list
+ * @param fd File to remove
+ * @retval 0 Success
+ * @retval -1 Error
+ */
static int mutt_poll_fd_remove(int fd)
{
- int i = 0, d;
- for (i = 0; i < PollFdsCount && PollFds[i].fd != fd; ++i)
+ int i = 0;
+ for (; (i < PollFdsCount) && (PollFds[i].fd != fd); ++i)
;
if (i == PollFdsCount)
return -1;
- d = PollFdsCount - i - 1;
- if (d)
+ int d = PollFdsCount - i - 1;
+ if (d != 0)
memmove(&PollFds[i], &PollFds[i + 1], d * sizeof(struct pollfd));
PollFdsCount--;
return 0;
}
+/**
+ * monitor_init - Set up file monitoring
+ * @retval 0 Success
+ * @retval -1 Error
+ */
static int monitor_init(void)
{
if (INotifyFd == -1)
return 0;
}
+/**
+ * monitor_check_free - Close down file monitoring
+ */
static void monitor_check_free(void)
{
- if (!Monitor && INotifyFd != -1)
+ if (!Monitor && (INotifyFd != -1))
{
mutt_poll_fd_remove(INotifyFd);
close(INotifyFd);
}
}
+/**
+ * monitor_create - Create a new file monitor
+ * @param info Details of file to monitor
+ * @param descriptor Watch descriptor
+ * @retval ptr Newly allocated Monitor
+ */
static struct Monitor *monitor_create(struct MonitorInfo *info, int descriptor)
{
struct Monitor *monitor = mutt_mem_calloc(1, sizeof(struct Monitor));
return monitor;
}
+/**
+ * monitor_delete - Free a file monitor
+ * @param monitor Monitor to free
+ */
static void monitor_delete(struct Monitor *monitor)
{
struct Monitor **ptr = &Monitor;
ptr = &(*ptr)->next;
}
- FREE(&monitor->mh_backup_path); /* __FREE_CHECKED__ */
+ FREE(&monitor->mh_backup_path);
monitor = monitor->next;
- FREE(ptr); /* __FREE_CHECKED__ */
+ FREE(ptr);
*ptr = monitor;
}
+/**
+ * monitor_handle_ignore - Listen for when a backup file is closed
+ * @param desc Watch descriptor
+ * @retval >=0 New descriptor
+ * @retval -1 Error
+ */
static int monitor_handle_ignore(int desc)
{
int new_desc = -1;
struct Monitor *iter = Monitor;
struct stat sb;
- while (iter && iter->desc != desc)
+ while (iter && (iter->desc != desc))
iter = iter->next;
if (iter)
{
- if (iter->magic == MUTT_MH && stat(iter->mh_backup_path, &sb) == 0)
+ if ((iter->magic == MUTT_MH) && (stat(iter->mh_backup_path, &sb) == 0))
{
- if ((new_desc = inotify_add_watch(INotifyFd, iter->mh_backup_path,
- INOTIFY_MASK_FILE)) == -1)
+ new_desc = inotify_add_watch(INotifyFd, iter->mh_backup_path, INOTIFY_MASK_FILE);
+ if (new_desc == -1)
{
mutt_debug(2, "inotify_add_watch failed for '%s', errno=%d %s\n",
iter->mh_backup_path, errno, strerror(errno));
}
else
{
- mutt_debug(3, "cleanup watch (implicitely removed) - descriptor=%d\n", desc);
+ mutt_debug(3, "cleanup watch (implicitly removed) - descriptor=%d\n", desc);
}
if (MonitorContextDescriptor == desc)
return new_desc;
}
-#define EVENT_BUFLEN MAX(4096, sizeof(struct inotify_event) + NAME_MAX + 1)
+/**
+ * monitor_resolve - Get the monitor for a mailbox
+ * @param[out] info Details of the mailbox's monitor
+ * @param[in] mailbox Mailbox
+ * @retval >=0 mailbox is valid and locally accessible:
+ * 0: no monitor / 1: preexisting monitor
+ * @retval -3 no mailbox (MonitorInfo: no fields set)
+ * @retval -2 magic not set
+ * @retval -1 stat() failed (see errno; MonitorInfo fields: magic, isdir, path)
+ *
+ * If mailbox is NULL, the current mailbox (Context) is used.
+ */
+static int monitor_resolve(struct MonitorInfo *info, struct Mailbox *mailbox)
+{
+ struct Monitor *iter;
+ char *fmt = NULL;
+ struct stat sb;
+
+ if (mailbox)
+ {
+ info->magic = mailbox->magic;
+ info->path = mailbox->realpath;
+ }
+ else if (Context)
+ {
+ info->magic = Context->magic;
+ info->path = Context->realpath;
+ }
+ else
+ {
+ return RESOLVERES_FAIL_NOMAILBOX;
+ }
+
+ if (!info->magic)
+ {
+ return RESOLVERES_FAIL_NOMAGIC;
+ }
+ else if (info->magic == MUTT_MAILDIR)
+ {
+ info->isdir = 1;
+ fmt = "%s/new";
+ }
+ else
+ {
+ info->isdir = 0;
+ if (info->magic == MUTT_MH)
+ fmt = "%s/.mh_sequences";
+ }
-/* mutt_monitor_poll: Waits for I/O ready file descriptors or signals.
+ if (fmt)
+ {
+ snprintf(info->path_buf, sizeof(info->path_buf), fmt, info->path);
+ info->path = info->path_buf;
+ }
+ if (stat(info->path, &sb) != 0)
+ return RESOLVERES_FAIL_STAT;
+
+ iter = Monitor;
+ while (iter && ((iter->st_ino != sb.st_ino) || (iter->st_dev != sb.st_dev)))
+ iter = iter->next;
+
+ info->st_dev = sb.st_dev;
+ info->st_ino = sb.st_ino;
+ info->monitor = iter;
+
+ return iter ? RESOLVERES_OK_EXISTING : RESOLVERES_OK_NOTEXISTING;
+}
+
+/**
+ * mutt_monitor_poll - Check for filesystem changes
+ * @retval -3 unknown/unexpected events: poll timeout / fds not handled by us
+ * @retval -2 monitor detected changes, no STDIN input
+ * @retval -1 error (see errno)
+ * @retval 0 (1) input ready from STDIN, or (2) monitoring inactive -> no poll()
+ *
+ * Wait for I/O ready file descriptors or signals.
*
- * return values:
- * -3 unknown/unexpected events: poll timeout / fds not handled by us
- * -2 monitor detected changes, no STDIN input
- * -1 error (see errno)
- * 0 (1) input ready from STDIN, or (2) monitoring inactive -> no poll()
* MonitorFilesChanged also reflects changes to monitored files.
*
* Only STDIN and INotify file handles currently expected/supported.
- * More would ask for common infrastructur (sockets?).
+ * More would ask for common infrastructure (sockets?).
*/
int mutt_monitor_poll(void)
{
- int rc = 0, fds, i, inputReady;
+ int rc = 0;
char buf[EVENT_BUFLEN] __attribute__((aligned(__alignof__(struct inotify_event))));
MonitorFilesChanged = 0;
if (INotifyFd != -1)
{
- fds = poll(PollFds, PollFdsLen, MuttGetchTimeout);
+ int fds = poll(PollFds, PollFdsLen, MuttGetchTimeout);
if (fds == -1)
{
}
else
{
- inputReady = 0;
- for (i = 0; fds && i < PollFdsCount; ++i)
+ bool inputReady = false;
+ for (int i = 0; fds && (i < PollFdsCount); ++i)
{
if (PollFds[i].revents)
{
fds--;
if (PollFds[i].fd == 0)
{
- inputReady = 1;
+ inputReady = true;
}
else if (PollFds[i].fd == INotifyFd)
{
break;
}
- while (ptr < buf + len)
+ while (ptr < (buf + len))
{
event = (const struct inotify_event *) ptr;
mutt_debug(5, "+ detail: descriptor=%d mask=0x%x\n", event->wd,
return rc;
}
-#define RESOLVERES_OK_NOTEXISTING 0
-#define RESOLVERES_OK_EXISTING 1
-#define RESOLVERES_FAIL_NOMAILBOX -3
-#define RESOLVERES_FAIL_NOMAGIC -2
-#define RESOLVERES_FAIL_STAT -1
-
-/* monitor_resolve: resolve monitor entry match by Mailbox, or - if NULL - by Context.
- *
- * return values:
- * >=0 mailbox is valid and locally accessible:
- * 0: no monitor / 1: preexisting monitor
- * -3 no mailbox (MONITORINFO: no fields set)
- * -2 magic not set
- * -1 stat() failed (see errno; MONITORINFO fields: magic, isdir, path)
- */
-static int monitor_resolve(struct MonitorInfo *info, struct Mailbox *mailbox)
-{
- struct Monitor *iter;
- char *fmt = NULL;
- struct stat sb;
-
- if (mailbox)
- {
- info->magic = mailbox->magic;
- info->path = mailbox->realpath;
- }
- else if (Context)
- {
- info->magic = Context->magic;
- info->path = Context->realpath;
- }
- else
- {
- return RESOLVERES_FAIL_NOMAILBOX;
- }
-
- if (!info->magic)
- {
- return RESOLVERES_FAIL_NOMAGIC;
- }
- else if (info->magic == MUTT_MAILDIR)
- {
- info->isdir = 1;
- fmt = "%s/new";
- }
- else
- {
- info->isdir = 0;
- if (info->magic == MUTT_MH)
- fmt = "%s/.mh_sequences";
- }
-
- if (fmt)
- {
- snprintf(info->path_buf, sizeof(info->path_buf), fmt, info->path);
- info->path = info->path_buf;
- }
- if (stat(info->path, &sb) != 0)
- return RESOLVERES_FAIL_STAT;
-
- iter = Monitor;
- while (iter && (iter->st_ino != sb.st_ino || iter->st_dev != sb.st_dev))
- iter = iter->next;
-
- info->st_dev = sb.st_dev;
- info->st_ino = sb.st_ino;
- info->monitor = iter;
-
- return iter ? RESOLVERES_OK_EXISTING : RESOLVERES_OK_NOTEXISTING;
-}
-
-/* mutt_monitor_add: add file monitor from Mailbox, or - if NULL - from Context.
+/**
+ * mutt_monitor_add - Add a watch for a mailbox
+ * @param mailbox Mailbox to watch
+ * @retval 0 success: new or already existing monitor
+ * @retval -1 failed: no mailbox, inaccessible file, create monitor/watcher failed
*
- * return values:
- * 0 success: new or already existing monitor
- * -1 failed: no mailbox, inaccessible file, create monitor/watcher failed
+ * If mailbox is NULL, the current mailbox (Context) is used.
*/
int mutt_monitor_add(struct Mailbox *mailbox)
{
struct MonitorInfo info;
- uint32_t mask;
- int desc;
- desc = monitor_resolve(&info, mailbox);
+ int desc = monitor_resolve(&info, mailbox);
if (desc != RESOLVERES_OK_NOTEXISTING)
{
if (!mailbox && (desc == RESOLVERES_OK_EXISTING))
MonitorContextDescriptor = info.monitor->desc;
- return desc == RESOLVERES_OK_EXISTING ? 0 : -1;
+ return (desc == RESOLVERES_OK_EXISTING) ? 0 : -1;
}
- mask = info.isdir ? INOTIFY_MASK_DIR : INOTIFY_MASK_FILE;
- if ((INotifyFd == -1 && monitor_init() == -1) ||
+ uint32_t mask = info.isdir ? INOTIFY_MASK_DIR : INOTIFY_MASK_FILE;
+ if (((INotifyFd == -1) && (monitor_init() == -1)) ||
(desc = inotify_add_watch(INotifyFd, info.path, mask)) == -1)
{
mutt_debug(2, "inotify_add_watch failed for '%s', errno=%d %s\n", info.path,
return 0;
}
-/* mutt_monitor_remove: remove file monitor from Mailbox, or - if NULL - from Context.
+/**
+ * mutt_monitor_remove - Remove a watch for a mailbox
+ * @param mailbox Mailbox
+ * @retval 0 monitor removed (not shared)
+ * @retval 1 monitor not removed (shared)
+ * @retval 2 no monitor
*
- * return values:
- * 0 monitor removed (not shared)
- * 1 monitor not removed (shared)
- * 2 no monitor
+ * If mailbox is NULL, the current mailbox (Context) is used.
*/
int mutt_monitor_remove(struct Mailbox *mailbox)
{
{
if (mailbox)
{
- if (monitor_resolve(&info2, NULL) == RESOLVERES_OK_EXISTING &&
- info.st_ino == info2.st_ino && info.st_dev == info2.st_dev)
+ if ((monitor_resolve(&info2, NULL) == RESOLVERES_OK_EXISTING) &&
+ (info.st_ino == info2.st_ino) && (info.st_dev == info2.st_dev))
+ {
return 1;
+ }
}
else
{