Watch utility for local maildir.
git clone git://git.skec.site/pub/mailstatus.git
log | files | refs | readme | license

commit 1680ee952c1c2a4082ec77dcf5298d7b7ccf86b3
Author: Michael Skec
Date:   Thu, 16 Nov 2023 10:07:45 +1100

Initial commit

Diffstat:
A.gitignore | 1+
ALICENSE | 23+++++++++++++++++++++++
AMakefile | 18++++++++++++++++++
AREADME | 6++++++
Amailstatus.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 226 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +mailstatus diff --git a/LICENSE b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright © 2023 + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\ No newline at end of file diff --git a/Makefile b/Makefile @@ -0,0 +1,18 @@ +OUT=mailstatus +SRC=mailstatus.c + +CFLAGS+=-O2 +LDFLAGS+= + +all: $(OUT) + +debug: all + @gdb $(OUT) + +clean: + rm -f $(OUT) + +$(OUT): $(SRC) + gcc $(CFLAGS) $(LDFLAGS) $(SRC) -o $(OUT) + +.PHONY: all debug diff --git a/README b/README @@ -0,0 +1,6 @@ +tint2-mailstatus +---------------- + +Mail status information, for my own desktop setup. The program watches the +given mailbox's 'new' directory for new mail files and outputs this information +accordingly. diff --git a/mailstatus.c b/mailstatus.c @@ -0,0 +1,178 @@ +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/inotify.h> +#include <sys/types.h> +#include <unistd.h> + +/* Maximum bytes that can be displayed. */ +#define MAX_DISPLAY_LEN 25 + +const char *_maildir_inbox; +char _maildir_inbox_new[FILENAME_MAX]; + +/* The display buffer */ +static char _buffer[256]; +static int _buffer_len = 0; + +/* Current mail info */ +static int _unread_count = 0; + +static int _inotify = -1, _watch = -1; +static volatile sig_atomic_t _sigint = 0; + +static void +sigint_handler(int param) +{ + (void)param; + _sigint = 1; + fprintf(stdout, "caught SIGINT\n"); +} + +static void +update_status(void) +{ + DIR *d; + struct dirent *de; + + d = opendir(_maildir_inbox_new); + if (d == NULL) + { + fprintf(stderr, "update failed (%d): %s", errno, strerror(errno)); + return; + } + + _unread_count = 0; + for (; (de = readdir(d)) != NULL;) + { + if (de->d_type == DT_REG) + ++_unread_count; + } + closedir(d); + + if (_unread_count == 0) + { + _buffer_len = snprintf(_buffer, sizeof(_buffer), + "No new mail."); + } + else + { + _buffer_len = snprintf(_buffer, sizeof(_buffer), + "(%d) new mail.", _unread_count); + } +} + +static void +print_status(void) +{ + int p, pad; + + fputs(_buffer, stdout); + + pad = MAX_DISPLAY_LEN - _buffer_len; + + for (p = 0; p < pad; ++p) + fputc(' ', stdout); + + fputs("\n", stdout); + fflush(stdout); +} + +int +main(int argc, char **argv) +{ + int rc = 0; + + /* Get maildir from command line */ + if (argc < 2) + { + fprintf(stderr, "usage: mailstatus <mailbox>\n"); + rc = -1; + goto exit; + } + + _maildir_inbox = argv[1]; + snprintf(_maildir_inbox_new, sizeof(_maildir_inbox_new), + "%s/new", _maildir_inbox); + + //fprintf(stdout, "Watch directory: %s\n", _maildir_inbox_new); + + _inotify = inotify_init1(IN_NONBLOCK); + if (_inotify == -1) + { + fprintf(stderr, "failed to initialise inotify instance (%d): %s\n", + errno, strerror(errno)); + rc = -1; + goto exit; + } + + /* Register inotify watch on the mail directory */ + _watch = inotify_add_watch(_inotify, + _maildir_inbox_new, + IN_CREATE | IN_DELETE); + if (_watch == -1) + { + fprintf(stderr, "failed to add inotify watch (%d): %s\n", + errno, strerror(errno)); + rc = -1; + goto exit; + } + + struct sigaction sigact = { sigint_handler }; + sigaction(SIGINT, &sigact, NULL); + + /* Print initial mail status */ + update_status(); + print_status(); + + for (;!_sigint;) + { + /* Check for inotify updates */ + char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; + int n; + n = read(_inotify, buf, sizeof(buf)); + if (n == -1) + { + if (errno != EAGAIN) + { + fprintf(stderr, "read failed (%d): %s\n", + errno, strerror(errno)); + } + } + else if (n > 0) + { + char *p; + const struct inotify_event *e; + int count = 0; + + for (p = buf; + p < buf + n; + p += sizeof(struct inotify_event) + e->len) + { + e = (const struct inotify_event *)p; + + if (e->mask & (IN_CREATE | IN_DELETE)) + ++count; + } + + if (count > 0) + { + /* Update mail status */ + update_status(); + print_status(); + } + } + + sleep(1); + } + +exit: + if (_inotify > -1) + close(_inotify); + + return rc; +}