[Mew-dist 14657] incm.c
Yasunari Momoi
momo at example.com
2000年 10月 19日 (木) 15:10:50 JST
仕事で再び C を使うことになったので,リハビリのために :-)
incdir の C 版を書きました.これもせっかくなので公開しておき
ます.
私のマシンでは 900 通のメール処理で,sh 版より 10 倍ぐらい速
いです.
incm > /dev/null 0.06s user 1.70s system 94% cpu 1.865 total
incdir > /dev/null 0.84s user 17.24s system 91% cpu 19.723 total
各種仕様は incdir と同じです.現在は Maildir 形式のみ対応で
す.まだ,エラーチェックとか甘いところもあると思います.
FreeBSD では普通に動いてますが,移植性は全く考慮してません.
configure とか教えてくれる人がいれば,この機会に覚えようか
なぁ,とは思ってますが...(^^;)
# しばらく perl ばっかりだったので,#include と書こうとして
# #! と打ってたりとか,() をつけ忘れたりとか...
-- momo
-------------- next part --------------
/*
* incm - incorporate new mail
* author: Yasunari Momoi <momo at example.com>
* created: 2000/10/19
*
*
* Add the following to your emacs configuration file.
*
* (setq mew-mailbox-type 'mbox) ; this applies also maildir
* (setq mew-mbox-command "incm")
* (setq mew-mbox-command-arg "-d /path/to/your/maildir")
*
*
* Copyright (C) 2000 Yasunari Momoi. All rights reserved.
* Copyright notice is the same as Mew's one.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
static char Version[] = "$Id: incm.c,v 1.2 2000/10/19 05:59:53 momo Exp $";
static char* Command;
enum BOOL { FALSE, TRUE };
enum MBOXTYPE {
T_MAILDIR,
/* T_MBOX, */
};
static char InboxDir[BUFSIZ];
static char Mbox[BUFSIZ];
static int MboxType;
static int Backup;
void
warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
void
error(const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "error: ");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
void
usage()
{
printf("usage: %s [-b] [-d maildir] [-i inboxdir]\n", Command);
printf(" -b backup mail to maildir/cur directory\n");
printf(" -d maildir path to maildir (default: %s)\n", Mbox);
printf(" -i inboxdir path to inboxdir (default: %s)\n", InboxDir);
printf("\n");
printf("version: %s\n", Version);
exit(EXIT_FAILURE);
}
void
init_env(int argc, char** argv)
{
char *home = getenv("HOME");
Command = argv[0];
snprintf(InboxDir, sizeof(InboxDir), ".");
snprintf(Mbox, sizeof(Mbox), "%s/Maildir", home);
MboxType = T_MAILDIR;
Backup = FALSE;
}
int
is_number(char* str)
{
do {
if (!isdigit(*str))
return FALSE;
} while (*++str != '\0');
return TRUE;
}
int
get_last_seq()
{
struct dirent* dp;
DIR* dirp;
int last = 0;
int seq;
if ((dirp = opendir(InboxDir)) == NULL)
error("opendir(%s)", InboxDir);
while ((dp = readdir(dirp)) != NULL) {
if (!is_number(dp->d_name))
continue;
seq = atoi(dp->d_name);
last = last > seq ? last : seq;
}
closedir(dirp);
return last;
}
int
compare_string(char** i, char** j)
{
return strcmp(*i, *j);
}
/*
fastcopy: delived from mv.c (FreeBSD)
*/
int
fastcopy(char* from, char* to)
{
struct timeval tval[2];
static u_int blen;
static char *bp;
mode_t oldmode;
struct stat sb;
int nread, from_fd, to_fd;
if (lstat(from, &sb)) {
warning("lstat(%s)", from);
return 1;
}
if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
warning("%s", from);
return 1;
}
if (blen < sb.st_blksize) {
if (bp != NULL)
free(bp);
if ((bp = malloc(sb.st_blksize)) == NULL) {
blen = 0;
warning("malloc failed");
return 1;
}
blen = sb.st_blksize;
}
while ((to_fd = open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
if (errno == EEXIST && unlink(to) == 0)
continue;
warning("%s", to);
(void)close(from_fd);
return 1;
}
while ((nread = read(from_fd, bp, blen)) > 0)
if (write(to_fd, bp, nread) != nread) {
warning("%s", to);
goto err;
}
if (nread < 0) {
warning("%s", from);
err:
if (unlink(to))
warning("%s: remove", to);
(void)close(from_fd);
(void)close(to_fd);
return 1;
}
(void)close(from_fd);
oldmode = sb.st_mode & ALLPERMS;
if (fchown(to_fd, sb.st_uid, sb.st_gid)) {
warning("%s: set owner/group (was: %lu/%lu)", to,
(u_long)sb.st_uid, (u_long)sb.st_gid);
if (oldmode & (S_ISUID | S_ISGID)) {
warning(
"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)",
to, oldmode);
sb.st_mode &= ~(S_ISUID | S_ISGID);
}
}
if (fchmod(to_fd, sb.st_mode))
warning("%s: set mode (was: 0%03o)", to, oldmode);
/*
* XXX
* NFS doesn't support chflags; ignore errors unless there's reason
* to believe we're losing bits. (Note, this still won't be right
* if the server supports flags and we were trying to *remove* flags
* on a file that we copied, i.e., that we didn't create.)
*/
errno = 0;
if (fchflags(to_fd, sb.st_flags))
if (errno != EOPNOTSUPP || sb.st_flags != 0)
warning("%s: set flags (was: 0%07o)", to, sb.st_flags);
tval[0].tv_sec = sb.st_atime;
tval[1].tv_sec = sb.st_mtime;
tval[0].tv_usec = tval[1].tv_usec = 0;
if (utimes(to, tval))
warning("%s: set times", to);
if (close(to_fd)) {
warning("%s", to);
return 1;
}
return 0;
}
void
movefile(char* fromfile, char* tofile, char* backupfile, int backup)
{
if (backup) {
if (fastcopy(fromfile, tofile))
error("fastcopy(%s, %s)", fromfile, tofile);
if (rename(fromfile, backupfile))
error("rename(%s, %s)", fromfile, backupfile);
} else {
if (rename(fromfile, tofile)) {
if (errno != EXDEV)
error("rename(%s, %s)", fromfile, tofile);
if (fastcopy(fromfile, tofile))
error("fastcopy(%s, %s)", fromfile, tofile);
if (unlink(fromfile))
error("unlink(%s)", fromfile);
}
}
}
void
process_maildir(int seq)
{
struct stat sb;
struct dirent* dp;
DIR* dirp;
char maildir[BUFSIZ];
char backupdir[BUFSIZ];
char mailfile[BUFSIZ];
char inboxfile[BUFSIZ];
char backupfile[BUFSIZ];
char** list;
int listsize = BUFSIZ;
int listend = 0;
int i;
if ((list = malloc(sizeof(char*)*listsize)) == NULL)
error("malloc");
snprintf(maildir, sizeof(maildir), "%s/new", Mbox);
snprintf(backupdir, sizeof(backupdir), "%s/cur", Mbox);
if ((dirp = opendir(maildir)) == NULL)
error("opendir(%s)", maildir);
while ((dp = readdir(dirp)) != NULL) {
snprintf(mailfile, sizeof(mailfile), "%s/%s", maildir, dp->d_name);
if (lstat(mailfile, &sb))
continue;
if (!((sb.st_mode & S_IFREG) && (sb.st_mode & S_IRUSR)))
continue;
if (listend >= listsize) {
listsize *= 2;
if ((list = (char**)realloc(list, sizeof(char*)*listsize)) == NULL)
error("realloc");
}
if ((list[listend++] = strdup(dp->d_name)) == NULL)
error("strdup(%s)", dp->d_name);
}
closedir(dirp);
qsort(list, listend, sizeof(char*),
(int (*)(const void*, const void *))compare_string);
for (i = 0; i < listend; i++) {
do {
snprintf(inboxfile, sizeof(inboxfile), "%s/%d", InboxDir, ++seq);
if (access(inboxfile, R_OK) && errno == ENOENT)
break;
} while (TRUE);
snprintf(mailfile, sizeof(mailfile), "%s/%s", maildir, list[i]);
if (Backup)
snprintf(backupfile, sizeof(backupfile),
"%s/%s:2,S", backupdir, list[i]);
movefile(mailfile, inboxfile, backupfile, Backup);
printf("%d\n", seq);
}
}
void
process()
{
int seq = get_last_seq();
switch (MboxType) {
case T_MAILDIR:
process_maildir(seq);
break;
}
}
void
sanity_check()
{
/* XXX not yet */
}
int
main(int argc, char** argv)
{
extern char* optarg;
extern int optind;
int ch;
init_env(argc, argv);
while ((ch = getopt(argc, argv, "hbd:i:")) != -1) {
switch (ch) {
case 'b':
Backup = TRUE;
break;
case 'd':
snprintf(Mbox, sizeof(Mbox), "%s", optarg);
break;
case 'i':
snprintf(InboxDir, sizeof(InboxDir), "%s", optarg);
break;
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
sanity_check();
process();
return 0;
}
Mew-dist メーリングリストの案内