/*
* Copyright 1998-2002 Ben Smithurst <ben@smithurst.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Miscellaneous routines shared by multiple programs.
*/
static const char rcsid[] =
"$BCPS: src/mailutils/misc.c,v 1.75 2003/01/19 19:18:25 ben Exp $";
#include "misc.h"
int quiet = -1;
volatile sig_atomic_t sig_count = 0;
char *curfile = NULL;
static int maildir_seq;
void
signal_handler(int sig) {
if (++sig_count > 2)
exit(2);
}
char *
print_mailbox(const char *name) {
int maildir = is_maildir(name);
int mlen = strlen(mailpath());
int hlen = strlen(homedir());
static char buf[1024];
const char *pre, *suf;
if (strncmp(name, mailpath(), mlen) == 0 && name[mlen] == '/') {
pre = "=";
name += mlen + 1;
} else if (strncmp(name, homedir(), hlen) == 0 && name[hlen] == '/') {
pre = "~";
name += hlen;
} else
pre = "";
if (maildir)
suf = "/";
else
suf = "";
snprintf(buf, sizeof buf, "%s%s%s", pre, name, suf);
return (buf);
}
static char *
hostname(void) {
static char host[MAXHOSTNAMELEN];
if (host[0] == '\0') {
if (gethostname(host, sizeof host - 1) < 0)
return (NULL);
host[sizeof host - 1] = '\0';
}
return (host);
}
#define GLT_MAX 10
#define GLT_BUF 1024
gl_getline_t glts[GLT_MAX];
int glcount = 0;
char *
gl_getline(FILE *fp) {
gl_getline_t *glp;
int i, n;
for (i = 0; i < glcount; i++)
if (glts[i].fp == fp || glts[i].fp == NULL)
break;
if (i == glcount || glts[i].fp == NULL) {
if (glcount == GLT_MAX) {
warnx("glcount==GLT_MAX");
errno = ENOMEM;
return (NULL);
}
glts[i].fp = fp;
if (i == glcount) {
if ((glts[i].buf = malloc(GLT_BUF)) == NULL) {
warnx("can't malloc %d bytes for glt.buf", GLT_BUF);
return (NULL);
}
glts[i].size = GLT_BUF;
glts[i].eof = 0;
glcount++;
}
}
glp = &glts[i];
if (glp->eof)
return (NULL);
n = 0;
for (;;) {
char *p;
if (glp->size - n < GLT_BUF) {
glp->size += GLT_BUF;
if ((p = realloc(glp->buf, glp->size)) == NULL) {
warnx("can't realloc glp->buf to %d",
glp->size);
gl_destroy(glp->fp);
return (NULL);
} else
glp->buf = p;
}
p = fgets(glp->buf + n, glp->size - n, glp->fp);
if (p == NULL) {
glp->eof = 1;
if (n == 0)
return (NULL);
glp->buf[n] = '\n';
glp->buf[n+1] = '\0';
return (glp->buf);
}
n += strlen(p);
if (glp->buf[n-1] == '\n')
return (glp->buf);
}
}
void
gl_destroy(FILE *fp) {
int i;
for (i = 0; i < glcount; i++)
if (glts[i].fp == fp) {
glts[i].fp = NULL;
return;
}
}
/*
* This function would be much easier to write by just using a regex, but
* that would probably require linking with the PCRE library. It's not hard
* to do manually... honest.
*
* XXX this will fail on addresses with a space in them (e.g., "foo
* bar"@domain.example.com).
*/
int
is_from(char *line, int strict) {
/* this function attempts to check whether the specified line matches
* this regular expression:
*
* "^From\\s+(\\S+)\\s+(?:[a-zA-Z]{3},?\\s+)?" / Common start
* "(?:" / Non-extracting bracket
* "[a-zA-Z]{3}\\s+\\d?\\d|" / First form
* "\\d?\\d\\s+[a-zA-Z]{3}\\s+\\d\\d(?:\\d\\d)?" / Second form
* ")" / End alternation
* "\\s+\\d\\d?:\\d\\d?"; / Start of time
*/
/* Check for From\s */
if (strncmp(line, "From", 4) != 0 || !isspace(line[4]))
return (0);
else if (!strict)
return (1);
/* Now skip spaces, non-spaces (address), and more spaces. */
line += 5;
while (isspace(*line))
line++;
while (*line != '\0' && !isspace(*line))
line++;
while (isspace(*line))
line++;
if (*line == '\0')
return (0);
/* Check for three letters. This must be followed by either a comma
* or a space, and could either be the month name or the weekday
* name.
*/
if (isalpha(line[0]) && isalpha(line[1]) && isalpha(line[2])) {
if (line[3] == ',')
line += 4;
else if (!isspace(line[3]))
return (0);
else
line += 3;
while (isspace(*line))
line++;
if (*line == '\0')
return (0);
/* we have matched three letters. They are either a weekday
* name or a month name. If the next character is another
* letter, it was a weekday name, and is followed by a
* month. (first form.) Otherwise, we have to do a bit more
* work. There should be a digit next, if not a letter,
* followed by some spaces. After the spaces, the next
* character will determine which of the two alternatives to
* take.
*/
if (isalpha(*line)) {
if (!isalpha(line[1]) || !isalpha(line[2]))
return (0);
line += 3;
while (isspace(*line))
line++;
if (!isdigit(*line++))
return (0);
if (isdigit(*line))
line++;
} else if (!isdigit(*line++))
return (0);
else {
if (isdigit(*line))
line++;
if (!isspace(*line++))
return (0);
while (isspace(*line))
line++;
if (*line == '\0')
return (0);
if (isalpha(*line))
goto second_form;
else if (isdigit(*line++))
/* was first form all along */
goto near_end;
else
return (0);
}
} else {
/* the second form */
if (!isdigit(*line++))
return (0);
if (isdigit(*line))
line++;
while (isspace(*line))
line++;
second_form:
if (!isalpha(line[0]) || !isalpha(line[1]) ||
!isalpha(line[2]))
return (0);
line += 3;
while (isspace(*line))
return (0);
if (!isdigit(line[0]) || !isdigit(line[1]))
return (0);
line += 2;
if (isdigit(line[0]) && isdigit(line[1]))
line += 2;
}
while (isspace(*line))
line++;
if (!isdigit(*line++))
return (0);
near_end:
if (isdigit(*line))
line++;
if (*line != ':')
return (0);
line++;
if (!isdigit(*line))
return (0);
return (1);
}
static char *
maildir_nextfile(char *dir) {
static int done_new;
static DIR *dp;
struct dirent *dep;
static const char *subdir;
static char file[MAXPATHLEN];
if (dir == NULL) {
done_new = 0;
if (dp != NULL) {
closedir(dp);
dp = NULL;
}
return (NULL);
}
if (dp == NULL) {
subdir = done_new ? "cur" : "new";
if ((dp = opendir(subdir)) == NULL) {
warn("opendir %s/%s", dir, subdir);
return (NULL);
}
}
while ((dep = readdir(dp)) != NULL)
if (dep->d_name[0] != '.') {
snprintf(file, sizeof file, "%s/%s", subdir, dep->d_name);
return (file);
}
if (done_new)
return (NULL);
closedir(dp);
dp = NULL;
done_new = 1;
return (maildir_nextfile(dir));
}
static int
process_maildir(char *file, int (*func)(int, char *, FILE *)) {
int ok = 1, fd, reset = 1, ret;
FILE *fp;
char *fn;
if ((fd = open(".", O_RDONLY)) < 0) {
warn("open .");
return (0);
}
if (chdir(file) != 0) {
warn("chdir %s", file);
return (0);
}
maildir_nextfile(NULL);
while (ok && (fn = maildir_nextfile(file)) != NULL) {
if (sig_count > 0)
errx(1, "exiting because of signal");
if ((fp = fopen(fn, "r")) == NULL) {
warn("%s/%s", file, fn);
ok = 0;
break;
}
switch (ret = func(reset, fn, fp)) {
case RET_ERROR:
warn("maildir %s file %s", file, fn);
/* FALLTHROUGH */
case RET_LOCALERROR:
ok = 0;
break;
case RET_NOCHANGE:
/* nothing to do */
break;
case RET_DELETE:
if (unlink(fn) != 0) {
warn("unlink %s/%s", file, fn);
ok = 0;
}
break;
default:
warnx("unknown return %d for %s/%s", ret, file, fn);
ok = 0;
break;
}
fclose(fp);
reset = 0;
}
maildir_nextfile(NULL);
if (fchdir(fd) != 0) {
warn("fchdir");
return (0);
}
return (1);
}
int
process(char **argv, int (*func)(FILE *, FILE *), int (*maildir_func)(int, char *, FILE *)) {
int ok, i, fd, tmp;
FILE *in_fp, *out_fp;
char tmpname[MAXPATHLEN];
struct stat sb;
struct timeval times[2];
ok = 1;
times[0].tv_usec = times[1].tv_usec = 0;
/* process from stdin to stdout if no arguments */
if (argv[0] == NULL) {
curfile = "stdin";
switch (func(stdin, stdout)) {
case RET_ERROR:
warnx("%s", strerror(errno));
/* FALLTHROUGH */
case RET_LOCALERROR:
return (0);
default:
return (1);
}
}
for (i = 0; argv[i] != NULL; i++) {
curfile = argv[i];
if (is_maildir(curfile)) {
if (maildir_func != NULL) {
if (!process_maildir(curfile, maildir_func))
ok = 0;
} else {
warnx("%s: is a Maildir (not implemented yet)", curfile);
ok = 0;
}
continue;
}
if ((in_fp = fopen(argv[i], "r+")) == NULL) {
warn("%s", argv[i]);
ok = 0;
continue;
}
fd = fileno(in_fp); /* some things need raw fd */
snprintf(tmpname, sizeof tmpname, "%s.tmp.XXXXXXXX", argv[i]);
if ((tmp = mkstemp(tmpname)) < 0) {
warn("mkstemp: %s", tmpname);
fclose(in_fp);
ok = 0;
continue;
}
/* attach temp file to FILE* */
if ((out_fp = fdopen(tmp, "w")) == NULL) {
warn("fdopen: %d (%s)", tmp, tmpname);
fclose(in_fp);
close(tmp);
unlink(tmpname);
ok = 0;
continue;
}
if (!mailboxlock(argv[i], 0, fd, LF_GET)) {
warn("mailboxlock: %s", argv[i]);
fclose(in_fp);
fclose(out_fp);
unlink(tmpname);
ok = 0;
continue;
}
if (sig_count > 0) {
unlink(tmpname);
mailboxlock(argv[i], 0, -1, LF_REL);
errx(1, "exiting because of signal");
}
/*
* get the current atime/mtime, to put back if we change the
* file.
*/
if (fstat(fd, &sb) < 0) {
warn("fstat: %d (%s)", fd, argv[i]);
fclose(in_fp);
fclose(out_fp);
unlink(tmpname);
mailboxlock(argv[i], 0, -1, LF_REL);
ok = 0;
continue;
}
switch (func(in_fp, out_fp)) {
case RET_ERROR:
warn("%s", argv[i]);
/* FALLTHROUGH */
case RET_LOCALERROR:
ok = 0;
/* FALLTHROUGH */
case RET_NOCHANGE:
fclose(in_fp);
fclose(out_fp);
mailboxlock(argv[i], 0, -1, LF_REL);
unlink(tmpname);
break;
case RET_CHANGE:
if (fclose(out_fp) != 0) {
warn("fclose %s", tmpname);
ok = 0;
}
fclose(in_fp);
/*
* at this stage, the fcntl lock is no
* longer in effect, but the .lock file is,
* or should be.
*/
if (rename(tmpname, argv[i]) < 0) {
warn("rename: %s %s", tmpname, argv[i]);
ok = 0;
}
times[0].tv_sec = sb.st_atime;
times[1].tv_sec = sb.st_mtime;
if (utimes(argv[i], times) < 0)
warn("utimes: %s", argv[i]);
mailboxlock(argv[i], 0, -1, LF_REL);
break;
}
if (sig_count > 0)
errx(1, "exiting because of signal");
}
return (ok);
}
char *
inbox_check(char *file) {
if (strcmp(file, "INBOX") != 0)
return (file);
else
return (user_inbox());
}
/*
* Get user's inbox.
*/
char *
user_inbox(void) {
char *e;
struct passwd *pw;
static char buf[MAXPATHLEN];
if ((e = getenv("MAIL")) != NULL)
return (e);
if ((pw = getpwuid(getuid())) == NULL)
return (NULL);
if (snprintf(buf, sizeof buf, "%s/%s",
_PATH_MAILDIR, pw->pw_name) >= sizeof buf)
return (NULL);
else
return (buf);
}
/*
* Get the user's home directory.
*/
char *
homedir(void) {
struct passwd *pw;
char *home;
static char buf[MAXPATHLEN];
if (buf[0] == '\0') {
if ((home = getenv("HOME")) != NULL)
strlcpy(buf, home, sizeof buf);
else if ((pw = getpwuid(getuid())) != NULL)
strlcpy(buf, pw->pw_dir, sizeof buf);
else
errx(1, "couldn't find your home directory");
}
return (buf);
}
/*
* Get user's mail directory: checks for ~/mail and ~/Mail.
*/
char *
mailpath(void) {
char *mail;
char *env;
int len, i;
struct stat sb;
const char *home = homedir();
static char buf[MAXPATHLEN];
if (buf[0] != '\0')
return (buf);
if ((env = getenv("MAILUTILS_DIR")) != NULL) {
strlcpy(buf, env, sizeof buf);
return (buf);
}
len = strlen(home);
mail = malloc(len + sizeof "/mail");
if (mail == NULL)
errx(1, "malloc for mailpath failed");
for (i = 0; i < 2; i++) {
/* build the path, space will be replaced with 'm' or 'M' */
strcpy(mail, home);
strcat(mail, "/ ail");
if (i == 0)
mail[len + 1] = 'm';
else
mail[len + 1] = 'M';
if (stat(mail, &sb) == 0 && S_ISDIR(sb.st_mode)) {
strlcpy(buf, mail, sizeof buf);
return (buf);
}
}
errx(1, "couldn't find your mail directory");
}
unsigned int
str_to_int(char *p) {
unsigned int ret;
char *end;
ret = (unsigned int)strtol(p, &end, 0);
/* Cope with the G, M and K suffixes. */
switch (*end) {
case 'g':
case 'G':
if (ret > UINT_MAX / 1024)
return (UINT_MAX);
ret *= 1024;
/* FALLTHROUGH */
case 'm':
case 'M':
if (ret > UINT_MAX / 1024)
return (UINT_MAX);
ret *= 1024;
/* FALLTHROUGH */
case 'k':
case 'K':
if (ret > UINT_MAX / 1024)
return (UINT_MAX);
ret *= 1024;
end++;
break;
case '\0':
break;
default:
warnx("str_to_int: ignoring tailing garbage `%s'", end);
}
return (ret);
}
void
free_tree(TREE *p) {
if (p == NULL)
return;
free_tree(p->left);
free_tree(p->right);
free(p->file);
free(p);
}
void
check_tree(TREE *tp, void (*check_file)(char *)) {
if (sig_count > 0 || tp == NULL)
return;
check_tree(tp->left, check_file);
if (sig_count > 0)
return;
check_file(tp->file);
check_tree(tp->right, check_file);
}
/* add string to a tree */
void
add_to_tree(TREE **tp, char *str) {
if (tp == NULL)
errx(1, "tp == NULL in add_to_tree()");
if (*tp == NULL) {
MALLOC(*tp, sizeof **tp);
MALLOC((*tp)->file, strlen(str) + 1);
strcpy((*tp)->file, str);
(*tp)->left = (*tp)->right = NULL;
return;
}
if (strcmp((*tp)->file, str) < 0)
add_to_tree(&(*tp)->right, str);
else
add_to_tree(&(*tp)->left, str);
return;
}
/*
* convert an array (e.g. from argv) to the binary tree
* format needed.
*/
void
array_to_tree(TREE **tp, char **array) {
while (*array) {
add_to_tree(tp, *array);
array++;
}
}
/*
* walk through the directory tree starting at dir, adding all
* regular files to the specified tree.
*/
void
files_to_tree(TREE **tp, const char *dir) {
DIR *dirp;
struct dirent *dp;
char buf[MAXPATHLEN];
int len;
if (sig_count > 0)
return;
if ((dirp = opendir(dir)) == NULL)
err(1, "%s", dir);
while ((dp = readdir(dirp)) != NULL) {
struct stat sb;
/* skip files starting with a dot */
if (dp->d_name[0] == '.')
continue;
/* skip *.gz, *.lock, *.lock.* */
len = strlen(dp->d_name);
if ((len >= 3 && strcmp(dp->d_name + len - 3, ".gz") == 0) ||
(len >= 5 && strcmp(dp->d_name + len - 5, ".lock") == 0) ||
strstr(dp->d_name, ".lock.") != NULL)
continue;
snprintf(buf, sizeof buf, "%s/%s", dir, dp->d_name);
if (stat(buf, &sb) != 0) {
warn("stat of %s failed", buf);
continue;
}
if (is_maildir(buf) || S_ISREG(sb.st_mode))
add_to_tree(tp, buf);
else if (S_ISDIR(sb.st_mode)) {
if (strcmp(dp->d_name, "archive") == 0)
continue;
files_to_tree(tp, buf);
} else
warnx("%s not regular or directory (%#o), skipping",
dp->d_name, sb.st_mode);
}
closedir(dirp);
}
int
getlock(int fd, int type) {
struct flock fl;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_pid = getpid();
fl.l_type = type;
return (fcntl(fd, F_SETLK, &fl));
}
int
mailboxlock(char *name, int maildir, int fd, int op) {
int tries;
int delay;
int tmpfd;
char hitchpost[MAXPATHLEN], lockfile[MAXPATHLEN];
char newfile[MAXPATHLEN], *s;
struct stat sb;
time_t now;
int gotlock = 0;
int soft = 0;
if (maildir) {
/* kludge, so that releasing a lock on a /tmp/ file in a maildir
* will move it to /new/. This is used in conjunction with
* open_mailbox really.
*/
if (fd != -1 || op != LF_REL) {
errno = EINVAL;
warnx("mailboxlock(%s, %d, %d, %d) called", name, maildir, fd, op);
return (0);
}
/* check for "/tmp/" */
if (strlen(name) >= sizeof newfile ||
(s = strrchr(name, '/')) == NULL || s < name + 4 ||
s[-4] != '/' || s[-3] != 't' || s[-2] != 'm' || s[-1] != 'p') {
errno = EINVAL;
warnx("mailboxlock: %s: bad filename", name);
return (0);
}
/* copy, changing "tmp" to "new" */
strcpy(newfile, name);
memcpy(newfile + (s - name) - 3, "new", 3);
return (rename(name, newfile) == 0);
}
if (op == LF_GET_SOFT) {
op = LF_GET;
soft = 1;
}
if (name) {
snprintf(hitchpost, sizeof hitchpost,
"%s.lock.%s.%lx.%lx.XXXXXXXX", name, hostname(),
(unsigned long)getpid(), (unsigned long)time(NULL));
snprintf(lockfile, sizeof lockfile, "%s.lock", name);
}
if (op == LF_REL) {
if (fd >= 0)
getlock(fd, F_UNLCK);
if (name)
unlink(lockfile);
return (1);
}
if (fd < 0) {
errno = EBADF;
return (0);
}
delay = 200000;
for (tries = 0; tries < 10; tries++) {
if (getlock(fd, F_WRLCK) != -1) {
gotlock = 1;
break;
}
if (errno != EAGAIN)
break;
if (sig_count > 0)
return (0);
if (tries == 4)
delay = 2000000;
usleep(delay);
}
if (name) {
/* create a hitching post */
if ((tmpfd = mkstemp(hitchpost)) < 0)
return (gotlock);
close(tmpfd);
/* get current time */
if (time(&now) < 0) {
unlink(hitchpost);
return (gotlock);
}
/* check for stale lock -- race condition here? XXX */
if (stat(lockfile, &sb) == 0 && now - sb.st_ctime > 1800)
unlink(lockfile);
/* attempt a link to the real lockfile */
tries = 0;
delay = 200000;
while (link(hitchpost, lockfile) < 0) {
if (tries == 4)
delay = 2000000;
if (sig_count > 0 || ++tries == 10 ||
errno != EEXIST)
{
unlink(hitchpost);
return (gotlock);
}
/*
* check hitchpost link count, the link() may have
* succeeded after all, if the link count == 2.
*/
if (stat(hitchpost, &sb) < 0) {
/* shouldn't happen */
unlink(hitchpost);
return (gotlock);
}
if (sb.st_nlink == 2)
break;
usleep(delay);
}
/* remove hitching post */
unlink(hitchpost);
}
/* success */
return (gotlock);
}
FILE *
open_mailbox(char *file, char **ret, int *maildir) {
int fd, sverrno;
FILE *fp;
char buf[MAXPATHLEN];
file = inbox_check(file);
if (file == NULL)
return (NULL);
*maildir = (is_maildir(file));
if (*maildir) {
snprintf(buf, sizeof buf, "%s/tmp/%lu.%d_%d.%s",
file, (unsigned long)time(NULL), (int)getpid(),
maildir_seq++, hostname());
if ((fd = open(buf, O_CREAT|O_EXCL|O_WRONLY, 0600)) < 0)
return (NULL);
if ((*ret = strdup(buf)) == NULL) {
sverrno = errno;
close(fd);
unlink(buf);
errno = sverrno;
return (NULL);
}
if ((fp = fdopen(fd, "w")) == NULL) {
sverrno = errno;
close(fd);
unlink(buf);
free(*ret);
errno = sverrno;
return (NULL);
}
return (fp);
}
fd = open(file, O_WRONLY|O_CREAT, 0600);
if (fd < 0)
return (NULL);
if (!mailboxlock(file, 0, fd, LF_GET)) {
sverrno = errno;
close(fd);
errno = sverrno;
return (NULL);
}
*ret = malloc(strlen(file) + 1);
if (*ret == NULL) {
sverrno = errno;
close(fd);
errno = sverrno;
return (NULL);
} else
strcpy(*ret, file);
fp = fdopen(fd, "w");
if (fp == NULL) {
sverrno = errno;
free(*ret);
close(fd);
errno = sverrno;
return (NULL);
}
if (fseek(fp, 0, SEEK_END) < 0) {
sverrno = errno;
free(*ret);
fclose(fp);
errno = sverrno;
return (NULL);
}
return (fp);
}
static int
_checkdir(const char *d, const char *e) {
char buf[MAXPATHLEN];
struct stat sb;
snprintf(buf, sizeof buf, "%s/%s", d, e);
return (stat(buf, &sb) == 0 && S_ISDIR(sb.st_mode));
}
int
is_maildir(const char *d) {
return (_checkdir(d, "new") && _checkdir(d, "tmp") && _checkdir(d, "cur"));
}
static int
_dirsize(const char *d, const char *e) {
char dirbuf[MAXPATHLEN], filebuf[MAXPATHLEN];
DIR *dp;
struct dirent *dep;
struct stat sb;
int total = 0;
snprintf(dirbuf, sizeof dirbuf, "%s/%s", d, e);
if ((dp = opendir(dirbuf)) == NULL)
return (-1);
while ((dep = readdir(dp)) != NULL) {
snprintf(filebuf, sizeof filebuf, "%s/%s", dirbuf, dep->d_name);
if (stat(filebuf, &sb) != 0) {
closedir(dp);
return (-1);
} else if (S_ISREG(sb.st_mode))
total += (int)sb.st_size;
}
closedir(dp);
return (total);
}
int
mailbox_size(const char *d, int *num) {
struct stat sb;
int n, c;
if (!is_maildir(d)) {
if (stat(d, &sb) != 0)
return (-1);
else
return ((int)sb.st_size);
}
if ((n = _dirsize(d, "new")) < 0)
return (-1);
if ((c = _dirsize(d, "cur")) < 0)
return (-1);
return (n + c);
}
void
xerr(int code, char *fmt, ...) {
va_list ap;
if (quiet)
exit(EX_TEMPFAIL);
va_start(ap, fmt);
verr(code, fmt, ap);
}
void
xerrx(int code, char *fmt, ...) {
va_list ap;
if (quiet)
exit(EX_TEMPFAIL);
va_start(ap, fmt);
verrx(code, fmt, ap);
}
void
xfprintf(FILE *fp, char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
if (vfprintf(fp, fmt, ap) < 0)
xerr(1, "vfprintf");
va_end(ap);
}
syntax highlighted by Code2HTML, v. 0.9.1