src/bin/ksh/mail.c

208 lines
4.3 KiB
C

/* $OpenBSD: mail.c,v 1.27 2019/01/14 08:48:16 schwarze Exp $ */
/*
* Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
* John R. MacMillan
*/
#include <sys/stat.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include "config.h"
#include "sh.h"
#define MBMESSAGE "you have mail in $_"
typedef struct mbox {
struct mbox *mb_next; /* next mbox in list */
char *mb_path; /* path to mail file */
char *mb_msg; /* to announce arrival of new mail */
time_t mb_mtime; /* mtime of mail file */
} mbox_t;
/*
* $MAILPATH is a linked list of mboxes. $MAIL is a treated as a
* special case of $MAILPATH, where the list has only one node. The
* same list is used for both since they are exclusive.
*/
static mbox_t *mplist;
static mbox_t mbox;
static struct timespec mlastchkd; /* when mail was last checked */
static time_t mailcheck_interval;
static void munset(mbox_t *); /* free mlist and mval */
static mbox_t * mballoc(char *, char *); /* allocate a new mbox */
static void mprintit(mbox_t *);
void
mcheck(void)
{
mbox_t *mbp;
struct timespec elapsed, now;
struct tbl *vp;
struct stat stbuf;
static int first = 1;
if (mplist)
mbp = mplist;
else if ((vp = global("MAIL")) && (vp->flag & ISSET))
mbp = &mbox;
else
mbp = NULL;
if (mbp == NULL)
return;
clock_gettime(CLOCK_MONOTONIC, &now);
if (first) {
mlastchkd = now;
first = 0;
}
timespecsub(&now, &mlastchkd, &elapsed);
if (elapsed.tv_sec >= mailcheck_interval) {
mlastchkd = now;
while (mbp) {
if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 &&
S_ISREG(stbuf.st_mode)) {
if (stbuf.st_size &&
mbp->mb_mtime != stbuf.st_mtime &&
stbuf.st_atime <= stbuf.st_mtime)
mprintit(mbp);
mbp->mb_mtime = stbuf.st_mtime;
} else {
/*
* Some mail readers remove the mail
* file if all mail is read. If file
* does not exist, assume this is the
* case and set mtime to zero.
*/
mbp->mb_mtime = 0;
}
mbp = mbp->mb_next;
}
}
}
void
mcset(int64_t interval)
{
mailcheck_interval = interval;
}
void
mbset(char *p)
{
struct stat stbuf;
afree(mbox.mb_msg, APERM);
afree(mbox.mb_path, APERM);
/* Save a copy to protect from export (which munges the string) */
mbox.mb_path = str_save(p, APERM);
mbox.mb_msg = NULL;
if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbox.mb_mtime = stbuf.st_mtime;
else
mbox.mb_mtime = 0;
}
void
mpset(char *mptoparse)
{
mbox_t *mbp;
char *mpath, *mmsg, *mval;
char *p;
munset( mplist );
mplist = NULL;
mval = str_save(mptoparse, APERM);
while (mval) {
mpath = mval;
if ((mval = strchr(mval, ':')) != NULL) {
*mval = '\0';
mval++;
}
/* POSIX/bourne-shell say file%message */
for (p = mpath; (mmsg = strchr(p, '%')); ) {
/* a literal percent? (POSIXism) */
if (mmsg > mpath && mmsg[-1] == '\\') {
/* use memmove() to avoid overlap problems */
memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
p = mmsg;
continue;
}
break;
}
/* at&t ksh says file?message */
if (!mmsg && !Flag(FPOSIX))
mmsg = strchr(mpath, '?');
if (mmsg) {
*mmsg = '\0';
mmsg++;
if (*mmsg == '\0')
mmsg = NULL;
}
if (*mpath == '\0')
continue;
mbp = mballoc(mpath, mmsg);
mbp->mb_next = mplist;
mplist = mbp;
}
}
static void
munset(mbox_t *mlist)
{
mbox_t *mbp;
while (mlist != NULL) {
mbp = mlist;
mlist = mbp->mb_next;
if (!mlist)
afree(mbp->mb_path, APERM);
afree(mbp, APERM);
}
}
static mbox_t *
mballoc(char *p, char *m)
{
struct stat stbuf;
mbox_t *mbp;
mbp = alloc(sizeof(mbox_t), APERM);
mbp->mb_next = NULL;
mbp->mb_path = p;
mbp->mb_msg = m;
if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
mbp->mb_mtime = stbuf.st_mtime;
else
mbp->mb_mtime = 0;
return(mbp);
}
static void
mprintit(mbox_t *mbp)
{
struct tbl *vp;
#if 0
/*
* I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we
* crash as the code looks now if we do not set vp. Now, this is
* easy to fix too, but I'd like to see what POSIX says before doing
* a change like that.
*/
if (!Flag(FSH))
#endif
/* Ignore setstr errors here (arbitrary) */
setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR);
shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
unset(vp, 0);
}